fix(kanban): claim-guard owner identity uses a clock-step-immune start token (t_21dfa673) - #1050
Conversation
…t token (t_21dfa673) _owner_identity compared psutil's EPOCH create_time with integer wall-clock event stamps. psutil re-bases that value on the current clock (Linux re-reads btime; macOS adjust_proc_create_time), so a wall-clock step of more than ~2 s between spawn and a later claim put a LIVE genuine owner outside [claimed_at-1, spawned_at+2]. That classified it "recycled" and let a second worker onto the card (the t_09180e10 shape). _set_worker_pid now records start_token = psutil create_time(monotonic=True), read before the write txn, in the spawned payload. That is the kernel start stamp psutil itself uses for PID-reuse identity: Linux starttime since boot, macOS raw kinfo p_starttime (psutil HISTORY NousResearch#2570: it does not follow clock updates). The claim guard matches a live PID against it within 0.05 s, with no wall clock involved, so it is immune to clock steps and DB lock lag. The causal window stays only for legacy rows without a token. Unreadable still fails closed ("unverified"). Tests: +/-10 s clock step keeps the genuine owner verified (legacy arm documents the window limit); token miss at +0.5 s/-1 s/2 h is recycled; the token beats stale stamps; the legacy LEAD/LAG edges are pinned with literal values; the task_runs.started_at fallback is pinned. Recycled-holder arms now also shift the recorded token. Mutants killed: token branch removed, token not recorded, tolerance 1.0/60, LEAD 60, LAG 60, no started_at fallback.
|
🤖 merged-by: apollo · lane: kanban-merge-pass · gate: BYPASS: FleetReview paused by Ace 2026-09-22 (state/fleetreview-pause marker present) · why: t_21dfa673: kanban claim guard: owner identity uses wall-clock window — a clock step >2s can; Argus off card review (Ace 13:08), CI green |
FleetReviewReview: post-merge · head Post-merge review ( profile: light (rule: default light: lines 311<800, files 2<1000000, hunks 12<1000000, no hot path) · round 0 · members: B-assert-ctx, L6, C-assert-xhigh, F · families: anthropic,openai Confidence: 3/5 Findings
FleetReview provenance · models: B=gpt-6-sol, C=claude-code-opus-5-5, F=gpt-6-sol · cost: $1.25 · duration: 3m 05s · rounds: 1 · files examined: 2 |
…_60634825) C7 backfill (durable-state loss) slice A. Each fix has a regression test that fails on 7a81d46 and passes here. - k103 (#987) home-session guard: takeover/operator_override events now record the home read BEFORE the guarded mutation, so `update --session <new> --takeover` names the displaced home instead of <new>. - k105 (#953) rate-limit circuit notify: the episode latch is written only after notify.py exits 0; a failed page is retried on the next tick. kanban_budget._run_notify now returns whether the send succeeded. - k107 (#1081) request_changes: a coverage comment the gate refuses on an already-held review run is rolled back with the refusal. - k109 (#1050) worker identity: `spawned` records the Linux boot_id next to the start token; a token stamped under another boot is ignored and the causal window decides. - k110 (#994) end_orphaned_terminal_runs merges its payload into existing run metadata (keeps `pool`) instead of overwriting it. - k112 (#980) model switch: the unknown-provider refusal is exempted only by a declaration on the CURRENT provider, same match as the override block. - k114 (#1254) strip_overlay restores PATH components the overlay removed. - k115 (#1035) desktop cron ticker passes a serve admission gate as can_dispatch, so a checkout hold refuses scheduled dispatch. - k121 (#1014) dashboard create homes the card to the viewing ?session=, so it stays visible under the default "this" facet. - k138 (#1024) kanban_attach refuses a supplied but invalid expected_sha256 instead of storing the file unverified. Dropped (no code change): k108, k111, k113. Reasons are in the PR body. test_desktop_cron_ticker_profiles: two exact-kwargs asserts now ignore the new can_dispatch key.
…_60634825) (#1373) C7 backfill (durable-state loss) slice A. Each fix has a regression test that fails on 7a81d46 and passes here. - k103 (#987) home-session guard: takeover/operator_override events now record the home read BEFORE the guarded mutation, so `update --session <new> --takeover` names the displaced home instead of <new>. - k105 (#953) rate-limit circuit notify: the episode latch is written only after notify.py exits 0; a failed page is retried on the next tick. kanban_budget._run_notify now returns whether the send succeeded. - k107 (#1081) request_changes: a coverage comment the gate refuses on an already-held review run is rolled back with the refusal. - k109 (#1050) worker identity: `spawned` records the Linux boot_id next to the start token; a token stamped under another boot is ignored and the causal window decides. - k110 (#994) end_orphaned_terminal_runs merges its payload into existing run metadata (keeps `pool`) instead of overwriting it. - k112 (#980) model switch: the unknown-provider refusal is exempted only by a declaration on the CURRENT provider, same match as the override block. - k114 (#1254) strip_overlay restores PATH components the overlay removed. - k115 (#1035) desktop cron ticker passes a serve admission gate as can_dispatch, so a checkout hold refuses scheduled dispatch. - k121 (#1014) dashboard create homes the card to the viewing ?session=, so it stays visible under the default "this" facet. - k138 (#1024) kanban_attach refuses a supplied but invalid expected_sha256 instead of storing the file unverified. Dropped (no code change): k108, k111, k113. Reasons are in the PR body. test_desktop_cron_ticker_profiles: two exact-kwargs asserts now ignore the new can_dispatch key. Co-authored-by: ang-fleet-workers[bot] <333956806+ang-fleet-workers[bot]@users.noreply.github.com>
Card: t_21dfa673 (found by Argus in the t_3a06ba8f r6 review of #946).
Problem
_owner_identitycompared psutil's epochcreate_time()with integer wall-clock event stamps. psutil re-bases that epoch value on the current clock. So a wall-clock step of more than ~2 s between spawn and a later claim put a LIVE genuine owner outside[claimed_at-1, spawned_at+2]. It was then classified "recycled", and the claim guard let a second worker in. Reachability is low (a claimable card with a live prior owner, plus a clock step), but it is the original t_09180e10 double-worker shape.Fix
_set_worker_pidrecordsstart_token = psutil create_time(monotonic=True)in thespawnedpayload, read before the write txn. This is the kernel start stamp psutil itself uses for PID-reuse identity: Linux starttime since boot; macOS rawp_starttime(psutil HISTORY feat(delegation): add background=true for fire-and-forget subagent tasks NousResearch/hermes-agent#2570: not clock-adjusted).unverified). Other platforms, or no psutil: no token, so the window applies.Tests (test_kanban_second_claim_class.py)
verifiedand refused. The legacy-row arm documents that the window alone misclassifies it.recycled. A token match beats stale stamps.task_runs.started_atfallback for rows without aclaimedevent is pinned.Local run of the 3 focused files (second_claim_class, reclaim_unprovable_liveness, diagnostics): 108 passed.
Mutants, each RED: token branch removed (6 failures), token not recorded (8), tolerance 1.0 and 60 (2 each), LEAD 60, LAG 60, and no started_at fallback.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.