Skip to content

Commit 47423eb

Browse files
hyperpolymathclaude
andcommitted
chore(contractiles): add canonical bust + dust to .machine_readable (satisfies Mustfile bust-dust-files-present)
Mirror the Bustfile (with the Zig-0.15.2 near-hit) into the real filled contractiles tree .machine_readable/contractiles/bust/, where the Mustfile bust-dust-files-present check (severity critical) looks, and author the sibling Dustfile (exnovation: ffi-scaffold deprecate, orphan rust wasm dependency-exit, dual rust trees keep-forever). The docs/templates/contractiles/ copies remain the blank-template tree per the stub's own copy-and-fill instruction. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 32bbe33 commit 47423eb

2 files changed

Lines changed: 125 additions & 0 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# Bustfile — design-aetiology / "bust" contract for snifs
3+
# Author: Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
4+
#
5+
# Paired runner: bust.ncl (NOT YET PRESENT — paired .ncl runners are an
6+
# estate-wide gap; cf the Adjustfile contractile-sync
7+
# check. Do not assume a runner exists.)
8+
# Verb: bust
9+
#
10+
# Semantics: BUST is about *broken* — the slang "damn, this thing is bust".
11+
# It records WHY things break (or nearly broke), cause-first, as
12+
# design aetiology — causal help/error/removal, NOT a bandaid that
13+
# suppresses the symptom. (Sibling: DUST = exnovation, the clean
14+
# reduction-to-dust / deliberate removal of a thing.)
15+
# Run with: bust check (list failure modes + recovery status)
16+
# Fix with: bust fix (apply the causal recovery where deterministic; advisory otherwise)
17+
18+
@abstract:
19+
The snifs Bustfile in two disjoint halves.
20+
21+
## Failure Modes — OPEN, actionable defect contracts: a class that CAN break,
22+
each pairing a detection probe with a causal recovery. This is the half a
23+
`bust` runner / k9 gate would drill (declared -> drilled -> verified | failing).
24+
25+
## Near-Hit Cases — CLOSED, non-actionable recognition records: something that
26+
COULD have bitten but DID NOT, written down with its aetiology so future work
27+
recognises the same shape rather than re-stumbling into it. A near-hit is NOT a
28+
defect, has no recovery to drill, and must never be auto-promoted into a Failure
29+
Mode (that would invent an open issue from a non-issue). It only graduates by an
30+
explicit human decision.
31+
@end
32+
33+
## Failure Modes
34+
35+
### releasefast-guest-silent-wrong-answers
36+
- description: A SNIF guest built with `-Doptimize=ReleaseFast` (or `ReleaseSmall`) strips Zig's runtime safety traps (overflow, bounds, unreachable). At the wasm32-freestanding guest boundary the same fault that should TRAP becomes undefined behaviour — the guest returns a plausible-but-wrong value across the buffer ABI and the BEAM host accepts a silently corrupt answer. There is no crash to observe.
37+
- cause: optimisation-for-speed removes the only mechanism (safety traps) that surfaces ABI/arithmetic violations at the cleave surface; ReleaseSafe keeps the trap and converts the same bug into an *observable* guest trap the host catches as `{:error, _}`.
38+
- probe: ! grep -qE 'optimize *= *\.(ReleaseFast|ReleaseSmall)' zig/src/*.zig build.zig 2>/dev/null # shipped guest must be ReleaseSafe
39+
- probe: just abi-conformance # + a differential corpus: ReleaseSafe vs ReleaseFast must AGREE on edge inputs — divergence == this mode firing
40+
- recovery: build/ship the guest `-OReleaseSafe` (traps preserved); CI-gate so a ReleaseFast artifact cannot ship; on a field wrong-answer, rebuild ReleaseSafe and replay the differential corpus to localise the trapping input.
41+
- severity: critical
42+
- status: declared
43+
44+
### wasmex-nif-guest-load-failure
45+
- description: The host-side wasmex NIF, or the `.wasm` guest it loads, fails to instantiate — a missing/renamed export, ABI/version skew between the Idris2-verified model and the real Zig exports, wrong wasm target, or a NIF that won't link into the BEAM. The Elixir call surfaces `{:error, _}` (or a NIF load crash) instead of a working SNIF.
46+
- cause: the guest export set drifted from the verified ABI (`verification/proofs/idris2/ABI/*`), or the guest was built for the wrong target / missing an export, so wasmex cannot bind the expected interface at instantiation — boundary-erosion / ABI-drift, exactly the class `abi_conformance.py` guards.
47+
- probe: just abi-conformance # signature drift gate (safe_nif + buffer_abi vs Foreign.idr / BufferAbi.idr)
48+
- probe: cd demo && mix test # host-side instantiate + call; must not be {:error, _} for the happy path
49+
- recovery: re-sync exports to the Idris2 ABI model (realign safe_nif.zig + buffer_abi.zig against Foreign.idr / BufferAbi.idr), rebuild for wasm32-freestanding, re-run `just abi-conformance` until green, then re-instantiate.
50+
- severity: critical
51+
- status: declared
52+
53+
## Near-Hit Cases
54+
55+
# RECOGNITION LEDGER — read, do not drill. Entries here are NOT defects and NOT
56+
# merge-gated. A `bust` runner / k9 gate binds ONLY to "## Failure Modes" above.
57+
# Do not migrate these into Failure Modes; promotion is a human decision via the
58+
# entry's `graduates_to_failure_mode_if:` trip-condition.
59+
60+
### zig-0.15.2-opaque-with-fields-template-rot
61+
- kind: near-hit
62+
- status: recognised
63+
- not_an_issue: true
64+
- class: toolchain # (extends the runner's failure-class enum; near-hits do not bind the runner)
65+
- surfaced_by: cross-repo AFFIRMATION signal, 2026-06-16 — typed-wasm's `ffi/zig` was found genuinely BUST under Zig 0.15.2 (its `tests/e2e.sh` Zig step fails, 48/1/6); we then checked whether the same shape bites snifs.
66+
- what_almost_broke: the rsr-template `src/interface/ffi/src/main.zig` carries pre-0.15 Zig patterns — an `opaque { ...fields... }` (`Handle`) and `std.heap.c_allocator` — that Zig 0.15.2 rejects. Rendered and built, it would not compile.
67+
- why_it_did_not_bite: that file is *unrendered dead scaffold* — it still holds `{{project}}` placeholders and is wired into NO build / test / gate (`tests/e2e.sh` does not touch it; `src/interface/ffi/build.zig`'s compile step is commented out; the proof + conformance gates use `zig/src/safe_nif.zig` + `buffer_abi.zig` only). A direct compile dies at the `{{project}}` placeholder *before* even reaching the type errors. The LIVE guests compile clean under Zig 0.15.2 (verified this session).
68+
- aetiology:
69+
- root-1-template: the rsr-template shipped a generic C-FFI example written in pre-0.15 Zig idioms; snifs never rendered it (it uses `zig/src/safe_nif.zig` instead), so the dead patterns sat latent.
70+
- root-2-toolchain: Zig 0.15.2 tightened two things — `opaque` may no longer carry fields, and `std.heap.c_allocator` now requires explicit libc linking.
71+
- mechanism: `opaque {}` *means* "a type whose size/layout is unknown"; a field declares a layout, so opaque-with-fields is a contradiction and 0.15.2 correctly forbids it (declarations remain legal). `c_allocator` calls libc `malloc`/`free`, which a `wasm32-freestanding` guest has no business linking.
72+
- non-cause-ruled-out: this is NOT a Zig regression (the change is load-bearing type discipline, by design — nothing to fix upstream) and NOT a defect in the live SNIF guests (they never used these patterns and build clean). The bustedness is entirely contained in the dead template.
73+
- recognition: treat Zig 0.15.2's tightening as type discipline to CONFORM TO, not work around. If this template is ever rendered + built, use the opaque-handle idiom (a concrete `struct`, hand C an `*anyopaque` / `*Handle` token, recover via `@ptrCast`/`@alignCast`) and a freestanding allocator (`FixedBufferAllocator` / page-style over a static buffer) — never `c_allocator` at the wasm32-freestanding boundary. Early signal it is about to bite for real: anyone rendering the `{{project}}` placeholders or wiring `src/interface/ffi/` into a build/CI step.
74+
- graduates_to_failure_mode_if: the ffi scaffold is rendered and wired into a real build/gate — at which point "ffi guest won't compile under the toolchain" becomes a live Failure Mode deserving a recovery contract.
75+
- notes: companion to the `SCAFFOLD` banner atop `src/interface/ffi/src/main.zig` (extended 2026-06-16 with this Zig-0.15.2 note) and PROOF-STATUS.md "Scaffold (NOT counted, NOT gated)". The same pattern is genuinely *bust* in typed-wasm — there it is a real failure, here a recognised near-hit.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# Dustfile — exnovation contract for snifs
3+
# Author: Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
4+
#
5+
# Paired runner: dust.ncl (NOT YET PRESENT — paired .ncl runners are an
6+
# estate-wide gap; cf the Adjustfile contractile-sync
7+
# check. Do not assume a runner exists.)
8+
# Verb: dust
9+
#
10+
# Semantics: DUST is exnovation — the deliberate, CLEAN reduction-to-dust of a
11+
# thing, "without the mess". Per component it declares a disposition
12+
# (deprecate / untangle / dependency-exit / keep-forever) plus an
13+
# explicit exit-condition, so a removal is a recorded decision with a
14+
# trigger, never an ad-hoc deletion. (Sibling: BUST = broken — WHY
15+
# things break.) Nothing here removes anything by itself.
16+
# Run with: dust check (list exnovation declarations + whether the exit-condition holds)
17+
# Fix with: dust fix (execute a declared exnovation only when its exit-condition is met)
18+
19+
@abstract:
20+
Exnovation declarations for snifs. Each entry names a component, its disposition
21+
(deprecate / untangle / dependency-exit / keep-forever), the rationale, and the
22+
explicit exit-condition that must hold before the removal is carried out. This is
23+
the deliberate-retirement ledger; it states intent + trigger, it does not delete.
24+
@end
25+
26+
## Exnovation Declarations
27+
28+
### rsr-template-ffi-scaffold
29+
- target: src/interface/ffi/ (the unrendered rsr-template C-FFI tree, incl. src/main.zig, build.zig, test/integration_test.zig)
30+
- disposition: deprecate
31+
- rationale: dead scaffold — unrendered (`{{project}}` placeholders), wired into no build or gate, and would not compile under Zig 0.15.2 even if rendered (see the Bustfile near-hit `zig-0.15.2-opaque-with-fields-template-rot`). The real guest ABI is `zig/src/{safe_nif,buffer_abi}.zig` + `verification/proofs/idris2/ABI/*` + `abi_conformance.py`.
32+
- exit-condition: remove once the template-instantiation tooling (`tests/e2e/template_instantiation_test.sh`, `scripts/validate-template.sh`, `benches/template_bench.sh`) no longer requires the ffi scaffold present, OR once that tooling is itself retired. Until then KEEP as the untouched rsr-template baseline (it is harmless: it builds nothing).
33+
- status: declared
34+
- severity: advisory
35+
36+
### orphan-rust-guest-wasm-artifact
37+
- target: priv/demo_guest_rust.wasm (813-byte build artifact in the working tree)
38+
- disposition: dependency-exit
39+
- rationale: a stale built artifact NOT loaded by the demo — `SnifDemo.RustGuest` loads `rust/target/.../demo_guest.wasm`, not this. It is a generated leftover, not source. (`priv/*.wasm` is gitignored, so it is never committed.)
40+
- exit-condition: deletable from the working tree at any time; it is regenerated on demand from `rust/crates/demo-guest`. No source depends on this path.
41+
- status: declared
42+
- severity: advisory
43+
44+
### keep-forever-dual-rust-guest-trees
45+
- target: rust/ (Cargo workspace, canonical) and rust-guest/ (standalone single-crate experiment)
46+
- disposition: keep-forever
47+
- rationale: NOT duplication-to-exnovate — owner-declared deliberate (see rust-guest/README.adoc + ADR-0005). The workspace `rust/crates/demo-guest` is the demo's guest; `rust-guest/` is the minimal proof-of-shape. Recorded here so a future hygiene sweep does not "dedupe" them.
48+
- exit-condition: none — explicit keep. Revisit only on owner instruction.
49+
- status: declared
50+
- severity: advisory

0 commit comments

Comments
 (0)