Issue #5 (Python core migration, CAS state transitions, shadow watcher,
auto-completion gate, eager-import immutable runtime, B-13 freeze removal)
is complete and merged to main. This stamps the release.
SemVer: MINOR, not MAJOR. The change is large but breaks nothing. Across the
whole Issue #5 range there are zero file deletions and zero renames, the event
wire schema stays at SCHEMA_VERSION 1, all four literal script paths survive as
compatibility shims, the 968-line legacy shell loop is still in run_loop.sh,
and every default change reverts through a single environment variable
(MAM_DELEGATION_PY / MAM_ENGINE_CAS / MAM_LOOP_PY / MAM_LOOP_FREEZE). The
riskiest new feature, auto-completion, ships default-off. No operator has
anything to migrate, which is the decisive reason not to call it 5.0.0. The
reasoning is recorded in the VERSIONS.md release notes, not just here.
Close a gap in the lockstep guard. lib.sh exports MAM_VERSION and eager_load()
refuses to start the loop when it disagrees with FRAMEWORK_VERSION, so eager.py
is a load-bearing fourth leg — but test_version_consistency.py only checked
three, and the sole check on eager.py was a hardcoded literal in the phase 4
test that a release bump has to edit anyway. The lockstep test now covers
eager.py, a new test pins the fail-closed gate itself, and the phase 4 test no
longer carries a version literal.
Fix a latent test-isolation defect the bump exposed. The first full run failed
four tests with "MAM_VERSION mismatch: expected 4.4.0, got 4.3.0 in
environment" — 4.3.0 was not in the tree, it was inherited from the shell that
launched pytest and had sourced the pre-bump lib.sh. Those tests were measuring
the runner's shell rather than the tree under test, and only passed until now
because the two values always happened to agree. A session-scoped fixture drops
the ambient value. Production is unaffected: run_loop.sh:17 sources lib.sh
before anything else and lib.sh re-exports MAM_VERSION unconditionally, so a
stale inherited value is always overwritten before Python starts.
Full suite: 750 passed in 912.50s, 0 failed, 0 skipped.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrDNJkHmm18uu7n7JwV5Qd
Three scenarios join the contract set, so the fifteen vectors now cover an
unproven side effect, an undecidable attribution, and an agent whose state goes
unreadable in the window between finalization and notification. All fifteen
match their expected decision.
The side-effect case needed a decision about layering. Authoritative enforcement
belongs in manifest verification, which has the job record and therefore knows
what was declared; the policy honours an explicit negative from the collector so
the case is expressible as a vector. Absence is not read as proof there, because
the manifest check is the layer that fails closed on missing evidence.
The operations note records why the agent declares and the reader re-derives,
the three gates and why the backend allowlist can only be narrowed, the
notification transaction, and the rollback.
It also records a deviation from my own plan. §D2 asked for reviewer sign-off
before promoting an attempt id into the record, since that changes a contract
phase 1 froze. Measurement during implementation showed the change was not
needed: the attempt guard already lives in the evidence layer, where attempt
identity actually is, so auto-completion passes the engine a status precondition
instead. The schema is untouched, the frozen contract holds, and a
characterization test fails the moment a record starts carrying an attempt id,
which is when to revisit it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrDNJkHmm18uu7n7JwV5Qd
Records what phase 2a actually delivers and, as importantly, what it does not:
the shadow runner decides and logs, and nothing commits an inferred completion
until 2b.
state-transitions.md gains an implementation mapping so each contract row names
the code and the test that hold it up, with the inferred-completed row marked as
judged-but-not-committed.
phase2a-shadow.md is the operations note: the rollback switch, the lock
discipline and why transition_seq exists, the herdr wait rules that are easy to
get wrong (milliseconds, always name --until, errors on stderr, a timeout is a
healthy answer), the fail-closed decision order, and the 2b handoff.
task.md records P2A-00..04 with commit evidence, including two corrections to my
own plan: pick_pending was already a correct swap rather than a hole, and the
engine had to live with the registry writers because putting it in lib_py/loop
would have made delegation import a sibling and mix trees under a frozen loop.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrDNJkHmm18uu7n7JwV5Qd
Records the measured numbers and syncs every document that names the
delegation script paths, then bumps the framework to 4.3.0 in the three-way
lockstep (lib.sh, VERSIONS.md current + matrix, eight SKILL.md frontmatters).
phase1-benchmark.md carries both arms. The deterministic fake transport is what
CI asserts on: 50 publishes cost 50 connections one-shot and 1 persistent, p50
54.813ms -> 0.002ms. The real broker run is informational, since the PoC
default is a public broker: p50 1892.728ms -> 275.992ms, about 85% off.
It also corrects the plan's own estimate rather than repeating it. D8 predicted
the new shell path would drop from 3 forks to 2; the implementation is still 3,
because keeping the `_delegate_script publish_event.py` lookup is the only
thing that produces the "missing script tree does nothing" non-fatal contract
that C-02 pins. Saving that fork would change the contract, so it is deferred
to phase 4 rather than taken quietly.
SKILL.md gains the context table saying which tree wins where, so an operator
reading it knows a frozen loop runs frozen delegation code. The script paths in
every document remain correct and every quoted command still works verbatim.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrDNJkHmm18uu7n7JwV5Qd