feat(worker): secondmate cloud monitor, send routing, and spawn gate (R2/R3 PR 4) - #269
Merged
Conversation
…ment request lane
…measured duration
- reclaim refuses a manifest-carrying redispatch when the assignment moved (marker records its claim assignment; the resume rmtree reconstruction) - durable verified tip in the mailbox verifier: a rewound or re-minted chain refuses instead of verifying - fm-send fences envelopes to the controller's CURRENT assignment - provider docstrings mark the runner-side generation refusal DEFERRED - TTL overrun arithmetic and local-state fragility named in the monitor header
…U stat -f does not fail)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Scope (R2R3-DESIGN.md C item 4, verbatim)
"PR 4 - fm-secondmate-cloud-monitor.sh + fm-send routing + spawn gate behind FM_SPAWN_SECONDMATE_CLOUD. O_EXCL leg-dispatch discipline (reused from fm-spawn-cloud-monitor.sh); leg 2+ requests are asserted manifest-free (a golden request-shape test - the rmtree regression guard); off-flag byte-identity golden."
Design refs: R2R3-DESIGN.md B.3 (session legs), B.4 (message lane, monitor-owned chain verification, honest latency), B.9 (gate rewrite), AMENDMENT 1. Child RELAY is PR 5 and is not here: a verified
fm.secondmate-child-request/v1renders into the pane and is otherwise not acted on.What this adds
executebehind per-leg O_EXCL markers (the fm-spawn-cloud-monitor discipline, including the stale-claim reclaim rule); leg 1 carries the payload+account staging pair, legs 2+ carry NEITHER manifest so they hit the supervisor's verified staging-skip branch instead of the rmtree; relays fm-send envelopes via the claim-exemptmessage-put; pollsmessage-collectwith the summary cursor (defensive against a cursorless summary shape); renews on a verified reason=wall summary, stops on close/idle with a terminal status file, and refuses renewal past FM_SECONDMATE_TTL_HOURS (72) from first dispatch. Release is deliberately NOT performed here (PR 6 / operator work). Each leg argv carries an FM_SECONDMATE_LEG=n prefix because execute dedupes by request digest: without it, leg n+1's manifest-free request would be byte-identical to leg n's and would replay its recorded result.chain_digest = sha256(prev_hex + content_sha256_hex)from a 64-zero genesis. ANY break refuses the ENTIRE mailbox (including entries before the break), retains every file, writes a sticky.chain-breakmarker, and the bash loop freezes relay in both directions until an operator clears it. Verified replies render into the pane; declared bundles are verified size+digest against their chained declaration and landed into the local secondmate home worktree by fast-forward only when the worktree is clean and the bundle applies; anything else keeps the bundle and reports its path (the outcome-landing doctrine).state/<id>.cloud-inbox/<seq>-<sha256>.json: one canonical, content-addressed envelope per send, text carrying the same from-firstmate marker as local secondmate sends, sequence claimed with O_EXCL.--keyrefuses (no composer exists). Only meta written by the gated cloud spawn selects this route, so every existing send path is byte-identical (unit-pinned).request --role secondmate --owner-kind primary, runs reconcile (queued stays a durable notice), records the assignment, and launches fm-secondmate-cloud-monitor.sh in the tracking pane. The spawn NEVER dispatches an execute for a compartment and writes no crewmate entrypoint. Flag off: byte-identical gate behavior with the exact pre-flag notice string (golden-tested). Compartment leg config (FM_SECONDMATE_LEG_SECONDS/POLL/IDLE/TTL_HOURS, numeric only) persists into the allowlisted cloud-env file so the closed pane environment reproduces the spawn's configuration.The envelope-generation finding (task item 2)
The runner's inbox schema on main REFUSES unknown envelope keys:
validate_closedin bin/fm-secondmate-session.py admits exactly the per-kind fields pluskindandnonce, so a first-classassignment_generationkey would bounce every captain message. The delivery-fencing generation therefore rides INSIDEnonce, the one documented free-form field: fm-send writesnonce = "<assignment_generation>/<seq08d>"(which also makes repeated identical texts distinct messages), and refuses to write an envelope at all when no assignment is recorded in meta or the controller. DEFERRED, explicitly: the runner-side refusal of an envelope naming a foreign generation (the provider docstring's PR 4 contract) needs a runner change; the runner is owned by the PR 2 lane and its closed schema cannot carry the field today. Until that lands, fencing is fm-send-side stamping plus message-put's exact-assignment gate in the lifecycle CLI.wall_seconds arithmetic
The runner exits its poll loop at
leg_seconds - min(300, leg/10)and its finish-leg can spend up to 600s (git bundle) + 600s (blob PUT) after that, so the supervisor wall must be at leastleg_seconds + 900. The monitor grantswall = leg_seconds + 1800(requirement plus equal slack) and refusesleg_seconds > 19800at startup so the wall always fits the pinned supervisor's MAX_WALL_SECONDS = 21600. Default: 14400 + 1800 = 16200.Tests (tests/fm-secondmate-cloud-monitor.test.sh, 15 units, hermetic)
The lifecycle CLI is a fixture (argv captured as unit-separator-joined lines, canned JSON, blob transfers modeled against a local store); every outbox the monitor verifies is produced by the REAL bin/fm-secondmate-session.py against that store, so the real producer feeds the real verifier. The spawn units drive the REAL fm-spawn.sh against a REAL fm-home-seed secondmate home, the fake herdr/tmux backends, and the hermetic provider protocol fixture (extended with the role=secondmate compartment tag set).
The leg-2 golden is a byte-exact comparison of the full recorded execute argv (every field, in order), so ANY drift in the manifest-free shape goes red, not just a missing flag. Neighbor suites re-run green on this branch after rebasing onto merged PR 3: fm-worker-lifecycle, fm-spawn-cloud, fm-send-secondmate-marker, fm-secondmate-session, fm-secondmate-lifecycle-e2e.
bin/fm-lint.sh(the CI shellcheck owner) exits 0 over all 243 scripts.Mutation proofs (applied, proven red, reverted)
not ok - timed out waiting for: chain-break marker(the dropped-blob unit).set -Cclaim):not ok - two racing monitors dispatched 0 executes for one leg (O_EXCL guard broken); the exactly-one assertion catches any deviation from single dispatch.-eq 1mutated to-ge 1):not ok - GOLDEN: leg 2 dispatch argv is not byte-exact manifest-free.Honest limits
Operator recipe (live compartment, for the acceptance run)
From the primary home, with the worker-lane FM_AZURE_* environment loaded and a seeded secondmate home at SUBHOME for ID:
FM_SPAWN_CLOUD=azure FM_SPAWN_SECONDMATE_CLOUD=1 bin/fm-spawn.sh <ID> <SUBHOME> --secondmate(optionally FM_SECONDMATE_LEG_SECONDS=14400 FM_SECONDMATE_IDLE_SECONDS=7200 FM_SECONDMATE_TTL_HOURS=72; leg cap 19800). The Herdr pane runs fm-secondmate-cloud-monitor.sh; it dispatches leg 1 itself once the queue entry is assigned.bin/fm-worker-lifecycle.sh reconcile --apply --confirm-subscription <sub>until assigned; the monitor converges on its own from there.bin/fm-send.sh <ID> "<text>"(routes into state/.cloud-inbox and reports the envelope path; the monitor relays it and replies render in the pane). Time this round trip for the honest-latency evidence.bin/fm-send.sh <ID> ...is not the close path; write the close control by sending nothing and letting idle fire, or enqueue afm.secondmate-control/v1 {"action":"close"}envelope manually into the inbox. Terminal status lands in state/.cloud-secondmate-status. Release stays operator work through the ordinary release lane (PR 6 adds the compartment evidence mode).Size justification
2151 added lines against the 2000-line discipline: 1071 are the hermetic test suite, whose bulk is the suite-conventional self-contained fixtures (the fake herdr backend and the provider protocol fixture are duplicated from tests/fm-spawn-cloud.test.sh by the same convention that suite followed, plus a real seeded-home spawn world). The product surface itself is 850 lines across the two new monitor binaries and the two focused edits, single-concern per design C item 4; splitting the monitor from its gate/routing would ship an unreachable monitor or an untested gate.
Adversarial review round (head 7ae05cf)
Suite is now 20 units, all ok (5 new: reclaim refusal, reclaim replay, rewind refusal, re-genesis refusal, current-assignment fencing). Two new mutation proofs, applied -> red -> reverted: dropping the reclaim assignment comparison makes the resume scenario silently redispatch the manifest-carrying leg (refusal unit red); dropping the tip comparison makes the re-genesis chain verify and deliver attacker entries (unit red). fm-worker-lifecycle suite re-run green after the provider docstring edit; full-repo bin/fm-lint.sh rc=0 (245 scripts).
Post-review CI portability fix (head 8da508b): the reclaim staleness guard now branches epoch-mtime on uname (the bin/fm-lock-lib.sh idiom) because GNU
stat -f %msucceeds with non-numeric output instead of failing, which silently disabled the reclaim path on Linux runners - the new refusal unit caught it in CI. Flagged, not fixed here: bin/fm-spawn-cloud-monitor.sh:86 carries the same latent BSD-first chain on its (CI-unexercised) crewmate reclaim path.