Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

.nornir/xtask-thin-door-audit.md

facett xtask — thin-door audit (LAW: "xtask is a THIN DOOR onto the build thing")

Measured on origin/main = 537ea35, 2026-09-29, lane C. xtask = 2342 lines (main.rs 525 · pip.rs 746 · holger.rs 880 · jsguard.rs 178 · lib.rs 13). The recipe is facett-build-thing (679 + 70 lines), 7 rows, all tagged ORIGINAL.

The law: xtask owns no package name, no feature string, no triple, no list of rows; cargo xtask build [--variant] [--list] resolves through the RECIPE and materializes every argv with nornir_build_thing::build_variant; rows cargo cannot build are listed and refused BY NAME with the door that builds them; one dependency, the recipe, which re-exports nornir_build_thing.

1. Build knowledge xtask owned at 537ea35

# where (537ea35) knowledge recipe already declared? disposition
K1 xtask/src/main.rs:42 triple wasm32-unknown-unknown (WASM_TARGET) yes — target on rows facet-wrapped-wasm, facett-demo-wasm deleted; the row's target rides cargo_args()
K2 xtask/src/main.rs:44 package facet-wrapped (WRAP_CRATE) yes — WASM_PACKAGE, row facet-wrapped-wasm deleted; wrap-wasm = the row by name
K3 xtask/src/main.rs:252-257 argv build --release --lib -p facet-wrapped --target wasm32-… yes — the row materializes build -p facet-wrapped --profile release --target wasm32-… replaced by nornir_build_thing::build_variant(set, "facet-wrapped-wasm")
K4 xtask/src/main.rs:259-264 cargo artifact path (-→_ stem, <triple>/release/) derived in edda wasm::cargo_artifact replaced by wasm::cargo_artifact(row)
K5 xtask/src/main.rs:267-279 wasm-bindgen argv --target web --no-typescript, out dir facet-wrapped/dist yes — [[build.wasm]] facet-wrapped-wasm (BindgenStage{Web, typescript:false}, out_dir) replaced by wasm::run_stages(cell, …, XtaskWasmStageRunner)
K6 xtask/src/main.rs:282-292 manifest stage argv run --quiet --example manifest -p facet-wrapped → manifest.json yes — ComponentManifestStage{example:"manifest", file:"manifest.json"} same run_stages call
K7 xtask/src/main.rs:229-241 build verb resolved via suite_cli::run, which calls BuildVariantSet::discover_validated = reads the emitted toml the recipe is the source; the toml is its description now resolves FacettBuildThing.variants() (code) and validates it in-process
K8 xtask/src/main.rs:179 (help) "--list to see all seven" — a row count the recipe's row count help no longer counts rows
K9 suite_cli::run:219-235 (edda) non-cargo rows "skipped (use the image/vm/iso builders)" — no named door — xtask refuses them BY NAME: container → dwarves buildthing facett --variant <n> (file-mode cell) · package → cargo xtask publish-pip / the forge
K10 xtask/src/main.rs:61 package facett-demo (demo_crate) yes — DEMO_PACKAGE reads facett_build_thing::DEMO_PACKAGE
K11 xtask/src/main.rs:86 demo wasm feature axis yes — DEMO_WASM_CARGO_ARGS (already read from the recipe) unchanged (was already a door)
K12 xtask/src/main.rs:48,317-384 panel-wasm: package facett-wasm-demo, example panel, triple, bindgen flags, dist no — BuildVariant has no example field, so a cargo example cannot be a row left in xtask, VIKTIGT: needs example on edda's BuildVariant (or the fixture moved to a [[bin]]) before it can be a row; it is a WebGPU-guard fixture, not a shipped artifact
K13 xtask/src/pip.rs:42 image name nordisk-pip-toolchain yes — PIP_TOOLCHAIN_IMAGE re-exported from the recipe
K14 xtask/src/pip.rs:52-54 distribution facet-wrapped-web / import facet_wrapped_web yes — DISTRIBUTION = "facet_wrapped_web" IMPORT_PACKAGE = recipe DISTRIBUTION; the PEP 503 spelling stays derived
K15 xtask/src/pip.rs:64-65,334-335 the 3 wasm outputs (twice) yes — WASM_OUTPUTS WASM_BUILD_OUTPUTS = recipe WASM_OUTPUTS; REQUIRED_ASSETS = those + the package-source index.html
K16 xtask/src/pip.rs:304-311,442 dirs pkg-facet-wrapped/dist, facet-wrapped/dist, pkg-facet-wrapped yes — OUT_DIR, WASM_DIST, SOURCE read from the recipe
K17 xtask/src/pip.rs:47, holger.rs:341,591 Containerfile bytes via include_str!("containerfiles/…") yes — file-mode [[build.container]] cells name the same three paths kept: include_str! needs a literal, and tests/declared.rs already holds the three declared paths to exist; noted, not moved
K18 xtask/src/holger.rs:339,589 image names nordisk-pip-consumer, nordisk-streamlit yes — PIP_CONSUMER_IMAGE, STREAMLIT_IMAGE re-exported from the recipe
K19 xtask/src/pip.rs:540-545 python -m build --wheel --sdist --no-isolation script the package cell (format = pip, sdist = true); edda's PackageStager/DraupnirPackageRunner is the forge's road left: moving publish-pip onto edda's package road is its own lane (the jsguard residual below is why the two roads still differ)
K20 xtask/src/jsguard.rs (all) the no-JS-graphics rule on the staged loader page explicitly NOT declared (recipe lib.rs:56-63) left: it is a content rule, not a build axis; the recipe's docs already say closing the forge gap means moving it into validate
K21 xtask/Cargo.toml 12 deps (nornir-pages, nornir-xtask-support, facett-wasm-demo, nornir-build-thing, facett-build-thing, draupnir, zip, sha2, base64, 4× znippy) — nornir-build-thing dropped: reached only through the recipe's pub use nornir_build_thing (one row type by construction). The rest serve verbs that are not builds (publish/verify/run/deploy) and stay — see §3

2. What the recipe declared and what was missing

Declared (7 rows, all suites = ["ORIGINAL"] since 1ad8e85): facet-wrapped-wasm (wasm + [[build.wasm]] bindgen+manifest), facet-wrapped-web (package, from_wasm), facett-demo (exe), facett-demo-wasm (wasm + bindgen cell), three file-mode containers. [build.toolchain] pinned by manifest digest.

Missing for a pure door:

3. Why xtask does not have ONE dependency

The law's "done" example (holger 424945a) has 10 [dependencies]; one-dep is not what any "done" repo measures. In facett the extra deps are verbs that build NOTHING: publish-holger (znippy ×4), publish-pip/verify-install/streamlit/test-demo (draupnir, zip, sha2, base64), deploy-pages (nornir-pages), panel-wasm (facett-wasm-demo). Folding them into facett-build-thing would put bollard and znippy into a crate the forge compiles to wasm32 and loads as a sandboxed plugin — the recipe would stop being a declaration. The build knowledge is what had to leave xtask; the dependency that mattered was nornir-build-thing, and it is gone.

4. After (lane C, measured 2026-09-29)

5. Not done

6. The container road (lane C, oden 2026-09-29)

cargo xtask build / wrap-wasm build every cargo row in the pinned [build.toolchain] image and have no host road (xtask/src/door.rs), and since this commit the row's [[build.wasm]] stages run in the same image too (xtask/src/stages.rs, ContainerStages: a second impl of edda's WasmStageRunner, over DraupnirCargoRunner).

7. ONE dependency — design (lane X, dwarf 2026-09-30)

Owner order: xtask carries ONE dependency, the recipe. §3 said that needs the non-build verbs out of xtask, and that folding them into the recipe is wrong (the forge compiles facett-build-thing to wasm32 and loads it as a plugin; bollard and znippy cannot follow it there). No repo has done it yet (holger, the "done" example, has ~10 deps), so this is the worked example.

The split. Two crates, two jobs:

crate job normal deps verbs
xtask the THIN DOOR: builds rows, nothing else facett-build-thing (features wasm-stage, cargo-container) — ONE build, wrap-wasm, buildgate
facett-ops (new member, plain bin + lib) everything that is not a build: publish, verify, run, deploy the recipe + nornir-pages, facett-wasm-demo, draupnir, zip, sha2, base64, znippy-* publish-pip, publish-holger, verify-install, streamlit, test-demo, demo-wasm, deploy-pages, panel-wasm

Order B in the same stroke (edda): nornir_pages::demo_wasm loses its host cargo build + host wasm-bindgen + wasm-opt. New shape: demo_wasm(cfg, set, variant) builds the caller's DECLARED wasm row with container_road (pinned image, bindgen stage in the image), checks the row's cell produces the files the page imports, and stages the site shell; deploy_pages(cfg, set, variant) = that + publish_dist. A caller without a wasm row gets a refusal by name, never a host build. nornir and dwarves callers pass their recipe's set and row name.

What landed (lane X, dwarf 2026-10-01) — owner option (b): facett ALONE, edda untouched.

The split landed against edda main 6b5f905; the container road did NOT move to edda. It stays in facett (xtask/src/door.rs + xtask/src/stages.rs), DUPLICATED with edda branch laneX/demo-wasm-container-r2 (nornir_build_thing::container_road), until that branch can merge — blocked because it changes nornir_pages::demo_wasm and dwarves_runa::Step::Xtask, which nornir and dwarves call, and neither workspace resolves on dwarf. Two things xtask got from crates it no longer links are spelled locally until then: the "is the pin in the engine?" question is a no-op true run through DraupnirCargoRunner (was draupnir::ContainerBoot::image_present), and stages::locked_wasm_bindgen_version copies nornir_xtask_support's Cargo.lock scan. facett-ops builds its rows through xtask::door (a library edge, never a spawn).

Twin closed (lane X, dwarf 2026-10-01, later the same day). Proof build in a scratch tree (edda e0a2d0b + the holger-gaps-only crates, Skidbladnir holger-gaps crates, lane R's gunnar/modgunn clones) showed the nornir and dwarves callers compile in their real workspaces: nornir -p xtask 2/2 on our test (its no_silenced_tests is red on main, tests/viz_jobs_headless.rs, unrelated); dwarves -p xtask check green and runa_exec 6/6 incl. the new an_xtask_step_runs_through_its_cargo_alias. Merged: edda main e0a2d0b, dwarves main 68c28e1, nornir main fe406ec. So: