Agents Builders

Session claim lease gate

Archived
session-claim-lease-gate

Created

Jun 22, 20:55

Started

Jun 23, 14:17

Completed

Jun 23, 15:35

DevOps handoff

Type

Feature

Shape

backend

Worktree Slug

session-claim-lease-gate

Repositories

mcritchie-studio

Release Train

Branch

feat/session-claim-lease-gate

Local URL

QA URL

Production URL

tooling

Acceptance Criteria

  • Building a claimed task warns unless the lease expired
  • Claim is keyed on session id plus a process nonce
  • Expired or dead-session lease is reclaimable automatically
  • Same session open in two terminals is detected

Expected Test Plan

  • [unit] ClaimLease lease math: evaluate/live?/heartbeat_age/renewed with injected clock+nonce
  • [integration] bin/task move building gate + bin/task heartbeat renewal against a stub API
  • [integration] bin/statusline heartbeat wiring + throttle

Checks Run

  • [unit] test/lib/claim_lease_test.rb — 15 runs, 0 failures (AC 1-4 + degrade)
  • [unit] test/models/task_test.rb claim helpers — green (full file 69 runs)
  • [integration] test/lib/task_cli_test.rb — 21 runs, 0 failures (gate refuse/steal/reclaim/same-instance + heartbeat)
  • [integration] test/lib/statusline_test.rb — 6 runs, 0 failures (heartbeat fire/throttle/stage-gate/no-session)
  • [integration] full bin/rails test — 1209 runs, 0 failures, 4 skips
  • [lint] rubocop clean; zeitwerk:check clean

Agent Context

V2 of session-resume (V1 shipped: stores session_id + session_provider in metadata.devops, /tasks copy-resume control, bin/statusline last-4). V2 adds the ENFORCEMENT gate. Key design decision (settled this session): claim by a LIVE INSTANCE, not bare session id — claimed_by = session_id + a per-process nonce generated at process start, with claim_expires_at (TTL lease) renewed by heartbeat. Before a session moves a task to building, refuse if it's already claimed by a different, non-expired instance ('claimed by <instance>, last heartbeat Ns ago — steal?'). The instance nonce is what catches the operator's terminal-A/terminal-B case: 'claude --resume X' in terminal B is a NEW process (new nonce) forking from the same transcript, so the gate detects a second live instance of the same session and warns. Same mechanism also catches two DIFFERENT sessions grabbing one task (the original duplicate-work problem — e.g. my #88 vs the parallel #98 this session). This is the build-stage analogue of the heartbeat lease already specced in devops-cycle-design.md §4.1. Resume link should reuse the liveness check (warn 'session looks active in another terminal — resume anyway?').

Stage Timeline

Who handled each stage, the time it took (measured), and the model / tokens / cost reported (best-effort) — plus who's on it right now. means the agent didn't report that metric.

  1. Designed Building
    A Alex
    Alex
    Model
    claude-opus-4-8
    Duration
    Tokens
    Cost
    Completed Jun 23, 14:17 · 4 days ago
    cli
  2. Building Submitted
    3
    3751cb0d-9e1d-4260-ab96-20f5b00c0547
    Model
    claude-opus-4-8
    Duration
    about 1 hour
    Tokens
    6,485,720
    Cost
    ~$4.71
    Started Jun 23, 14:17
    Completed Jun 23, 15:17 · 4 days ago
    cli
  3. Submitted Reviewed
    C Carl
    Carl primary
    S Shannon
    Shannon light
    Model
    claude-opus-4-8
    Duration
    10 minutes
    Tokens
    4,802,757
    Cost
    ~$3.65
    Started Jun 23, 15:17
    Completed Jun 23, 15:27 · 4 days ago
    cli
  4. Reviewed Assembled
    S Steffon
    Steffon
    Model
    Duration
    under a minute
    Tokens
    Cost
    Started Jun 23, 15:27
    Completed Jun 23, 15:28 · 4 days ago
  5. Assembled Shipped
    A Avi
    Avi
    Model
    Duration
    7 minutes
    Tokens
    Cost
    Started Jun 23, 15:28
    Completed Jun 23, 15:35 · 4 days ago
  6. Shipped Archived
    Model
    Duration
    about 2 hours
    Tokens
    Cost
    Started Jun 23, 15:35
    Completed Jun 23, 17:13 · 4 days ago

Conversation

QA review feedback, agent handoffs, and follow-up notes for this task.

Comment avi 4 days ago

2-senior review (reviewer-select: carl heavy + shannon light). BOTH APPROVE. Carl(heavy, adversarial) EMPIRICALLY ran the prod-only nonce resolver live inside a Claude Code bash tool: 'claude' ancestor stable, nonce identical across invocations (0d190f608cf8), TTY degrade also stable per-terminal -> AC#4 holds via 2 mechanisms; lease fails-open on every garbled/nil path (never bricks a task); heartbeat never steals a live claim; claim persists through the real API update! path (3 keys in DEVOPS_SCALAR_KEYS). 111 tests 0 failures, rubocop clean. Non-blocking: sub-second TOCTOU on same-unclaimed-task = last-writer-wins (out of scope for a soft signal); cosmetic <=120s live-dot. Shannon(light) APPROVE — board hint well-formed + degrades, CLI warn/steal message clear (stderr+exit1), --steal documented; noted 'refuses vs warns' = intended gate semantics per spec. = 2 approvals.

Sealed-bid sizing

Edit →

Alex (PM)

Avi (PO)

Dev

Actual