# Migrating from `nwn-tool` to Crucible (operator cutover) The legacy toolchain lives at `gitea/sow-tools` as a single `nwn-tool` binary (entry `cmd/nwn-tool`, logic under `internal/`). This repo is the Crucible rewrite. Per the migration hard rules, the `internal/` source is **not** transplanted automatically — the operator migrates it. This is the checklist. ## Why a clean scaffold first - The hard rules forbid copying old-repo source into the migration tree (`build from scratch; reference, don't transplant`). - The downstream skeletons (`sow-module`, `sow-topdata`) already fail closed until they can resolve a Crucible binary; the scaffold satisfies that contract without faking artifacts. - The scaffold pins the **shape** (module path, dispatcher, multi-binary layout, container, CI) so the migrated logic drops into a stable frame. ## Cutover steps 1. **Move the internal packages.** Bring `internal/{app,pipeline,project,erf, gff,topdata,music,changelog,validator}` from `gitea/sow-tools` into this repo. Re-home `internal/app` command wiring behind `internal/dispatch` instead of the old flag parser in `cmd/nwn-tool`. 2. **Restore dependencies.** Add `golang.org/x/text` and `gopkg.in/yaml.v3` back to `go.mod`; commit the resulting `go.sum`. (`docker/Dockerfile` already tolerates an absent `go.sum` for the scaffold; it will be present after this.) 3. **Wire each builder.** In `internal/dispatch`, register a handler per builder and remove it from the unwired fail-closed path. Map the legacy commands per [`command-surface.md`](command-surface.md). 4. **Kill the home-dir / `NWN_ROOT` defaulting.** Honor only explicit env/flag roots per [`consumer-contract.md`](consumer-contract.md). 5. **Drop the committed binary.** Delete `nwn-tool` / `tools/sow-toolkit` from the source tree; `.gitignore` already excludes them. Binaries become CI artifacts and image layers only. 6. **Carry the determinism test.** Port `TestBuildNativeKeepsGeneratedIDsStableAcrossRepeatedBuilds` and keep it green. 7. **Runtime base for music.** If music conversion is wired, switch the final Docker stage from `distroless/static` to a debian-slim base that includes `ffmpeg`. 8. **Update `make smoke`.** Once a builder is wired, change the smoke expectation for it from exit `70` to a real success path. ## Wrapper contract carried over Consumer repos keep the `SCRIPT_WRAPPER_CONTRACT` resolution behavior, now pointed at Crucible binaries — see [`consumer-contract.md`](consumer-contract.md). The old `SOW_TOOLS_DEV_BINARY` dev override maps to `$CRUCIBLE` / `$SOW_MODULE_BUILD` / `$SOW_TOPDATA_BUILD`.