Files
sow-tools/SCRIPT_WRAPPER_CONTRACT.md
T

9.1 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 largely solved for the active root wrappers.
  • module/ and assets/ now use repo-root launchers plus scripts/run-nwn-tool.* passthrough shims.
  • Wrapper regression coverage already exists in module/tests/ and assets/tests/.
  • 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 $@ / @args forwarding 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. The bigger gaps are wrapper thickness and duplicated orchestration:

  • module/build-module.sh still shells out to config effective --json, parses config locally, probes script sources, resolves manifest paths, and performs pre-build manifest refresh.
  • module/build-module.ps1 mirrors the same logic in a second implementation.
  • module/scripts/release-all.sh and assets/scripts/release-haks.sh both parse effective config themselves and still own substantial orchestration that is not just "launch the toolkit".

That means the wrapper contract should now optimize for:

  • a single source of truth for preflight behavior
  • thinner root wrappers
  • shared repo plumbing only where toolkit ownership is not appropriate
  • no duplicated config decoding logic between .sh and .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

  1. Audit every wrapper that does more than cd plus exec.
  2. Classify wrapper logic into:
    • toolkit-owned behavior
    • shared repo plumbing
    • release-only orchestration
  3. Move duplicated config reads and command decisions out of paired .sh and .ps1 wrappers where possible.
  4. Keep root wrappers as thin launchers with transparent argument forwarding.
  5. Expand regression coverage from passthrough correctness into:
    • 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

  1. Inventory all wrappers.
  2. Classify active vs obsolete wrappers.
  3. Update wrappers to support transparent argument forwarding.
  4. Remove outdated assumptions.
  5. Align wrappers with YAML config loading.
  6. Update docs/examples/help text.
  7. Add regression tests.
  8. Ensure compatibility with current toolkit commands.
  9. Remove duplicated wrapper logic where possible.
  10. 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.