Skip to content

Implement production scenario driver (ADR 0007) - #71

Merged
pyrex41 merged 17 commits into
mainfrom
claude/system-testing-framework-review-nlyn0i
Jul 30, 2026
Merged

Implement production scenario driver (ADR 0007)#71
pyrex41 merged 17 commits into
mainfrom
claude/system-testing-framework-review-nlyn0i

Conversation

@pyrex41

@pyrex41 pyrex41 commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Summary

This PR implements the production scenario execution driver (urdr.run.execute) that composes the modeled laboratory's components into an end-to-end system. It adds fact routing with cascade guards, per-component model binding, pinned decision selection, and the step loop that drives seeded exploration through real scenarios with property verification.

Key Changes

Core Driver Implementation

  • Added shen/run/run.shen (699 lines): the production driver implementing ADR 0007 decisions D1 (step loop), D2 (pinned selections), and D5 (budget enforcement)
    • urdr.run.execute: main entry point taking a scenario and model table, returning discovered violations or exhausted attempts
    • Fail-closed validation phases before any world effect: scenario validation, property loading, roster pre-check, world boot
    • Step loop with menu collection and strategy-driven selection
    • Delivery-back rule: selected alternatives returned to publishing components as ordinary inputs

World Routing (ADR 0007 D4)

  • Extended shen/world/world.shen with world/v2 adding a tenth slot for routing state
    • urdr.world.initial-with-routes: boot world with declared fact routes
    • Router matches emitted facts against declared routes in declaration/emission order
    • Cascade guard: route-depth counter prevents infinite routing loops (limit: 64, bound to scenario max-steps)
    • Node targets resolve to all registered components with matching META node in canonical order

Per-Component Model Binding (ADR 0007 D3)

  • Extended shen/world/component.shen with self reserved authority for component identity
  • Updated shen/world/models/registry.shen to support optional model field in roster entries
    • Kind dispatch via association list of [KIND BUILDER] pairs
    • Per-component model binding with separate handler and init-builder
    • Every registry entry carries validated META record (id, node) for reducer to pass as self

Scenario Extensions

  • Updated shen/scenario/scenario.shen to support optional routes field
    • Routes are [route FACT FROM TARGET] with TARGET ::= [component ID] | [node NODE]
    • Validator gates fact names, from names, and component/node existence
    • Keys still strictly ascending in canonical order

Testing & Validation

  • Added shen/tests/run/run-tests.shen (590 lines): comprehensive conformance suite with fixture models
    • Tests quiescent exhaustion, discovery with failure signatures, verdicts, certificates
    • Tests determinism, seed divergence, budget enforcement (max-steps, max-events)
    • Tests cascade limit binding and property rejection
  • Added shen/tests/run/test.sh: multi-port golden output comparison
  • Added shen/tests/run/golden.txt: checked-in semantic output baseline
  • Updated existing test suites to use urdr.component.entry constructor for registry entries

Documentation

  • Added docs/adr/0007-scenario-execution-and-fact-routing.md: comprehensive ADR covering all decisions
  • Added docs/reviews/2026-07-29-deep-dive-persona-review.md: deep-dive review from four persona lenses identifying the composition gap and roadmap

Supporting Changes

  • Optimized shen/world/integer.shen: O(log(N/D)) divmod instead of O(N/D) repeated subtraction
  • Updated shen/world/eventlog.shen to encode selected choice entries with coordinates and selected alternative
  • Updated shen/world/replay.shen to handle both drawn (menu) and selected (menu + selection) choice payloads
  • Updated shen/search/search.shen and shen/search/grammar.shen to enforce canonical encoding order on choice alternatives
  • Updated shen/world/models/fault.shen documentation to clarify crash/restart routing
  • Updated test fixtures in shen/tests/models/, shen/tests/search/, shen/tests/replay/, shen/tests/properties/, shen/tests/integration/ to use new

https://claude.ai/code/session_01NzxQMRqtNAXcyo5hTx8caK

claude and others added 17 commits July 29, 2026 23:19
…ion roadmap

Four persona lenses (Torvalds, Hickey, Kingsbury, Lamport) over the full
tree at dfb03cb, with consensus findings ranked, an honest built-vs-specified
inventory, and a three-phase roadmap toward testing diverse systems with a
three-artifact configuration contract (component, scenario, properties).

Key consensus findings: no end-to-end search-finds-a-real-bug path yet
(demo oracle is a planted marker; the reference scenario's property does
not load), inert scenario fields with no scenario-to-run driver,
single-handler registry with no kernel fact routing, snapshots not
committing to facts or handler identity, and unpinned selections in
replayable transcripts. Strengths recorded with equal weight: the
canonical-value/PRNG substrate, the replay/divergence/certificate spine,
and the fail-closed evidence culture.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzxQMRqtNAXcyo5hTx8caK
…n loop)

Adds section 7.5 to the deep-dive review: an explicitly experimental,
flag-gated LLM 'brain' that participates in the testing process. Three
seats in ascending risk order (outer-loop orchestrator, within-run
policy-as-data advisor, verdict participation -- the last is refused
outright), five mandatory guardrails including full advisor-transcript
auditing and a permanent seeded-random control arm with measured lift,
and a cheapest-first deliverable. Gated on the Phase 1 exit demo and
removable without touching the core roadmap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzxQMRqtNAXcyo5hTx8caK
The reference scenario's liveness pattern was a bare symbol, but the
property engine requires the {name, source, value} record shape, so
urdr.properties.load returned properties-pattern-shape and the flagship
example's only property was dead weight. The pattern is now the record
form: an ack fact recorded by the client component, deadline unchanged
at 8, all record keys in ascending canonical order.

Only the scenario frame digest moves in the integration golden; the
explore path digest, replay root, and certificate run-id are unchanged
because exploration is seeded off the scenario seed and topology, not
the property spec bytes. The m1-integration pin is updated in both
bifrost.suite.json and the gate's EXPECTED_CASES table, which must
byte-agree.

Evidence (shen-go, the documented generation port; other launchers not
built in this environment): scenario suite 93/93 ALL PASS, integration
suite exact-golden PASS, make quality green. docs/status/m1.md still
carries the old frame digest and is refreshed in a later docs pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzxQMRqtNAXcyo5hTx8caK
…ns; wire adversarial suite

Launcher resolution in every test.sh and the adversarial runner now
follows SHEN_* env override, then URDR_DEPENDENCIES, then the repo's
.cache/urdr/dependencies pinned checkouts -- the same authority-drift
fix 1af5c27 applied to test_canonical_fixtures.py. Personal absolute
paths are gone from the harness; a stale local launcher can no longer
validate against trees the gate does not pin. Existing skip/fail
semantics are preserved per suite, except the GOLDEN_ONLY escape in
integration/search/grammar, which permitted a fail-open 'ALL PASS
ports=0' and existed only for the deleted self-blessing workflow.

Golden self-blessing is deleted: a missing golden.txt now fails with an
instruction to create it deliberately instead of being regenerated and
blessed by the run that happened to come first.

The 87 offline adversarial tests join 'make test' (they were wired into
no automated lane at all); 'make adversarial' additionally probes port
launchers and fails closed when they are missing. make quality stays
green offline at ~2.7s.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzxQMRqtNAXcyo5hTx8caK
Two numeric-core fixes, verified byte-compatible and gated together.

small.divmod was repeated subtraction, O(N/D) host ops -- the dominant
cost of software-integer division (one 256-bit divmod ~10^7 host ops).
It is now descending-weights binary restoring division, O(log(N/D)),
reusing the exact-natural-weights idiom from canonical.shen. The
doubling guard is written 2W > N as (> W (- N W)) so every host
intermediate stays within the existing ADR 0003 reviewed bound
99,990,000; no new bound is introduced. canonical.shen's own
small-divmod had the identical shape and is fixed identically (kept as
its own copy: the protocol layer must not depend on urdr.int). Ten new
divmod edge vectors land in the prng suite (integer unit-test home),
plus a 60k-case exhaustive Q*D+R=N sweep run out-of-tree. Semantics are
byte-identical: all non-prng suite outputs diffed clean against
pre-change captures. Measured on shen-go: world 5.9s -> 1.4s, search
6.5s -> 3.2s, grammar 8.6s -> 3.6s.

search.shen and grammar.shen each carried a private mag-small fold that
read limbs most-significant-first, but integer.shen magnitudes are
little-endian base-10000: [big 1 [5000 1]] (= 15000) decoded as
50000001. Unreachable today only because those paths stay below one
limb. Both copies are deleted in favor of one new urdr.int.small-of /
mag.to-small in integer.shen, symmetric with from-small: it validates
first, fails closed with the existing unsafe-host-integer code above
the from-small window, and checks width before folding so no host
arithmetic can overflow on double-backed ports. Regression cases cover
the previously-misdecoding two-limb values.

Pins: named-prng and explore-shrink/scenario-grammar program and golden
digests re-pinned in bifrost.suite.json and the gate's EXPECTED_CASES
(both must byte-agree). Evidence: make conformance REQUIRED_IMPLS=shen-go
PASS, 12/12 cases; make quality green (90 + 87 tests). Four-port
byte-identity for the re-pinned cases awaits the port sweep; only the
shen-go launcher is built in this environment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzxQMRqtNAXcyo5hTx8caK
Records the Phase 1 authority decisions before the code lands: one
driver owns scenario execution (D1); explore becomes a step loop over
published choices with selections pinned into the replayable transcript
(D2); the registry becomes data with per-component model bindings and a
reserved self identity key (D3); fact routing is kernel-owned, declared,
synchronous, and cascade-budgeted (D4); declared budgets are enforced
(D5). Alternatives recorded: router-as-component rejected on authority
grounds, silent sorting and PRNG-replay rejected as silent repair,
asynchronous routed delivery deferred with rationale.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzxQMRqtNAXcyo5hTx8caK
The component door has always rejected choice alternatives that are not
strictly ascending in canonical encoding order, so no host iteration
order can reach world semantics. The schedule door did not, and
selection is positional, so two permutations of the same alternative
set were different worlds under the same seed. The kernel now rejects
unordered (and thereby duplicate) alternatives on scheduled choice
events with the stable code choice-alternatives-order, after
encodability is proven and before any state change, via one shared
predicate (urdr.component.encoding-ascending?) used by both doors.
urdr.search.menu mirrors the check at construction time with
search-menu-order so strategy authors fail early.

Canonical atom encoding is length-prefixed, so alphabetical order is
not encoding order (4:beta sorts before 5:alpha). Four existing sources
relied on the alphabetical assumption and are fixed: the world suite's
primary fixture, the search and integration color alternatives (whose
comments asserted the broken assumption and now state the truth), and
the grammar expander's cartesian products, which are now emitted in
encoding order through an insertion sort on canonical encodings --
its author-order documentation contract is updated accordingly.

Deliberate re-pins recorded here: world (WORLD-TRACE embeds the
reordered fixture and the selected value changes; 21/21 -> 22/22 with
two new misbehaving-fixture rejection tests), search and grammar
(explore path and seed digests move with positional selection), and
integration (explore path digest only). Models and replay goldens are
byte-identical, confirming scope. Pins updated in bifrost.suite.json
and the gate's EXPECTED_CASES together; the gate's world-reducer shape
check and its unit-test fixture track the new count.

Evidence: make conformance REQUIRED_IMPLS=shen-go PASS 12/12;
make quality green (90 + 87 tests). Four-port identity awaits the
port sweep; only shen-go is built here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzxQMRqtNAXcyo5hTx8caK
…7 D3)

The model registry's kind dispatch becomes an association table of
kind -> entry-builder pairs (host data validated at boot, never
serialized), extensible by a driver-supplied table. The scenario roster
gains an optional per-component model binding: a model table maps model
symbols to handler/init-builder pairs, so two process components can
run different models with different initial states -- previously one
handler and one init threaded through the entire roster. Init-builders
receive the full roster record plus baseline. The roster's node binding
is no longer parsed and dropped, and the registry's two silent-drop
catch-alls (vocab-entries, domains) now fail closed like their sibling.

Runtime registry entries carry a validated META record {model, node}
(component-meta-shape / component-meta-value on violation; a 3-arg
convenience constructor keeps fixtures terse). The event record a
component receives gains a self key -- {id, node} built from the
entry's name and META -- and self joins the reserved-authority list:
a component may read its identity but never emit it as a claimed key.
Models decode events by key lookup, so the new key required no model
changes; the one fixture that echoed its whole event now claims time
explicitly, and a new fixture pins the self-claim rejection.

Scenario surface: roster records accept an optional model symbol (key
order id < kind < model < node), rendered only when present so the
losslessness law holds; four new fixtures pin acceptance and the
type/duplicate/misplaced rejections (duplicate and misplaced die in the
ADR 0002 decoder, documented in cases.tsv).

Deliberate re-pins: models golden 47 -> 54 cases (identity-delivery
demonstrations, unknown-model and fail-closed registry rejections;
pre-existing digests byte-identical), scenario 93 -> 97 fixtures,
program pins for the seven suites whose run-tests gained the 5-slot
entry constructor. World golden is byte-identical (22/22 unchanged).

Evidence: make conformance REQUIRED_IMPLS=shen-go PASS 12/12;
make quality green. Snapshot/certificate binding of model identity is
deliberately deferred to the ADR 0008 wave, commented in world.shen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzxQMRqtNAXcyo5hTx8caK
A scenario may now declare routes -- [route FACT FROM TARGET] with
TARGET a component id or a node -- and the kernel delivers matching
newly-emitted facts as ordinary component inputs at the current logical
time, in (route declaration order, fact emission order), with fresh
event ids ordered by the pending queue's existing (time, id). Node
targets fan out to every component whose META node matches, in
canonical component-name order. Facts matching no route remain
observations. Routing introduces no choice, no time advance, and no
PRNG draw; anything reorderable still belongs to the net component.

The world value becomes world/v2 with a tenth routing slot
[routing Rules Limit Count]: routes are a semantic input (same
components, different routes, different reductions), so they live in
world state and in the snapshot -- embedded whole rather than digested,
since routes are declaration-sized and the live same-time capacity
count must be visible. Same-time cascades are bounded: routed
deliveries at one logical time above the limit (64 this wave; the D5
driver binds it to the budget's max-steps) fail the whole commit closed
with route-cascade-exceeded, world unchanged; advancing to a strictly
later time resets the count.

Scenario surface: optional routes field between properties and seed,
same optional-field mechanics as the roster model key so losslessness
holds; fail-closed codes for shape, unknown from, unknown target, and
reserved fact names (the nine authority names are duplicated into
scenario.shen with mirror cross-references rather than inverting the
layering). Six new fixtures pin acceptance and each rejection.

fault.shen's crash/restart facts now carry {kind, members} -- literally
the process wrapper's control input, as mask is literally the net
model's -- and its stale claim that the kernel routes them is replaced
by a description of the now-real mechanism. Models suite 54 -> 65:
declared-route partition without hand-plumbing, node-target crash
fan-out, under-cap cascade, route-cascade-exceeded, per-time counter
reset, unrouted facts stay observations. The hand-scheduled mask cases
are kept as manual-path parity proof.

Re-pins: world (snapshot gains routes; suite stays 22/22), scenario
(97 -> 103 fixtures), models, replay and integration (state hashes and
run-id embed the snapshot). canonical/prng/netkat/netpol/properties/
search/grammar goldens unchanged -- audited, no world snapshot in their
outputs. The world suite's trace projection moved to the m1-large
encode profile after crossing the m0 256-node ceiling; the snapshot
itself still encodes under m0.

Evidence: make conformance REQUIRED_IMPLS=shen-go PASS 12/12;
make quality green; all 12 suites ALL PASS directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzxQMRqtNAXcyo5hTx8caK
…ADR 0007 D2)

A scheduled choice may now carry its selection: the 7-element form
[choice Sub Actor Purpose Alternatives Selected Coordinates] passes
every drawn-form validation plus coordinate shape (re-validated through
urdr.prng.coordinate, so a transcript can never carry a coordinate the
PRNG could not produce) and membership by canonical-encoding equality
(choice-selection-not-member, rejected before the event enters pending
or transcript). Dispatch applies the selection with no PRNG call and no
stream-counter advance, embedding the recorded coordinates verbatim --
they are evidence of the driver-space draw, not re-derivable. The
round-trip theorem -- a drawn choice record and its replayed selected
form are byte-identical while the selected world's stream counters stay
untouched -- is printed by the world suite and enforced digest-equal by
the gate's output-shape check.

The transcript writer/reader carries the selected form losslessly and
additively (drawn-form entries unchanged; write remains a total inverse
of read). Replay consumes recorded selections; a non-member selection
in a transcript that verified as the recorded artifact is engine/
artifact disagreement and surfaces as replay-selection-divergence in
the DIVERGED family -- the fourth divergence code, with the reasoning
recorded in replay.shen and certificate.shen. State roots can never
catch a selection change (selections live in the event stream, not the
snapshot); the replay suite proves all three tamper outcomes
(tampered / event-divergence / selection-divergence) distinctly.

urdr.search.schedule-input now emits the selected form, so explore
transcripts are pinned by construction; the shrinker classifies the new
arity (SHRINK|pinned reaches the same minimum as the drawn case).

Re-pins: world 22/22 -> 24/24 (round-trip + counter cases), replay
48 -> 55, search 14 -> 16, grammar REPLAY|full root. m1-integration is
byte-identical. Pre-existing quirk kept verbatim and documented: the
coordinate-record key is literally 'algorith'; fixing it is a
schema-wide re-pin out of scope here.

Evidence: make conformance REQUIRED_IMPLS=shen-go PASS 12/12;
make quality green; all 12 suites ALL PASS directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzxQMRqtNAXcyo5hTx8caK
…/D5)

urdr.run.execute is the single production path from a validated
scenario to a certified result: validate; load every declared property
(unloadable declarations reject before any world exists); build the
world through the registry's scenario path with the routing cascade
limit bound to the budget's max-steps (the 64 default remains for
non-driver constructors); boot each process component at time 0 in
canonical roster order (built-in models reject unknown inputs fail-
closed, so boot reaches only model-bound processes -- and a process
without a model binding is refused with run-process-unbound, since the
scenario is the whole program); then the D2 step loop: drain pending
events collecting published menus, hand open menus to the reused
search-layer strategy (driver draws stay in the search subsystem,
provably disjoint from world streams), schedule the pinned selected
form plus deliver the alternative to its publisher, until quiescence or
a budget trips. A step is one reducer input, so max-steps bounds
selections and makes per-attempt PRNG counters stride-safe and
reproducible from (seed, attempt) alone.

Budgets are enforced, not decorative: run-steps-exceeded and
run-events-exceeded abort one attempt and appear in the summary;
exhausted returns attempt count, distinct failure signatures in
first-seen order, and every attempt-level abort -- not an empty tuple.
Attempt errors are recorded, never swallowed; all attempts erroring is
run-all-attempts-failed.

The new run suite proves the chain with no fixture verdicts anywhere:
a ping/ack scenario over declared routes discovers a genuine liveness
violation through the property engine (attempt 0 passes with an entry
witness; attempt 1 selects drop, no ack fact enters the trace, FAIL
no-witness), and the certificate binds a digest of the full m1-large
scenario payload, verdicts from urdr.properties, achieved profile
modeled-d1-analog via its internal replay re-drive of the pinned
transcript. Same seed twice: identical discovery digests; different
seed: different attempt and root. The cascade fixture proves the D5
binding: five same-commit deliveries trip route-cascade-exceeded under
max-steps 4, far below the old fixed 64.

run-driver joins the gate as the 13th case (CASE_ORDER, EXPECTED_CASES,
projection prefixes, FAIL|-family output shape); module-boundaries
gains the shen/run/ root -- nothing may depend on run/, and shrink/ is
deliberately unlisted from its dependencies until the shrink wave.
Pre-existing 12 case goldens are untouched (gate re-verified byte-
for-byte). Known deferral, documented in run.shen: net and timer menus
need deliver/fire input wrapping before built-ins resolve under the
driver -- the wave that integrates them owns that rule.

Evidence: make conformance REQUIRED_IMPLS=shen-go PASS 13/13;
make quality green; run suite 7s on shen-go, exact-golden.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzxQMRqtNAXcyo5hTx8caK
… 0008)

A certified run now commits to its own observable output and to which
model produced it -- the review's consensus finding: two runs emitting
different facts from identical states had identical snapshot digests,
and no certificate could say which implementation ran.

snapshot-value's fact-count becomes facts-root: a tagged SHA-256
(urdr-facts-root-v1) over each fact's canonical encoding under
m1-large -- the profile the component door validated those values
under and the one state digests already use; the eventlog's
m0-plus-content-address rule was rejected because a snapshot binding
has no content-address escape hatch. The count is dropped entirely:
zero consumers existed and the root distinguishes every length by
construction. The snapshot's component entries carry their validated
META verbatim ({meta, name, state-digest}), discharging the D3
deferral comment.

Certificates bump to urdr-certificate-v2 with an eighteenth key:
models -- the initial world's {component, model, node} bindings in
registry order (strictly ascending by construction). Model identity
deliberately does NOT join the run-id preimage: field 3 already hashes
the initial snapshot, which now commits to the bindings, so run
identity covers them transitively -- the reasoning is written next to
the existing baseline-descriptor argument and proven executable by a
mutation case where two worlds differing only in one model binding
produce different run ids.

ADR 0008 (Accepted for M1.5) records both decisions, the profile
choice, the transitivity argument, and four rejected alternatives.

Re-pins, predicted before running and matched exactly: world 26/26
(facts-divergence and meta-visibility cases), models (the one snapshot
digest line), replay 57 (29 state-hash lines + models/mutation cases),
run (RUN|models), integration (run-id only). search/grammar goldens
byte-identical -- their digests commit to reducer inputs, not
snapshots. Gate world stanza tracks 26/26; drawn==selected equality
unchanged.

Evidence: make conformance REQUIRED_IMPLS=shen-go PASS 13/13;
make quality green; all 13 suites ALL PASS directly. Four-port matrix
re-run remains owed before the next full-matrix gate claim.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzxQMRqtNAXcyo5hTx8caK
The M1 exit demo no longer demonstrates itself. The stub counter
world, the marker-grep oracle, and every hand-written verdict are
deleted; all seven items are now produced by the production chain:
urdr.run.execute on the partition-retry scenario discovers a liveness
failure from a seed at attempt 2 (attempts 0-1 legitimately pass);
shrink minimizes 28 -> 5 entries under an EVAL that re-drives the
world and re-evaluates properties; replay verifies the minimized
artifact and reproduces the verdicts; NetKAT and netpol items hold
unchanged; the v2 certificate reports status FAIL, modeled-d1-analog,
zero uncontrolled entropy, and names its five model bindings.

The discovered failure is a real race, not a planted marker: the
fault's early partition selection is delivered while the first reply
is in flight, so the reply meets the cut at the network model and
surfaces as a routed timeout; retries meet the cut fabric; the client
gives up; no ack by deadline 8. A second temporal property
(request-served, always-eventually) pins the unanswered request as a
positive witness so the failure signature commits to a real artifact
and the shrinker cannot empty the transcript.

Design: the declared roster keeps all five component ids, each bound
to a reference process model (shen/run/models/partition-retry.shen).
Binding the built-in fault/net models under the driver is deferred
with the reason recorded: the built-in fault republishes its menu
every step and the driver resolves every open menu, so driver-run
built-ins need a menu-quiescence policy that does not exist yet --
new kernel policy, not this wave's surgery. The one real kernel
change: eventlog state digests move from the m0 to the m1-large
profile -- a 5-component, 8-route snapshot decodes to ~330 nodes and
m0's 256-node cap made every legally reached scenario-scale world
unhashable; profiles bound but never change encoding bytes, so the
change is digest-preserving (predicted zero golden movement outside
integration; held exactly).

docs/status/m1-5.md records the earned state and its non-claims
(shen-go evidence lane, four-port sweep owed, built-ins-under-driver
deferred, no-witness-only liveness until timer integration);
docs/status/m1.md's stale digests are fixed with a lane note.

Evidence: make conformance REQUIRED_IMPLS=shen-go PASS 13/13;
make quality green; integration exact-golden and deterministic across
repeated runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzxQMRqtNAXcyo5hTx8caK
urdr.model.registry.entry dispatches on the presence of a model before
it ever consults the kind, so a roster entry of kind net/timer/fault
that bound a model validated cleanly and built as that model's
crash-aware process: the declared kind was silently discarded, the run
driver's roster pre-check (which only rejects an unbound process) never
saw it, and the certificate's models field records model and node but
not kind, so the substitution left no trace in the evidence. ADR 0007
D3 only ever contemplates model bindings on process components.

Fail closed at the validation door, before any registry or world
exists: urdr.scenario.component-parts rejects a model bound on a
non-process kind with the new stable code scenario-component-model-kind
(after kind-unsupported, before unknown-node, so an unsupported kind
still reports itself). The registry's dispatch is unchanged; validated
scenarios can no longer reach it with the poisoned shape.

New protocol fixture invalid/component-model-kind.frame -- the accepted
component-model frame with its kind swapped to net -- pins the
rejection beside the existing component-model-* cases; cases.tsv
documents why validation is the only effective door, and the suite's
count pin moves 103 -> 104.

Deliberate re-pins, all scenario-parse, all three copies (goldens and
SHA256SUMS are source inputs): the fixture manifest is regenerated for
the new frame and the cases.tsv edit; bifrost.suite.json and
scripts/bifrost-gate EXPECTED_CASES move program_sha256 (the count
pin), fixture_manifest_sha256, and raw_golden_sha256 to the new
four-port semantic digest. No other suite's pin moved.

Evidence: make fmt-check and make test green; four-port scenario suite
PASS cases=104 sha256=987d8aa830b1d5bacfa8b20d81ea57c677c32832a120d821
38038fcc375e5f5b on shen-cl/shen-go/shen-rust/shen-lua; the models,
replay, integration, and run suites (the other loaders of
scenario.shen) all PASS exact-golden on all four ports.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CONTRIBUTING.md makes accepted ADRs immutable historical records; that
is exactly why these corrections must land now, while the ADR is still
Proposed. This PR implements the decision in full, and ADR 0008, which
depends on it, is already Accepted for M1.5 -- an implemented,
depended-on ADR left Proposed misstates the record in the other
direction.

- Status: Proposed -> Accepted for M1.5, matching ADR 0008.
- D4's decision text stated the route shape as
  [route FROM-COMPONENT FACT-NAME TARGET]. Everything implemented --
  the scenario validator's route records (fields ascending
  fact < from < to), the world kernel's [route FACT FROM TARGET]
  values, and the rendering -- agrees on FACT-first, so the ADR text
  is what was wrong; corrected to
  [route FACT-NAME FROM-COMPONENT TARGET].
- D3 now states that the optional model binding is legal only on a
  process component and rejected at validation otherwise, matching the
  scenario-component-model-kind door landed with this acceptance.
- Required verification items 2 and 5 are annotated as deferred rather
  than silently deleted or claimed: item 2's shrink and replay legs
  are explicitly the next wave per shen/run/run.shen's header scope
  note, and item 5's strategy-independence demonstration is absent
  because urdr.run.execute-with has no caller that configures the
  baseline strategy. Each annotation names the wave that owes the
  evidence, so acceptance claims no verification that does not exist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both ports carried pre-existing code-generation defects that made them
reject code the other two accept, and this PR is the first change deep
enough to trip them:

  shen-lua 4e3b43a (pyrex41/shen-lua#53)
    - pattern-match codegen exceeded LuaJIT's parser nesting limit
    - the reader overflowed the Lua stack on block comments over ~7.2KB
      (shen/run/run.shen's header is 8092 bytes)

  shen-cl e48776d (pyrex41/shen-cl#12)
    - accessor chains rebuilt per conditional level: 802MB of compiler
      consing for one defun, fatal in a 1GB image
    - factorise-cases spliced its fallthrough twice, so generated code
      grew 2^groups; a 33-clause dispatch function was fatal

Neither fix changes semantics: all four ports agree exact-golden on
every suite, and each port's own kernel certification is unchanged
(134/134 both, byte-identical report lines).

Pins move in both places that carry them -- build/locks/shen-ports.lock.json
and scripts/bifrost-gate EXPECTED_PINS -- and the launchers were rebuilt and
re-stamped from the new commits with `make ports` before the gate ran.
`make conformance` PASS, four ports, twelve cases, zero skips.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tion

The deferral notes were written when items 2 and 5 were both owed. The
exit demonstration has since been earned, so item 2's note was false as
written and item 5's cross-reference pointed at a wave that has landed.

Item 2 is now recorded as met, naming what actually produced it --
discovery at attempt 2, shrink 28 -> 5 under a re-driving EVAL, replay
reproducing the verdicts, a v2 certificate reporting FAIL -- and
carrying forward the two non-claims that survive it (built-ins are not
resolvable under the driver; liveness failures are the no-witness form
until timer integration).

Item 5 stays deferred, because it genuinely is: urdr.run.execute-with
still has no caller configuring `baseline`, so nothing demonstrates a
boundary-discovered transcript replaying unchanged under another
strategy. It is now recorded as a standalone debt rather than one
riding on item 2.

An ADR is a record of what was decided and what was proved. A deferral
note that outlives its deferral is the same defect as an unearned
claim, in the opposite direction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@pyrex41
pyrex41 merged commit 4516100 into main Jul 30, 2026
0 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants