9.2 KiB
Refactor and harden all shell wrapper scripts across the module and assets repositories.
Status Snapshot
Current state as of 2026-05-13:
- Argument passthrough is solved for the active root wrappers.
module/andassets/now use repo-root launchers plusscripts/run-nwn-tool.*passthrough shims.- Wrapper regression coverage already exists in
module/tests/andassets/tests/. - Active repo-root
.shand.ps1wrappers are intentionally thin launchers. - The previously stale JSON-config assumption shown in this contract is no
longer current; active repos use
nwn-tool.yaml.
Active Scope
This contract is still active, but the priority has changed.
The old priority was:
- make
$@/@argsforwarding work
The current priority is:
- remove wrapper-side business logic that still duplicates toolkit behavior
- keep wrappers thin across shell, PowerShell, and release-entrypoint paths
- align wrapper logs and error handling with the current logging contract
- finish wrapper/tooling parity before moving deeper into wiki deployment
Current Findings
The main active wrapper gap is no longer argument forwarding or root-wrapper
thickness. The root wrappers are already thin cd plus run-nwn-tool launchers.
The remaining wrapper-adjacent thickness is release orchestration:
module/scripts/release-all.shandassets/scripts/release-haks.shboth parse effective config themselves and still own substantial orchestration that is not just "launch the toolkit".scripts/run-nwn-tool.*still own tool installation/update bootstrap. This is shared repo plumbing, not build business logic.
That means the wrapper contract should now optimize for:
- a single source of truth for preflight behavior
- preserving thin root wrappers
- shared repo plumbing only where toolkit ownership is not appropriate
- no duplicated config decoding logic between
.shand.ps1
Deferred Scope
Defer unrelated wrapper-adjacent work for now:
- model-compilation wrapper concerns
- release-announcement publication scripting
- broader asset-processing script consolidation outside wrapper ownership
Immediate Plan
- Audit every wrapper that does more than
cdplusexec. - Classify wrapper logic into:
- toolkit-owned behavior
- shared repo plumbing
- release-only orchestration
- Keep paired
.shand.ps1root wrappers thin; do not add new wrappers unless a command is intentionally promoted to a user-facing entrypoint. - Expand regression coverage from spot checks to all active root wrappers.
- Continue release-entrypoint review separately:
- wrapper-side preflight behavior
- stderr/stdout separation for JSON-producing subcommands
- shell/PowerShell parity for any remaining unavoidable wrapper logic
Problem
The current .sh and .ps1 wrapper scripts do not properly forward arbitrary CLI arguments to the underlying toolkit commands.
Example:
--verbose--debug--dry-run--config--dataset--force- future flags
Many wrappers only support fixed commands or partially forward arguments.
Additionally, after recent refactors — including:
- YAML configuration migration
- configuration hardening
- toolkit restructuring
- command refactors
- repository abstraction changes
— several wrapper scripts are now outdated and no longer correctly reflect the current toolkit behavior.
Example of a recent error produced:
$ ./build-haks.sh
run-nwn-tool.sh: Updating sow-toolkit to match latest sow-tools release...
installed: ${HOME}/Projects/nwnee-shadowsoverwestgate/assets/tools/sow-toolkit
run-nwn-tool.sh: Refreshing credits cache before build-haks...
Traceback (most recent call last):
File "${HOME}/Projects/nwnee-shadowsoverwestgate/assets/./scripts/sync-credits-cache.py", line 61, in <module>
main()
~~~~^^
File "${HOME}/Projects/nwnee-shadowsoverwestgate/assets/./scripts/sync-credits-cache.py", line 46, in main
music_configs = load_music_configs()
File "${HOME}/Projects/nwnee-shadowsoverwestgate/assets/./scripts/sync-credits-cache.py", line 16, in load_music_configs
config = json.loads(CONFIG_PATH.read_text(encoding="utf-8"))
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.14/pathlib/__init__.py", line 787, in read_text
with self.open(mode='r', encoding=encoding, errors=errors, newline=newline) as f:
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.14/pathlib/__init__.py", line 771, in open
return io.open(self, mode, buffering, encoding, errors, newline)
~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: '${HOME}/Projects/nwnee-shadowsoverwestgate/assets/nwn-tool.json'
Goal
Make all wrapper scripts:
- consistent
- argument-transparent
- future-safe
- deterministic
- repository-agnostic where possible
Core requirements
Global argument forwarding
All wrappers must forward arbitrary arguments transparently to the underlying command.
Examples:
./run-tool.sh build-haks --verbose
./run-tool.sh music build --dataset westgate --dry-run
./extract-module.sh --debug --force
Must correctly pass all additional arguments through unchanged.
PowerShell wrappers must behave equivalently:
.\run-tool.ps1 build-haks --verbose
No wrapper should:
- silently swallow arguments
- hardcode known flags only
- require updating for every new CLI option
- partially parse arguments unless absolutely necessary
Wrappers should behave as thin passthrough layers.
Wrapper audit
Audit all .sh and .ps1 scripts in:
- module repositories
- assets repositories
- toolkit-related repositories
Identify wrappers that:
- no longer match current toolkit commands
- reference outdated config paths
- reference old JSON config names
- assume old directory layouts
- assume deprecated commands
- hardcode obsolete arguments
- bypass the new normalized YAML config system
- duplicate logic now handled in the toolkit
Consistency requirements
All wrappers should follow consistent conventions:
- argument forwarding
- environment setup
- working directory resolution
- config resolution
- logging behavior
- error handling
- exit code propagation
- path normalization
- quoting/escaping
Behavior requirements
Argument passthrough
Shell wrappers should use safe passthrough semantics.
Examples:
- Bash:
"$@" - PowerShell:
@args
Arguments must preserve:
- ordering
- quoting
- spaces
- special characters
Exit codes
Wrappers must propagate underlying command exit codes correctly.
No swallowing failures.
Logging
Wrappers should:
- print concise execution summaries
- show the underlying toolkit command being executed
- optionally support wrapper-level debug logging
- avoid duplicating toolkit logs
Configuration awareness
Wrappers must support:
- YAML config paths
- overridden config locations
- repository-local config resolution
- future config expansion
No wrapper should assume:
config.json- fixed build paths
- fixed repository names
- fixed datasets
- fixed HAK names
Repository hardening alignment
Wrappers must align with the broader configuration hardening effort:
- no repository-specific hardcoding
- no hidden defaults
- no implicit assumptions
- no duplicated business logic
Wrappers should invoke toolkit functionality, not reimplement it.
Desired architecture
Wrappers should become:
- thin launchers
- environment bootstrap layers
- argument passthrough adapters
The toolkit itself should contain:
- validation
- command parsing
- config loading
- business logic
- deterministic behavior
Cross-platform behavior
Ensure .sh and .ps1 wrappers behave equivalently where possible.
Validate:
- quoting
- argument escaping
- relative path handling
- environment variable handling
- error propagation
- working directory behavior
Migration tasks
- Inventory all wrappers.
- Classify active vs obsolete wrappers.
- Update wrappers to support transparent argument forwarding.
- Remove outdated assumptions.
- Align wrappers with YAML config loading.
- Update docs/examples/help text.
- Add regression tests.
- Ensure compatibility with current toolkit commands.
- Remove duplicated wrapper logic where possible.
- Consolidate shared wrapper behavior if appropriate.
Testing requirements
Add tests for:
- argument passthrough
- quoted arguments
- multi-argument forwarding
- verbose/debug forwarding
- config override forwarding
- dataset override forwarding
- exit code propagation
- invalid command handling
- PowerShell parity
- shell parity
- wrapper execution from different working directories
Examples:
--verbose--debug--dry-run--config path/to/config.yaml--dataset westgate--force- unknown future flags
Acceptance criteria
- All wrappers forward arbitrary arguments correctly.
- Wrappers reflect the current toolkit architecture.
- No wrappers assume old JSON config behavior.
- No wrappers contain obsolete repository-specific logic.
- Wrapper behavior is deterministic and cross-platform.
- Exit codes propagate correctly.
- Wrapper maintenance burden is minimized.
- New CLI flags work automatically without wrapper updates.
- The toolkit, not wrappers, owns application logic.