fix(mosaic): de-flake mutator-class lease gate TTL-expiry test #861
Reference in New Issue
Block a user
Delete Branch "fix/860-deflake-mutator-lease-gate"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
De-flakes the
whole mutator-class lease gate > observer revocation and monotonic TTL expiry deny the next mutatoracceptance test in@mosaicstack/mosaic, which flaked on push CI#1945 (expected { ok: false } to match { ok: true, decision: 'allow' }).Root cause
The test reused a single 1-second-TTL lease for two purposes: (1) an immediate "still valid" authorize check right after promotion, and (2) the deliberate real-time-wait TTL-expiry demonstration. On a CPU-contended push-CI host (concurrent build-gateway/build-appservice/build-web matrix), scheduling delay alone between
promote()and the immediate authorize check could consume the entire 1-second margin, causing the broker's monotonic clock to already read pastexpires_at— flipping the "still valid" assertion to a spurious deny. This exactly matches the CI#1945 failure signature.Red-first repro
Reproduced deterministically pre-fix by inserting an artificial ~995ms delay between
promote()and the "still valid" authorize check (simulating scheduling contention), which reliably reproduced the exact CI#1945 assertion failure:Also confirmed the fixed test remains stable (5 consecutive runs, plus a run under full-core CPU stress load).
Fix (test-determinism only)
LEASE_EXPIREDafter TTL wait,MUTATOR_UNVERIFIEDafter revoke) are unchanged and unweakened — nothing was skipped, loosened, or repointed.Gates (all from repo root)
pnpm typecheck— pass (42/42 tasks)pnpm lint— pass (23/23 tasks)pnpm format:check— passpnpm test— pass (43/43 tasks;@mosaicstack/mosaic1434/1434 tests passed, including the de-flaked case)shellchecknot applicable.Closes #860
The "observer revocation and monotonic TTL expiry deny the next mutator" acceptance test reused a 1-second-TTL lease both to prove authorization works immediately after promotion and, later, to demonstrate real monotonic-clock expiry. Under a contended push-CI host (concurrent build-gateway/appservice/web matrix), scheduling delay alone between promote() and the immediate "still valid" authorize() check could consume the entire 1-second margin, flipping that setup assertion to a spurious deny (Woodpecker push CI#1945: `expected { ok: false } to match { ok: true, decision: 'allow' }`). Reproduced deterministically by inserting a 995ms delay between promote() and the check, which reliably reproduced the exact CI#1945 failure signature. Fix is test-determinism only: the initial "lease authorizes" check now uses a normal-TTL lease with no real-time race, and a dedicated, isolated short-TTL lease drives only the deliberate wait-then-expire assertion. No production lease-broker/mutator-gate code changed, and the deny-the-next-mutator security assertions (LEASE_EXPIRED after TTL wait, MUTATOR_UNVERIFIED after revoke) are unchanged and unweakened. Closes #860 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BMpAGx3YQZT64dov5xCNJLDurable exact-head Reviewer-of-Record — APPROVE (do not merge without coordinator named-executor clearance)
Exact head bound:
06d6828d739f0e1203dc512c6d3f89756cbb9e94— confirmed unchanged at review START and END (no drift). Basemain; PR branched before #859 merged, so the TRUE merge-base isacd7d380f63d192f034266b97f02dd4c1054247e(NOT current main tip344d86a6, which advanced with the disjoint #859 squash); files are disjoint and the true prospective merge-tree is clean at6210180e37e49765fb412683ce63ae46635756a1(homelab W-jarvis independently computed).Independent CODE review — author lane
ms-860-flakefix!= reviewer lanems-861-review(distinct network-capable lane; strictly READ-ONLY — Gitea API GETs + a throwaway clone at/home/hermes/ms-861-review, since removed; no commits/pushes/tea-approve/PR-comments/merges). Persisted here as a durable comment. VERDICT: APPROVE.acd7d380AND/pulls/861/files): exactly 1 file —packages/mosaic/src/mutator-gate/mutator-gate.acceptance.spec.ts(+17/-3). ZERO production/security code (daemon.py, mutator-gate source, lease-broker) touched anywhere.deny-the-next-mutatorsecurity semantics are byte-for-byte intact — the post-TTL-expiryLEASE_EXPIRED/deny block and the post-revoke_leaseMUTATOR_UNVERIFIED/deny block are identical to the merge-base (sametoMatchObjectfields/values), only relocated after the new isolated short-TTL lease setup. No.skip/.only, no loosened matchers, no repointing.ttl_seconds=300) instead of the shared 1s lease that raced the expiry window; (b) a dedicatedshortLivedttl=1lease drives the expiry demo in isolation; (c) the third lease'scompactionEpochbumped 2->3 purely to stay distinct — verified COSMETIC:daemon.pyvalid_bindingonly checkscompaction_epochis a non-negative int and never compares it against prior state (opaque value baked into the binding hash, not an enforced monotonic counter). Both checks still exercise the same realauthorize()/broker behaviors.acd7d380, injecting a ~995ms delay betweenpromote()and the immediateauthorize()on the PRE-FIX shared-lease code reproduced the exact CI#1945 signature (expected {decision:'deny',ok:false} to be {decision:'allow',ok:true}) 3/3 runs; the identical delay against the POST-FIX code (generous-TTL lease for that check) passed 3/3 — confirming the fix removes the race, not a coincidental green. De-flaked test also ran standalone 5/5 consistent. No tautology.--no-verify, no.only/.skip, no mocking around a real failure, no commented-out logic; added comments are accurate.pnpm typecheck42/42;pnpm lint23/23;pnpm format:checkclean;mutator-gate.acceptance.spec.ts20/20 incl the de-flaked case;test:framework-shellpython/bash suite green. (3cli-smoke.spec.tsfailures observed locally = PRE-EXISTING build-order artifact needingdist/cli.jsfrom a priorpnpm build, unrelated to this PR — the authoritative Woodpecker pipeline runs the full build.)pull_request@06d6828d) = success — all 9 steps green (clone, ci-postgres, install, sanitization, upgrade-guard, typecheck, lint, format, test), every step exit 0. CI-SHA == reviewed-SHA == exact head06d6828d739f0e1203dc512c6d3f89756cbb9e94, all full-40.Non-blocking notes: the
compactionEpoch2->3 bump is unnecessary for enforcement (daemon does not check monotonicity) but harmless. The pre-existingcli-smokefailures are orthogonalpnpm testnoise (needpnpm buildfirst), not introduced here.Author != reviewer preserved (author
ms-860-flakefix, reviewerms-861-review). Independently corroborated by homelab W-jarvis (same integrity facts + prospective-tree6210180e). Merge authority is the coordinator's (Mos) — NO self-merge; awaiting a NAMED-EXECUTOR clearance.MOS NAMED-EXECUTOR PRE-MUTATION PROVENANCE — PR #861 (Closes #860)
Executor lane: Mos (main orchestrator, merge-authority). About to squash-merge PR #861 at full-40 head
06d6828d739f0e1203dc512c6d3f89756cbb9e94after primary-source 6-check re-verification (not merge-on-relay).Identity (measured this session): host
web1; tmux panemos-claude:0.0(pane_pid2312433); session_uuide3f67fa7-ed83-4635-9269-c3d0aea10f8e.6-check (Mos primary-source):
06d6828d73(true merge-baseacd7d380, disjoint from #859; prospective tree6210180e).Governance: authorized #860 flake-fix stayed within test-determinism scope; HARD GUARD (security-semantics fork) NOT triggered. Two-lead concordant: MS-LEAD 103012Z + homelab corroboration. Post-merge terminal-green descendant push-CI closes #856+#858. Shared jason.woltje token => merged_by structurally unattributable; this comment is the accountability anchor (xref framework #855).