Skip to content

fix(kanban): claim-guard owner identity uses a clock-step-immune start token (t_21dfa673) - #1050

Merged
Kyzcreig merged 1 commit into
mainfrom
fix/t_21dfa673-owner-start-token
Sep 25, 2026
Merged

Kyzcreig merged 1 commit into
mainfrom
fix/t_21dfa673-owner-start-token

Conversation

@Kyzcreig

@Kyzcreig Kyzcreig commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

Card: t_21dfa673 (found by Argus in the t_3a06ba8f r6 review of #946).

Problem

_owner_identity compared psutil's epoch create_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_pid records start_token = psutil create_time(monotonic=True) in the spawned payload, read before the write txn. This is the kernel start stamp psutil itself uses for PID-reuse identity: Linux starttime since boot; macOS raw p_starttime (psutil HISTORY feat(delegation): add background=true for fire-and-forget subagent tasks NousResearch/hermes-agent#2570: not clock-adjusted).
  • The owner is the process at the PID iff its current token matches within 0.05 s. No wall clock is involved, so a clock step or DB lock lag cannot flip the verdict.
  • Legacy rows without a token keep the causal window. Unreadable input still fails closed (unverified). Other platforms, or no psutil: no token, so the window applies.

Tests (test_kanban_second_claim_class.py)

  • Genuine owner under a +/-10 s simulated clock step stays verified and refused. The legacy-row arm documents that the window alone misclassifies it.
  • Token miss at +0.5 s, -1 s and 2 h is recycled. A token match beats stale stamps.
  • Legacy LEAD/LAG edges are pinned with literal values (holder 5 s outside each edge).
  • The task_runs.started_at fallback for rows without a claimed event is pinned.
  • The existing recycled-holder, lock-lag, unreadable and boot-time arms are kept (they now also shift or strip the token).

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.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…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.
@Kyzcreig

Copy link
Copy Markdown
Collaborator Author

🤖 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

@Kyzcreig
Kyzcreig added this pull request to the merge queue Sep 25, 2026
@Kyzcreig
Kyzcreig removed this pull request from the merge queue due to a manual request Sep 25, 2026
@Kyzcreig
Kyzcreig added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit 4861a08 Sep 25, 2026
57 checks passed
@Kyzcreig
Kyzcreig deleted the fix/t_21dfa673-owner-start-token branch September 25, 2026 05:37
@Kyzcreig Kyzcreig added the fleetreview:post-merge Ask FleetReview to review this MERGED pull (merge commit vs first parent) label Sep 25, 2026
@ang-prism

ang-prism Bot commented Sep 27, 2026

Copy link
Copy Markdown

FleetReview

Review: post-merge · head 4861a0824b6d · duration 3m 10s
Profile: light (merit: default light: lines 311<800, files 2<1000000, hunks 12<1000000, no hot path) · policy: below-size-and-path-gates
Roster: B-assert-ctx → gpt-6-sol (openai), C-assert-xhigh → claude-code-opus-5-5 (anthropic), F → gpt-6-sol (openai), L6 → gpt-6-sol (openai)

Post-merge review (fleetreview:post-merge override): this reviewed the merge commit against its first parent — the bytes that already shipped. It is not a pre-merge gate pass.

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

  • P1 hermes_cli/kanban_db.py:774 — PID reuse · agreed: B-assert-ctx (openai)
  • P1 hermes_cli/kanban_db.py:7835 — Reboot Identity · agreed: B-assert-ctx,C-assert-xhigh (openai, anthropic)
  • P1 hermes_cli/kanban_db.py:7777 — Include boot identity in Linux start tokens · agreed: C-assert-xhigh,F (anthropic, openai)
  • P1 hermes_cli/kanban_db.py:7769 — Do not accept different process start ticks within a 50 ms window · agreed: F (openai)

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

Kyzcreig pushed a commit that referenced this pull request Sep 28, 2026
…_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.
Kyzcreig pushed a commit that referenced this pull request Sep 28, 2026
…_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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fleetreview:post-merge Ask FleetReview to review this MERGED pull (merge commit vs first parent)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant