release(v4.4.0): bump framework and eight skills to 4.4.0 in lockstep

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
This commit is contained in:
GodopuandClaude Opus 5 committed 2026-09-07 15:55:26 +09:00
1 parent b347efd760
commit 387479732e
21 files changed
+160 -40

No files matched your search

+25
View File
@@ -1065,6 +1065,31 @@ print(str(uuid.uuid4()))
# lib_py, which is why test_tier1_unit.py's inserts stay legal.)
# ==============================================================================
# ==============================================================================
# Ambient MAM_VERSION isolation.
#
# lib.sh exports MAM_VERSION, so any shell that has sourced it — an agent
# session, a developer's terminal — hands that value down to pytest and on to
# every subprocess the suite spawns. lib_py.loop.eager.eager_load() compares it
# against FRAMEWORK_VERSION and refuses to start on a mismatch, so an inherited
# value from a *pre-upgrade* shell makes the loop tests fail against a tree that
# is perfectly consistent with itself.
#
# The suite must measure the tree under test, not the version its launcher
# happened to source, so the ambient value is dropped for the whole session.
# Tests that exercise the gate set MAM_VERSION explicitly and are unaffected.
# ==============================================================================
@pytest.fixture(scope="session", autouse=True)
def _drop_ambient_mam_version():
previous = os.environ.pop("MAM_VERSION", None)
try:
yield
finally:
if previous is not None:
os.environ["MAM_VERSION"] = previous
@pytest.fixture(scope="session", autouse=True)
def delegation_tree_provenance_guard(tmp_path_factory):
yield