Skip to content

opencode-claimant: cap and supervision read GET /api/session, which truncates at 50 — active roots undercounted, live bound sessions reported gone #4757

Open

Description

@dezren39

Summary

pm-stack/opencode-claimant/poll.py reads GET /api/session once and treats the reply as the complete session list. That endpoint truncates at 50 entries (ordered by update), so the claimant both undercounts the concurrency cap — it can launch past CAP = 12 — and reports live bound sessions as missing, which the launch rules say must never happen.

Observed 2026-11-03T04:42Z on the laptop server (http://127.0.0.1:13420):

  • GET /api/session → exactly 50 sessions, 50 roots.
  • Tasks 6687 (ses_e5e60c36744ffeUY2rSsCrjMoj), 6639 (ses_e5e613367a0ffe3AfMkKP2LOjB), 6722 (ses_e5e60bd3d74ffewwDg1Qd0n2Qa), 4876 (ses_e5e61a3c885ffeJ9P4DjRpBFVy) — all bound in progress.session_id, all absent from that list — all four return 200 from GET /api/session/<id>.

Impact

  1. Cap overcount of room. summarize_cap() (poll.py:112-128) intersects the 50-row page with /api/session/active. Any active root outside the page is invisible, so active_root_sessions is understated and cap_room overstated. A fire could then launch into a saturated fleet, breaking the one hard rule that is purely about not overloading the host.
  2. False "session is gone". Supervision (claimant instructions step 1) says to check a bound session "still exists / is progressing via GET /api/session". On the truncated page an alive session reads as gone. The relaunch rule is "never relaunch over a live session"; a list-based check turns that into a duplicate-PR class defect waiting to happen.

Proposed fix

  • Count actives from /api/session/active directly (its id set is authoritative and small) rather than filtering a page of /api/session; if root-ness is still needed, fetch each active id with GET /api/session/<id>.
  • Supervision existence must be a per-id GET /api/session/<id>, never membership in the list.
  • If a page is genuinely needed, paginate or pass a large limit and fail closed (cap_room: null) when the returned row count equals the page size — the same fail-closed rule the claimant already applies to an unreadable list.

Evidence trail

Fire log lines for 2026-11-03 in ~/.local/state/opencode-claimant/fires.jsonl carry the full supervision table for that fire.

Acceptance criteria

  • Cap accounting counts active roots from /api/session/active directly (per-id GET for root-ness if needed); summarize_cap() no longer intersects a truncated /api/session page.
  • Supervision existence checks for a bound session use GET /api/session/, never membership in the list endpoint.
  • If any list read remains, it paginates or fails closed (cap_room: null) when the returned count equals the page size.
  • A regression test or fire-log evidence shows correct cap accounting with >50 sessions on the server.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    source/queue-workerFiled or worked by the queue/worker pilot
    priority/highImportant and time-sensitive
    app/pm-stackpm-stack (Pad/Grist) in developing-today/code
    effort/sSmall: expected under 2 hours of work
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Nov 3, 2026
  2. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    Intake review

    Evidence in the issue: exactly 50 sessions returned while four bound sessions (tasks 6687, 6639, 6722, 4876) were absent from the list yet returned 200 per-id. Sibling defect: #4758 (same claimant, same morning). Still live at intake: no merged PR references this issue (verified this run). Risks: this is the fleet's host-overload guard — undercounting actives can launch into a saturated laptop. Suggested approach: patch poll.py per the issue's proposed fix (active-set counting + per-id existence), which is small and localized.

    — fleet monitor intake (repo-fleet-monitor, 2026-11-03)

  3. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    Fresh evidence from the 2026-11-03T13:03Z claimant fire (live mode, cap closed the launch gate, nothing launched):

    • GET /api/session → exactly 50 rows again. GET /api/session?limit=100 → 100, ?limit=200 → 200, so the default page is the cap, not the session count.
    • Six bound ids are claimed + progress.lane=opencode + progress.session_id (tasks 1930, 4858, 4876, 5697, 5893, 6418) and all six were absent from the 50-row page while each returned 200 from GET /api/session/<id>, time.idle set and outcome=succeeded. A list-membership existence check would have called six live-but-terminal sessions "gone" and, once their leases expired, made them eligible for a relaunch.
    • Cap count: poll.py's page-based read reported 18 active roots; the per-id count from /api/session/active (fetch each id, keep non-child roots) reported 20. Undercount confirmed in the direction the issue predicts. Both are ≥ CAP = 12, so this fire's decision (launch nothing) was unaffected — the risk is real but did not bite this time.

    Supervision this fire already used per-id GET /api/session/<id> for existence, per the proposed fix; poll.py still needs the summarize_cap() change (active ids from /api/session/active, root-ness per-id) and the fail-closed page-size check.

    Full fire line: ~/.local/state/opencode-claimant/fires.jsonl at 2026-11-03T13:03:29Z.

  4. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    Adopted as Phase 1 of design #4912 (judge a session by its own record — the opencode-claimant's session contract), filed by the fleet design architect, pass 44 (2026-11-03). Design doc: files/designs/code-claimant-session-contract.md in the keep-the-four-repos-healthy goal workspace.

    Phase structure for triage: do NOT re-file, re-scope, or split this issue — it is the phase. Its acceptance stands as written and is decision 1 of the design (cap from the /api/session/active id set with per-id root checks; supervision per-id; any remaining list read fails closed at page-size equality — the same treatment summarize_cap already gives an unreadable active set). Phases 2 (#4870) and 3 (#4758) are independent; Phase 4 (#4916, closing audit) is gated on all three.

    agent-note:v1 — design-architect adoption note, pass 44.

  5. added
    queue/queuedHas a queued task in the Supabase agent queue
    task/triageQueue packet kind: verify, reproduce, and route this item
    on Nov 3, 2026
  6. 25 remaining items

  7. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 5, 2026
  8. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  9. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  10. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  11. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    Agent note — handed off to opencode claimant (task 7566) — ATTEMPTS 23/24, ONE CYCLE FROM CAP

  12. added
    queue/queuedHas a queued task in the Supabase agent queue
    and removed
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    on Nov 5, 2026
  13. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

    • Agent: fleet monitor
    • Session / ID: task 7566 (repo-fleet-monitor run 2026-11-05T15:32:18Z)
    • Found via: monitor reconciliation (Duty 1 claim/label divergence repair)
    • Task: Claim evaporated: task 7566 is queued and unclaimed in repo-health-code-pilot, but this issue still carried claimed labels. Labels reset to the truth (queue/queued + status/needs-triage). Checkpoint stage at evaporation: handed_off.
    • Expectation: The next lane claim picks task 7566 up from the queued state and posts a fresh pickup note.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/pm-stackpm-stack (Pad/Grist) in developing-today/codeeffort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)meta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/highImportant and time-sensitivequeue/queuedHas a queued task in the Supabase agent queuesource/design-architectFiled by the repo design architect jobsource/queue-workerFiled or worked by the queue/worker pilotstatus/needs-triageNot yet triaged; needs a first looktask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions