feat(869-c4): enforcement/activation version-coupling assertion #881

Merged
Mos merged 1 commits from feat/869-c4-version-coupling into main 2026-07-23 19:07:28 +00:00
Owner

Part of #869 (Point-1 C4)

Locks the lease broker's ENFORCEMENT half (launch-runtime.py) and ACTIVATION half (execLeaseGatedRuntime() / C1's LEASE_ACTIVATION_CAPABILITY) as one versioned unit, closing the #828 version-skew gap that C1 made observable but did not itself enforce.

What changed

  • New packages/mosaic/framework/tools/lease-broker/activation_version_gate.py:
    • EXPECTED_ACTIVATION_CAPABILITY — the ENFORCEMENT-side expected activation-capability constant (mirrors, but is a separate declaration from, C1's LEASE_ACTIVATION_CAPABILITY in lease-activation-probe.ts).
    • default_probe_activation_capability() — fail-closed subprocess probe of the CLI's hidden mosaic __lease-capability subcommand (the same mechanism C1's TS-side probe uses).
    • assert_activation_capability_matches() — raises VersionCouplingError with an actionable, #869-referencing remediation message on any mismatch (version OR name) or on an absent/unreadable capability.
  • launch-runtime.py now runs this assertion first, before any broker registration, and denies with a dedicated exit code (65) distinct from its existing usage-error (64) and registration-failure (1) codes. Clear stderr message, non-zero exit, never a silent pass, never a dead gate.
  • This is additive alongside — not a replacement for — the existing fail-closed-on-absent-identity gate locked in runtime_tools_unittest.py; that suite stays green (matching-capability fakes were injected into its pre-existing calls so the new gate doesn't interfere with what it already covers).

Tests (red-first)

  • New packages/mosaic/src/mutator-gate/version_coupling_unittest.py:
    • Module-level: matching capability passes; absent capability fails closed; version mismatch and name mismatch both fail loud with actionable messages; reversed-direction drift (newer activation than enforcement expects) also fails.
    • Probe-level: PATH-not-resolvable, override-command parsing, non-zero exit / malformed JSON / missing fields / boolean-as-version, timeout and OSError — all fail closed (None).
    • Seam-level (LAUNCHER.main()): matching version proceeds to registration/exec; mismatch/name-mismatch/absent all deny with the dedicated exit code and never reach broker registration or exec.
    • Verified genuinely red-first: with launch-runtime.py's wiring reverted, the 6 seam-level tests fail (TypeError/AttributeError); module-level tests (testing activation_version_gate.py directly) are unaffected by that revert, as expected.
  • mutator-gate.acceptance.spec.ts updated to supply a fake CLI-capability-probe override (MOSAIC_LEASE_VERSION_PROBE_COMMAND) everywhere it drives launch-runtime.py as a real subprocess — mirroring the fake broker / fake runtime-binary test doubles already used throughout that suite — so the new mandatory gate doesn't depend on a real mosaic binary being on PATH in CI.
  • package.json's test:framework-shell now includes the new unittest file.

Scope / conflict-avoidance

  • launch.ts and lease-activation-probe.ts are untouched — no overlap with C2 (install-guard) or C5 (doctor), and C1's constant is consumed read-only.

Gates run

  • python3 src/mutator-gate/runtime_tools_unittest.py — 25/25 pass (fail-closed lock intact).
  • python3 src/mutator-gate/version_coupling_unittest.py — 15/15 pass (new, red-first-verified).
  • pnpm run test:framework-shell (full chain) — pass.
  • pnpm exec vitest run src/mutator-gate/mutator-gate.acceptance.spec.ts — 20/20 pass.
  • pnpm test (full mosaic package) — 1015/1015 tests pass; 14 pre-existing suite-collection failures unrelated to this change (missing built dist/ for unrelated workspace packages — confirmed identical on a stash of this diff against the same checkout).
  • pnpm run lint (mosaic package) — clean.
  • Pre-push hooks (turbo typecheck, lint, root format:check) — all passed before the branch was accepted by the remote.
Part of #869 (Point-1 C4) Locks the lease broker's ENFORCEMENT half (`launch-runtime.py`) and ACTIVATION half (`execLeaseGatedRuntime()` / C1's `LEASE_ACTIVATION_CAPABILITY`) as one versioned unit, closing the #828 version-skew gap that C1 made observable but did not itself enforce. ## What changed - New `packages/mosaic/framework/tools/lease-broker/activation_version_gate.py`: - `EXPECTED_ACTIVATION_CAPABILITY` — the ENFORCEMENT-side expected activation-capability constant (mirrors, but is a separate declaration from, C1's `LEASE_ACTIVATION_CAPABILITY` in `lease-activation-probe.ts`). - `default_probe_activation_capability()` — fail-closed subprocess probe of the CLI's hidden `mosaic __lease-capability` subcommand (the same mechanism C1's TS-side probe uses). - `assert_activation_capability_matches()` — raises `VersionCouplingError` with an actionable, `#869`-referencing remediation message on any mismatch (version OR name) or on an absent/unreadable capability. - `launch-runtime.py` now runs this assertion first, before any broker registration, and denies with a dedicated exit code (`65`) distinct from its existing usage-error (`64`) and registration-failure (`1`) codes. Clear stderr message, non-zero exit, never a silent pass, never a dead gate. - This is additive alongside — not a replacement for — the existing fail-closed-on-absent-identity gate locked in `runtime_tools_unittest.py`; that suite stays green (matching-capability fakes were injected into its pre-existing calls so the new gate doesn't interfere with what it already covers). ## Tests (red-first) - New `packages/mosaic/src/mutator-gate/version_coupling_unittest.py`: - Module-level: matching capability passes; absent capability fails closed; version mismatch and name mismatch both fail loud with actionable messages; reversed-direction drift (newer activation than enforcement expects) also fails. - Probe-level: PATH-not-resolvable, override-command parsing, non-zero exit / malformed JSON / missing fields / boolean-as-version, timeout and OSError — all fail closed (`None`). - Seam-level (`LAUNCHER.main()`): matching version proceeds to registration/exec; mismatch/name-mismatch/absent all deny with the dedicated exit code and never reach broker registration or exec. - Verified genuinely red-first: with `launch-runtime.py`'s wiring reverted, the 6 seam-level tests fail (`TypeError`/`AttributeError`); module-level tests (testing `activation_version_gate.py` directly) are unaffected by that revert, as expected. - `mutator-gate.acceptance.spec.ts` updated to supply a fake CLI-capability-probe override (`MOSAIC_LEASE_VERSION_PROBE_COMMAND`) everywhere it drives `launch-runtime.py` as a real subprocess — mirroring the fake broker / fake runtime-binary test doubles already used throughout that suite — so the new mandatory gate doesn't depend on a real `mosaic` binary being on PATH in CI. - `package.json`'s `test:framework-shell` now includes the new unittest file. ## Scope / conflict-avoidance - `launch.ts` and `lease-activation-probe.ts` are **untouched** — no overlap with C2 (install-guard) or C5 (doctor), and C1's constant is consumed read-only. ## Gates run - `python3 src/mutator-gate/runtime_tools_unittest.py` — 25/25 pass (fail-closed lock intact). - `python3 src/mutator-gate/version_coupling_unittest.py` — 15/15 pass (new, red-first-verified). - `pnpm run test:framework-shell` (full chain) — pass. - `pnpm exec vitest run src/mutator-gate/mutator-gate.acceptance.spec.ts` — 20/20 pass. - `pnpm test` (full mosaic package) — 1015/1015 tests pass; 14 pre-existing suite-collection failures unrelated to this change (missing built `dist/` for unrelated workspace packages — confirmed identical on a stash of this diff against the same checkout). - `pnpm run lint` (mosaic package) — clean. - Pre-push hooks (turbo `typecheck`, `lint`, root `format:check`) — all passed before the branch was accepted by the remote.
jason.woltje added 1 commit 2026-07-23 18:46:22 +00:00
feat(869-c4): enforcement/activation version-coupling assertion
Some checks failed
ci/woodpecker/pr/ci Pipeline was canceled
eaeb633d24
Part of #869 (Point-1 C4). Locks the lease broker's ENFORCEMENT half
(launch-runtime.py) and ACTIVATION half (execLeaseGatedRuntime, C1's
LEASE_ACTIVATION_CAPABILITY) as one versioned unit, closing the #828
version-skew gap C1 only made observable.

- New packages/mosaic/framework/tools/lease-broker/activation_version_gate.py:
  EXPECTED_ACTIVATION_CAPABILITY (enforcement-owned, mirrors but is
  independent from C1's LEASE_ACTIVATION_CAPABILITY), a fail-closed
  subprocess probe of the CLI's hidden `mosaic __lease-capability`
  command, and an assertion that FAILS LOUD (VersionCouplingError with
  an actionable #869 remediation message) on mismatch OR absence —
  never a silent pass, never a dead gate.
- launch-runtime.py now runs this assertion first, before any broker
  registration, and denies with a dedicated exit code (65) distinct
  from its existing usage (64) and registration-failure (1) codes.
- Red-first tests: src/mutator-gate/version_coupling_unittest.py
  (module-level match/mismatch/name-mismatch/absent-capability cases,
  and seam-level LAUNCHER.main() cases proving the gate runs before
  broker registration and never silently passes).
- Existing fail-closed-on-absent-identity lock
  (runtime_tools_unittest.py) stays green: matching-capability fakes
  were injected into its pre-existing LAUNCHER.main() calls so the new
  gate runs alongside, not in place of, identity enforcement.
- mutator-gate.acceptance.spec.ts updated to supply a fake CLI-capability
  probe (matching the existing fake-broker/fake-runtime-binary test
  doubles already in that suite) everywhere it drives launch-runtime.py
  as a real subprocess.

launch.ts and lease-activation-probe.ts are untouched (C2/C5 avoidance,
C1 read-only).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
jason.woltje changed title from test to feat(869-c4): enforcement/activation version-coupling assertion 2026-07-23 18:47:31 +00:00
jason.woltje force-pushed feat/869-c4-version-coupling from eaeb633d24 to 90eb48fa53 2026-07-23 18:53:11 +00:00 Compare
Mos approved these changes 2026-07-23 19:07:26 +00:00
Mos left a comment
First-time contributor

Gate-16 APPROVE -- stamped by Mos (id-11) -- LAST #869 Point-1 card

PR author jason.woltje(id2) != approver Mos(id11); commit-author mosaic-coder != reviewer -> no Finding-0. Registers the independent reviewer's APPROVE (#869 Point-1 C4, version-coupling); MS-LEAD relayed, Mos stamps+merges.

VERDICT: APPROVE @ 90eb48fa53 (Part of #869 -> on merge, #869 Point-1 CODE COMPLETE: C1/C3/C5/C2/C4 all in; umbrella stays open for Point-2).

HOLD-HARD CHECK CONFIRMED (my mandated gate): runtime_tools_unittest.py diff = 19 INSERTIONS, 0 DELETIONS -- pure additive (1 helper + probe kwarg on 7 pre-existing LAUNCHER.main() calls). The fail-closed-on-absent-identity assertions (ExecutableEntrypoint/LeaseRevocation/MutatorGate) run mutator-gate.py/revoke-lease.py via runpy/subprocess, NEVER call LAUNCHER.main(), ZERO diff, 25/25 byte-unchanged -- none relaxed/removed/reordered. The version-gate is ADDITIVE (runs first, hard-stop exit65 on skew before broker-register/exec, falls through UNCHANGED to the identity gate on match). Fail-closed on every path incl bool-version rejection.

Other criteria PASS: version-coupling constant matches C1 byte-for-byte; gate ordering + seam test correct; package.json chains C2+C4 tests (15/15 + 5/5); launch.ts + C1 probe zero-diff; scope 6 files.

6-CHECK re-verified by Mos @90eb48fa53239db71f29847d9bd08b967d473785: open/not-merged/mergeable onto main d351caad/base main; CI terminal-green wp1992 (8/8) @exact head; reviewed==CI==head; independent APPROVE author!=reviewer no REQUEST-CHANGES; body 'Part of #869'; queue clear. (Reviewer disclosed refusing 2 injected out-of-band instructions -- noted, good integrity.)

## Gate-16 APPROVE -- stamped by Mos (id-11) -- LAST #869 Point-1 card PR author jason.woltje(id2) != approver Mos(id11); commit-author mosaic-coder != reviewer -> no Finding-0. Registers the independent reviewer's APPROVE (#869 Point-1 C4, version-coupling); MS-LEAD relayed, Mos stamps+merges. VERDICT: APPROVE @ 90eb48fa53239db71f29847d9bd08b967d473785 (Part of #869 -> on merge, #869 Point-1 CODE COMPLETE: C1/C3/C5/C2/C4 all in; umbrella stays open for Point-2). HOLD-HARD CHECK CONFIRMED (my mandated gate): runtime_tools_unittest.py diff = 19 INSERTIONS, 0 DELETIONS -- pure additive (1 helper + probe kwarg on 7 pre-existing LAUNCHER.main() calls). The fail-closed-on-absent-identity assertions (ExecutableEntrypoint/LeaseRevocation/MutatorGate) run mutator-gate.py/revoke-lease.py via runpy/subprocess, NEVER call LAUNCHER.main(), ZERO diff, 25/25 byte-unchanged -- none relaxed/removed/reordered. The version-gate is ADDITIVE (runs first, hard-stop exit65 on skew before broker-register/exec, falls through UNCHANGED to the identity gate on match). Fail-closed on every path incl bool-version rejection. Other criteria PASS: version-coupling constant matches C1 byte-for-byte; gate ordering + seam test correct; package.json chains C2+C4 tests (15/15 + 5/5); launch.ts + C1 probe zero-diff; scope 6 files. 6-CHECK re-verified by Mos @90eb48fa53239db71f29847d9bd08b967d473785: open/not-merged/mergeable onto main d351caad/base main; CI terminal-green wp1992 (8/8) @exact head; reviewed==CI==head; independent APPROVE author!=reviewer no REQUEST-CHANGES; body 'Part of #869'; queue clear. (Reviewer disclosed refusing 2 injected out-of-band instructions -- noted, good integrity.)
Mos merged commit a32ce4c8f9 into main 2026-07-23 19:07:28 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#881