Follow-up to the mdb removal in the previous commit. Three of the six extensions #64 listed as deliberate local additions are NWN2 formats, not NWN:EE ones, and belong in an NWN:EE tool no more than mdb did. Checked against xoreos (src/aurora/types.h), which covers every Aurora game and so distinguishes the games' tables: gr2 4003, sits in the NWN2 block (MDB2 4000, MDA2 4001, SPT2 4002, GR2 4003, PWC 4008). Granny is NWN2; NWN:EE does not use it. wlk xoreos numbers it 20004 and xml 20003, both above xml kFileTypeMAXArchive in the section headed "Entries for files not found in archives with numerical type IDs / Found in NWN2's ZIP files". Neither has an archive restype in any Aurora game, so Crucible's 0x0BCC and 0x0BCD were invented outright, in a band Aurora does use elsewhere (3022 FSM, 3023 ART). NWN:EE covers those jobs with formats already in the table: wok, pwk and dwk for walkmeshes, and EE's own UI XML loads from ui/ovr rather than from a HAK by restype, which is consistent with upstream neverwinter.nim registering no xml number. The remaining three local additions stay. lyt 3000, vis 3001 and mdx 3008 are real Aurora archive types that NWN1 ships in its own data/*.bif; xoreos gives the same numbers. Upstream simply does not list them. No asset in the corpus uses gr2, wlk or xml, so nothing stops building. Also drops them from AssetExtensions, and records the rule in a comment on the table plus a test, so the next NWN2 format gets caught. Refs ShadowsOverWestgate/sow-tools#64 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
sow-tools — Crucible
Crucible is the Shadows Over Westgate build/conversion/sync toolchain: one Go
module producing several small binaries plus a crucible dispatcher (D11). It is
the only repo that owns builder logic; the artifact repos (sow-module,
sow-topdata, sow-assets-manifest) invoke Crucible through wrapper scripts and
never embed a toolkit.
Repos produce artifacts. sow-platform deploys artifacts.
Crucible is how the artifact repos turn source into artifacts.
Binaries
| Binary | Dispatcher form | Owns |
|---|---|---|
crucible |
— | dispatcher: crucible <builder> [args] |
crucible-depot |
crucible depot |
content-addressed depot blob verify/move |
crucible-hak |
crucible hak |
ERF/HAK pack/unpack + hak manifests |
crucible-module |
crucible module |
build/extract/validate/compare the .mod |
crucible-topdata |
crucible topdata |
compile 2da/tlk topdata + packages |
crucible-wiki |
crucible wiki |
render + deploy mechanical wiki pages |
The dispatcher and the standalone shims share one registry
(internal/dispatch); the shims exist so consumer wrapper scripts can resolve a
single-token command. The full legacy nwn-tool command surface and where each
command lands is mapped in docs/command-surface.md.
Status (cutover performed)
The internal app/pipeline/project/erf/gff/topdata/changelog/
validator packages from gitea/sow-tools have been migrated into this tree, and
the module, topdata, hak, and wiki builders now delegate to the migrated
nwn-tool command surface (mapped in
docs/command-surface.md). config and changelog
are global commands on the dispatcher. depot has no migrated logic yet, so it
keeps the fail-closed path: exit 70, never a faked artifact.
See docs/migration-from-nwn-tool.md for what
was done and what remains (the consumer --manifest/--source/--out flag contract
is the open Phase-6 item).
Quick start (no Nix)
Teammates without Nix don't build anything — they run the bootstrap wrapper,
which downloads the latest released crucible for your OS and runs it:
./crucible # interactive menu (pick a command)
./crucible module build
./crucible topdata validate
Windows (PowerShell):
.\crucible.ps1 module build
The binary is cached under ~/.cache/crucible/<version>/ (%LOCALAPPDATA%\crucible
on Windows); --repo-local caches inside the repo instead. Private releases:
set CRUCIBLE_TOKEN or write the token to ~/.config/crucible/token.
Develop
Self-contained (D8) — a host with only Nix can run everything:
nix develop # Go + shellcheck + yamllint + make
make check # go vet + go test + shellcheck + yamllint
make build # build every cmd/* into ./bin (gitignored)
make smoke # build + assert the fail-closed contract
Binaries are never committed — they are CI artifacts (D19).
This retires the old habit of checking in nwn-tool / sow-toolkit.
CI
PR-first (D7): checks run once on pull requests; the only publish event is a
v* tag (see runbooks/ci-trigger-standard.md in sow-docs,
https://git.westgate.pw/ShadowsOverWestgate/sow-docs).
ci.yml— vet, test, shellcheck, yamllint, binary smoke, and cross-build all targets once per pull request.build-binaries.yml— on av*tag, cross-build and upload the binaries,SHA256SUMS, and the wrappers to the Gitea release.sync-wrappers.yml— on amainpush that toucheswrappers/, auto-PR the canonical wrappers to the consumer repos inwrappers/consumers.txt. Consumer drift checks run after those PRs merge tomain, not on the PRs themselves, to avoid recursive cross-repo checks.
Consumers
How the artifact repos resolve a Crucible binary is
documented in docs/consumer-contract.md.