sow-topdata moved skills into a tree (skills/core, skills/specs, skillcats, skillspecranks), which broke validate/build: the generated feat families hardcoded dataset "skills". Resolution is now data-driven: the family spec's child_source names the dataset, child slugs come from the row key's own namespace, and the wiki loader accepts both the flat and tree layouts (topdata main still uses the flat one until the overhaul merges). Verified against the skill-grouping-overhaul topdata branch: validate/build/wiki all clean with zero lock churn, focus feats stay on their adopted rows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: #43 Co-authored-by: vickydotbat <vickydotbat@tutamail.com> Co-committed-by: vickydotbat <vickydotbat@tutamail.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 on pull requests and on push to main; the only
publish event is a v* tag (see runbooks/ci-trigger-standard.md in sow-docs,
https://git.westgate.pw/ShadowsOverWestgate/sow-docs).
test.yml— vet, test, shellcheck, yamllint, binary smoke (PR + main).build-binaries.yml— cross-build all targets (PR + main); on av*tag, 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.