Skip to content

Task claims from muster and the upstream; a superseded PR-review route counts as handled - #586

Merged
ZacxDev merged 5 commits into
mainfrom
claims-hq-and-superseded-routes
Nov 11, 2026
Merged

ZacxDev merged 5 commits into
mainfrom
claims-hq-and-superseded-routes

Conversation

@ZacxDev

@ZacxDev ZacxDev commented Nov 11, 2026 •

Copy link
Copy Markdown
Owner

Round 2 changes (final; the ladder ended with no 🔴).

  • Y-a: an upstream claim whose claimant session has ended on the host the mirror listed it on is released. This applies at the triage, at task_route_attention, and in the sweep. The release is recorded per (task, session) for the snapshot's revision, so the next snapshot at the same revision does not bring it back; a newer revision is judged afresh. A claimant session that no host listed is never judged ended.
  • Y-b: a claim's host is recorded only when the mirror lists the session (task_sessions.claim_host, part of 0111). There is no fallback to the request's host header, and an empty host is never judged ended.
  • 🟢: a claimed task starts no new agent. The check is in the one predicate behind both the card's offer and agent_dispatch's gate, so a question card offers no dispatch button.

Open, by decision (not changed in this PR):

  • The claim sweep reads at most 2000 claims a pass (claimSweepBatch). Past that, the oldest owners/tasks wait for a later pass.
  • There is no index on task_sessions.claimed_at. The sweep's read is a sequential filter.
  • Missing tests:
    • a sweep-level case for an offline host. The pure sessionEnded table covers an offline host; the sweep test covers a host that does not report status.
    • sweep owner-scoping across two owners. The test mirror answers the same hosts for every owner, so this needs a per-owner fake.

Round 1 changes (operator decisions).

  • Y1, releases: a session claim is released when:
    • a person, session or agent sets the task back to open;
    • the upstream reopens it;
    • it is stored as complete, by any writer;
    • the claiming session has ended. "Ended" means its window is gone from the host it claimed from while that host is online, reporting status and listing windows. muster checks this at the triage, at task_route_attention, and in a sweep every minute.
  • Y2: a claim only suppresses the pick-up card. task_route_attention with a question still raises a card on a claimed task.
  • Upstream claimants: an upstream claimant needs a valid agent_session; an agent alone is no claim.
  • The upstream rule now lives in #1099 (a session-authored pickup). Sections below that describe the "post-create agent_session" rule are superseded.

Round 0 changes (operator answers).

  • Q1: an upstream claim must name a claimant. reference.claimants is now [{agent, agent_session, name?}], and a status alone (an upstream in_progress or paused, or the owner's own card write) is no claim.
  • Q2: the newest head wins. muster reads the PR's commits through the GitHub App. A late review of an older head supersedes nothing. A verified newer head is the only supersede counted as handled. When the order is unknown, muster supersedes by arrival as before and the ask still fires (the fix(routes): a delivered PR review is handled; supersede keys on the round #413 invariant is restored).
  • D2: notes.Claimed is deleted.
  • D3: ReleaseClaims no longer takes a session.
  • D4: a dispatched agent's open releases the task's claims.

Sections below that say "or is in progress" counts as a claim, or that any later-arriving round's supersede is handled, describe the first head and are superseded by this note.

HQ companion: ZacxDev/homelab-infra#1099. It sends a claimant only for an agent_session that changed after the task was created, read from the task's history. HQ stamps the filer's agent and agent_session at create, so the create stamp never counts. A read failure sends no claimant. The rule is documented in docs/plugins.md (reference.claimants).

Two fixes in one PR.

  1. Explicit claims, from both systems. A task is claimed when a session claims the muster task, or when the upstream (the HQ task tracker, through its plugin) says the linked record is claimed. A claimed task's assignment gets no "needs you" card. The board shows who is working it, and an unclaim reopens it.
  2. A superseded PR-review route counts as handled. A route that failed only because a newer review round of the same PR superseded it no longer raises the "not acted on" ask. If the newer round's own route fails for a real reason, the ask is raised on that newer message.

1. Claims

The predicate: notes.Claims (internal/notes/claims.go) is the only definition. The triage, the chief's task_route_attention and the board all read it.

  • Muster side (a session's claim). This uses the existing pickup path, with no new tool. A status write to in_progress that carries the session's id claims the task. That covers PATCH /api/tasks/{id}/status with X-Muster-Session-Id (what muster task status <id> in_progress sends from inside a session) and a mirrored session's own task_set_status.
    • The session's task_sessions link becomes worked and gets claimed_at and claimed_by (migration 0111).
    • claimed_by is the mirrored session's name when a host of the owner lists it, otherwise session <id prefix>.
    • Nothing is inferred from focus or transcript text. A session that only read the task or commented on it is not a claim.
  • Upstream side. A reference whose last snapshot names claimants, or whose status is in_progress, is claimed.
    • With no claimant named, the status is the claim, shown as upstream (<label>).
    • A claimant whose session is a mirrored session that one of the owner's hosts lists now is linked to the task as worked.
    • The upstream claim is read from the stored snapshot, never copied. A later snapshot without it is the unclaim.
  • Effects.
    • Triage: a new task_claimed outcome. The assignment is settled as muster: task triage — left it alone: it is already claimed by <who>, with no card and no chief turn. The one case that still goes ahead is a clean forward to the single agent bound to the task.
    • Chief: task_route_attention on a claimed task raises no card and answers claimed: true with claimed_by. Either way the task comes off "Task to route".
    • Board: the card carries ⚑ worked by <who>. data-task-claimed names the sources.
  • Unclaim.
    • A status write back to open by a person (the card), a session or machine (the machine route) or an agent (the task tools) releases every session claim.
    • muster's own writers (a snapshot, a dispatch advance, the reopen after a destroy) neither claim nor unclaim.

What the HQ plugin actually sends today

I read the plugin's source (its refs.go and refclients.go), which lives outside this repo.

  • What a snapshot carries: title, url, status, status_label, rev, updated_at, body, comments (author, plus (agent <id>) when an agent wrote it) and detach, plus creator behind a flag.
    • status is the HQ status mapped onto muster's four. "in progress" and "paused" become in_progress, and "to discuss" becomes open.
    • status_label is the raw HQ status.
  • No assignment information is sent. The plugin reads HQ's assignees, but only to decide detach (the owner is no longer assigned). It never reads HQ's agent_assignees, which is what an agent's "assign me" adds.
  • Every live reference is assigned to the owner by construction. So "assigned to someone" can only mean someone other than the owner. An owner-only assignment cannot be the claim signal, or every reference would count as claimed.

Proposed minimal contract change (documented in docs/plugins.md): an optional reference.claimants: [{"kind": "person"|"agent", "name": "…", "session": "…"}].

  • The plugin would send the HQ agent_assignees, plus any assignees other than the owner, resolved to display names.
  • It would send each agent claimant's recorded session id when HQ has one.
  • Cleaning rules: anything that breaks a rule is cut, never refused. A claimant with no name is dropped. So is a kind other than person or agent, and a session that is not a session id. At most 20 claimants are kept.
  • The plugin must gate this behind a flag. A muster that predates this field answers 400 to it, the same way creator has to be gated.
  • When the field is absent, nothing breaks: the claim falls back to the snapshot status (in progress means claimed). This is tested.

The "to discuss" in_progress that never applied

On a reference, an owner's in_progress is queued for the plugin. The plugin maps it to the HQ status "in progress". A record whose type has no such status (a discussion record: proposal, to discuss, discussed, processed, wontfix) makes the plugin fail the write definitively: "the HQ task's type has no status "in progress" … not written".

A reference's status only moves when a snapshot arrives, so the task stays open. The write shows as failed on the task page.

Fix here: the claim does not depend on the status at all. It is recorded on the session's link whether the status was stored, queued or later refused. TestAClaimSurvivesAnUpstreamThatRefusesInProgress covers it: a pickup, then the plugin fails the write and pushes an open snapshot, and the task is still claimed and the triage still leaves it alone.

Not changed: the owner's own card write with no session is not a claim, because it names no claimant. Mapping a discussion record's "in progress" is the plugin's job.

2. Superseded review routes

  • The rule: supersededByNewerRound (internal/plugins/unrouted.go) is added to dispositioned. A route that is failed with exactly SupersededRouteError(<route id>) is handled, as long as the named route exists and belongs to another message.
  • One spelling: the writer (supersedePRRoutes) and the SQL pattern both use plugins.PRRouteSuperseded and SupersededRouteError.
  • Chains: a newer round that is itself superseded passes the rule on to its successor. So there is one ask, on the newest message.
  • Existing false-alarm asks: asks already open for such messages are closed by the sweep's close pass (tested).

⚠ This reverses an invariant from the audit of #413. TestALateOlderRoundDoesNotSettleAQueuedNewerRound asserted the ask fires for an unsent superseded route. Supersede orders rounds by arrival, so a late review of an older head can supersede a newer head's unsent route. The newer head's review then reaches no one, and nothing asks about it. That assertion is inverted here to match the operator's decision, and the tradeoff is written down in the test and in docs/plugins.md.

Red/green matrix (base = origin/main at the time of the run, each test run on its own database)

test origin/main head
TestASessionsPickupClaimsTheTaskAndTheTriageSkipsItsCard red (no claim recorded) green
TestAMirroredSessionsTaskToolPickupClaimsTheTask red (no claim) green
TestAnUpstreamClaimantMarksTheReferenceClaimed red (claimants refused, 400) green
TestAnUpstreamClaimantsKnownSessionIsLinked red (400) green
TestAnUpstreamInProgressStatusIsAClaim red (a card was raised) green
TestAnUnclaimReopensTheTask (both subtests) red green
TestAClaimSurvivesAnUpstreamThatRefusesInProgress red (the claim is lost) green
TestTheChiefsRouteAttentionRaisesNoCardForAClaimedTask red (400 on the fixture) green
TestASnapshotWithNoClaimantsFieldAndNoProgressGetsItsCard red (its second half sends claimants: [], 400) green
TestAnotherOwnersPickupIsNoClaim green (invariant guard) green
TestARouteSupersededByANewerRoundRaisesNoAsk red (ask raised) green
TestASupersedingRouteThatFailsRaisesTheAskOnTheNewestMessage red (an ask on the superseded message too) green
TestAnAskAlreadyRaisedForASupersededRoundIsClosed red (the ask stays open) green
TestALateOlderRoundDoesNotSettleAQueuedNewerRound (inverted) red green
TestARealRouteFailureStillRaisesTheAsk, TestASupersededRoundOfTheSameMessageIsNoExcuse, TestAFailedSupersedingRoundRaisesNoAskOnceItsPullRequestIsClosed green (invariant guards) green
e2e task-claims.spec.ts at 390×844 and 1280×800 red (no [data-task-claimed]) green

Pure unit tests of new code don't compile on the base tree, so they have no red column: TestClaimsPinnedCases, the new claimed rows of TestDecideTaskTriagePinnedCases, and TestAReferencesClaimantsAreCutNeverRefused.

Mutation sweep

Each mutant patches one expression, runs the named tests, and the file is then restored byte for byte. A test only counts as the killer if it is that mutant's own target test.

mutant killed by
M1: every session link counts as a claim TestClaimsPinnedCases
M2: upstream in_progress is no claim TestClaimsPinnedCases, TestAnUpstreamInProgressStatusIsAClaim
M3: status claim added even beside a claimant TestClaimsPinnedCases
M4: upstream claimants ignored TestClaimsPinnedCases, TestAnUpstreamClaimantMarksTheReferenceClaimed
M5: triage skip removed TestDecideTaskTriagePinnedCases, TestASessionsPickupClaimsTheTaskAndTheTriageSkipsItsCard
M6: claim skip moved ahead of the clean forward TestDecideTaskTriagePinnedCases
M7: chief's task_route_attention guard removed TestTheChiefsRouteAttentionRaisesNoCardForAClaimedTask
M8: unclaim on open removed TestAnUnclaimReopensTheTask, TestAMirroredSessionsTaskToolPickupClaimsTheTask
M9: a queued (reference) pickup claims nothing TestASessionsPickupClaims…, TestAClaimSurvives…
M10: superseded check removed TestARouteSupersededByANewerRoundRaisesNoAsk, TestASupersedingRouteThatFails…
M11: a route of the same message counts as the newer round TestASupersededRoundOfTheSameMessageIsNoExcuse
M12: the named newer route need not exist TestARealRouteFailureStillRaisesTheAsk
M13: any other message's route counts TestARealRouteFailureStillRaisesTheAsk, TestASupersedingRouteThatFails…
CTRL: the writer spells a different error TestARouteSupersededByANewerRoundRaisesNoAsk

M12 and M13 survived the first sweep. The ghost-route subtest had no other message's route in the database, and M13's run did not include the newest-message test. I fixed the fixture and the harness, and both mutants were then killed.

Ledgers touched

  • broadcast_ledger_test.go: claimOnStatus.
  • names_ledger_test.go: knownMirrorSession, a board chip rather than a notification.
  • TestSessionColsAndScanTargetsAgree: the projection gained claimed_at and claimed_by.
  • e2e/legs.json: the new spec is in leg 2 at an estimated 4 s.

🤖 Generated with Claude Code

@ZacxDev

ZacxDev commented Nov 11, 2026

Copy link
Copy Markdown
Owner Author

Round 0 (requirements & deletion) — round 0 · requirements: 13 (unattributed: 3) · deletion candidates: 5 · open premises: 2

D1 — linkUpstreamClaimants + its applyReference call + RefClaimant.Session (task_claims.go:104-121, plugin_task_refs.go:368-372): never runs, no plugin sends claimants; the thread link was not asked for
D2 — notes.Claimed (internal/notes/claims.go:58): test-only caller
D3 — ReleaseClaims' sessionID parameter (internal/notes/claims.go:94): every caller passes ""
D4 — unclaimWriters' writerAgentRoute/writerAgentTool entries (task_claims.go:46): those writers never reach claimOnStatus, so the entries are dead and docs/tasks.md's agent-unclaim sentence is wider than the code; delete or wire
D5 — reference.claimants intake + docs row: ships with no sender; defer to the plugin PR, or re-spec it against the tracker's own session-claim fields (agent / agent_session) instead of agent_assignees
Q1 — Today the upstream half is the snapshot status alone: any "in progress"/"paused" upstream (including the owner's own in_progress card write, once the snapshot returns) suppresses the card. Is status-as-claim acceptable under "sessions claim explicitly", or must the upstream claim name a claimant? · forcing: user
Q2 — Supersede orders rounds by arrival, so a late older-head review can supersede a newer head's unsent route and the newer review then raises no ask (inverting the #413 audit invariant). Does the "superseded counts as handled" decision cover that case? · forcing: user
Q1=answered: a named claimant is required — the upstream claim is the tracker's own session-claim fields (agent_session, with agent), sent by the tasks plugin in a companion PR; an upstream status alone is not a claim
Q2=answered: newest head wins — only a review of the same or a newer head supersedes; a late older-head review never hides a newer one (the #413 invariant stands)

Dispositions (operator): D1 kept, D5 kept and re-specced to agent/agent_session, D2 deleted, D3 deleted, D4 wired (an agent's own "open" releases its claim, as the docs say).

ZacxDev added a commit that referenced this pull request Nov 11, 2026
…wins a supersede

Q1 (operator): an upstream claim must name a claimant. A status alone (an
upstream in_progress or paused, the owner's own card write, a plugin that sends
no claimants) is no claim. reference.claimants is re-specified to the upstream's
claim fields: [{agent, agent_session, name?}]; the card shows name, else agent,
else the session's prefix; a claimant naming none of the three is dropped.

Q2 (operator): only a review of the same or a newer head supersedes. muster
reads the pull request's commits (githubapp.App.PullRequestCommits, the
repository-limited pull-requests read) and compareHeads orders the heads: a late
review of an OLDER head supersedes nothing; a verified newer head writes
SupersededByNewerHeadError, the only spelling counted as handled; an unknown
order (no App, a force-pushed head, an error) supersedes by arrival as before
and still raises the "not acted on" ask (the #413 invariant, its test restored).

D2: notes.Claimed deleted. D3: ReleaseClaims takes no session. D4: a dispatched
agent setting its own task back to open (its route and its tool) releases the
task's claims.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@ZacxDev

ZacxDev commented Nov 11, 2026

Copy link
Copy Markdown
Owner Author

Round 1 (nine axes) at 2aeff11, with its companion homelab-infra#1099 at 24721044: 1 🔴 (in the companion's claim rule), 3 🟡, 5 🟢 — the ladder continues. Two mutations re-run independently (every session link counts as a claim; head order flipped): both killed.

Operator decisions on the findings: the upstream claim becomes a session-authored pickup (the latest status change into "in progress" made by an actor with a session; released when the status moves back to a not-started status or to done), replacing the post-create agent_session rule (R1, Y3). On muster's side a claim releases on done, an upstream reopen and the claiming session ending (Y1), and a claim only suppresses the pick-up card — the chief can still raise a question card for a claimed task (Y2).

1. R1 (#1099 claimants.go, rollout blocker): a post-create agent_session the upstream later released (status back to "to do", or unassign-me) is still sent as a claimant, and muster keeps an agent-only claimant, so a reassigned task gets no "needs you" card
2. Y1 (muster task_claims.go:69): a session claim is not released by done, by an upstream reopen, or by the session ending
3. Y2 (muster task_routing.go:305): task_route_attention can never raise a card for a claimed task, whatever the reason
4. Y3 (#1099): real agent pickups (status plus actor.session) never change agent_session, so the claim signal misses them
5. G: 0111 header comment still says a status in progress is a claim

ZacxDev added a commit that referenced this pull request Nov 11, 2026
…wins a supersede

Q1 (operator): an upstream claim must name a claimant. A status alone (an
upstream in_progress or paused, the owner's own card write, a plugin that sends
no claimants) is no claim. reference.claimants is re-specified to the upstream's
claim fields: [{agent, agent_session, name?}]; the card shows name, else agent,
else the session's prefix; a claimant naming none of the three is dropped.

Q2 (operator): only a review of the same or a newer head supersedes. muster
reads the pull request's commits (githubapp.App.PullRequestCommits, the
repository-limited pull-requests read) and compareHeads orders the heads: a late
review of an OLDER head supersedes nothing; a verified newer head writes
SupersededByNewerHeadError, the only spelling counted as handled; an unknown
order (no App, a force-pushed head, an error) supersedes by arrival as before
and still raises the "not acted on" ask (the #413 invariant, its test restored).

D2: notes.Claimed deleted. D3: ReleaseClaims takes no session. D4: a dispatched
agent setting its own task back to open (its route and its tool) releases the
task's claims.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@ZacxDev
ZacxDev force-pushed the claims-hq-and-superseded-routes branch from 2aeff11 to d3cd253 Compare November 11, 2026 04:46
ZacxDev added a commit that referenced this pull request Nov 11, 2026
…ded session; a question is not a pickup

Y1 (operator): a session claim is released (claimReleasedBy, on setNoteStatus,
the funnel every writer passes) by an open from a person, session or agent
(stored or queued), by the upstream reopening a reference (a stored snapshot
open), and by complete stored by any writer; and when the claiming session
ENDED (sessionEnded: its window gone from the host it claimed from while that
host is online, reports status and lists windows — muster's EndGone rule). The
ended check runs at the triage and task_route_attention before they read a
claim, and in a claim sweep every minute (RunClaimSweep; one system-scoped read
of the claims, each judged and released as its owner). The claim's host is now
the mirror's host when one of the owner's hosts lists the session.

Y2 (operator): a claim suppresses only the pick-up card. task_route_attention
takes an explicit `question`; with one, the card is raised on a claimed task,
naming the question and who is working it.

An upstream claimant must carry a valid session (agent_session): an agent or
name alone is no claim (round 1: a claim is a session's pickup). D4: an agent's
open releases every session claim on its task (documented: an agent names no
session, so none is its own). 0111's header no longer says a status is a claim.
Rebased on origin/main (#588); 0111 is still the next free migration.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@ZacxDev

ZacxDev commented Nov 11, 2026

Copy link
Copy Markdown
Owner Author

Round-1 anchor carried across the rebase onto d24100f: git range-diff 2b469c05..2aeff11e d24100f7..d3cd253b maps every commit = (09b2ce5→df28af05, 077dae6→9b6ab690, 2aeff11→1b196972); the round-1 fix is the one new commit d3cd253. The round-2 delta is therefore 1b196972..d3cd253b.

@ZacxDev

ZacxDev commented Nov 11, 2026

Copy link
Copy Markdown
Owner Author

Round 2 (delta 1b196972..d3cd253b, plus homelab-infra#1099 24721044..63860d3f): R1 partially fixed (the unassign-me hand-off half remains, Y-a), Y1/Y2/Y3 and the 0111 comment fixed. 0 🔴, 2 🟡, 3 🟢 — the ladder ends here. Mutations re-run independently in both repos; all behavioural ones killed. #1099's red gitops-validate is inherited from trunk (same two files, same assertions on trunk d2d60552).

Operator decision on Y-a: a hand-off releases — the plugin releases a claim when the claiming agent is removed from the task's assignees, and muster releases an upstream claim whose session it sees ended on its host. Y-a, Y-b and the first 🟢 are fixed in a final commit (not re-audited); the rest recorded open.

1. Y-a (homelab-infra claimants.go:142-160; muster task_claims.go releaseEndedClaimsOf): an upstream claim is released only by a status move, so a session that hands off with unassign-me (no status change), or whose session ended, keeps the reassigned task's card suppressed; operator call whether a hand-off releases (🔴 if it should)
2. Y-b (muster task_claims.go:303-308 with sessionEnded:132): a claim whose session the mirror does not list takes the request's host header as its host, so the minute sweep releases a live claim from a session outside the host's listed windows (no tmux, unmirrored tmux session, beyond the OtherClaude/MaxWindows caps) and the next assignment gets a spurious card
3. G (muster task_routing.go:324): a question card on a claimed task whose muster row is still open (a queued or refused in-progress write) offers agent_dispatch buttons; chiefTaskActions ignores claims
4. G (muster notes/claims.go:146): the sweep reads at most 2000 claims ordered by owner and task, host-less rows included, so claims past the cap are never swept; no claimed_at index (a full scan each minute)
5. G (scaffolding, task_claims_sweep_pg_test.go): no sweep-level offline-host case and no owner-scoping case

ZacxDev and others added 5 commits November 10, 2026 23:51
…e counts as handled

Claims (migration 0111, internal/notes/claims.go, internal/api/task_claims.go):
- notes.Claims is THE predicate: a session's explicit claim (task_sessions.claimed_at,
  set when a status write to in_progress carries the session's id: the machine route
  with X-Muster-Session-Id, or a mirrored session's task_set_status), or a plugin
  task reference whose snapshot names claimants or is in_progress.
- reference.claimants (optional, cut never refused): [{kind, name, session}]; a
  claimant whose session a host of the owner lists is linked to the task (worked).
- The task triage leaves an assignment of a claimed task alone (task_claimed: no
  card, no chief turn; the clean forward to a bound agent still wins), and the
  chief's task_route_attention raises no card for one.
- The board card shows "worked by <who>" (data-task-claimed names the sources).
- An unclaim: a person's, session's or agent's status write to open releases every
  session claim; a snapshot without claimants and not in progress drops the
  upstream's. A status the upstream refuses (queued, then failed) does not take
  the claim with it.

Superseded review routes (internal/plugins/unrouted.go):
- A message whose route failed ONLY because a newer review round of the same pull
  request superseded it (SupersededRouteError naming another message's route) is
  dispositioned: no "not acted on" ask, and an open one is closed. The newer
  round's message raises the ask when its own route fails for a real reason.
  TestALateOlderRoundDoesNotSettleAQueuedNewerRound's ask assertion is inverted
  accordingly (operator decision).

Docs: tasks.md#claims, plugins.md (reference.claimants, the triage table, the
superseded-route rules), usage.md (task_claimed).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…wins a supersede

Q1 (operator): an upstream claim must name a claimant. A status alone (an
upstream in_progress or paused, the owner's own card write, a plugin that sends
no claimants) is no claim. reference.claimants is re-specified to the upstream's
claim fields: [{agent, agent_session, name?}]; the card shows name, else agent,
else the session's prefix; a claimant naming none of the three is dropped.

Q2 (operator): only a review of the same or a newer head supersedes. muster
reads the pull request's commits (githubapp.App.PullRequestCommits, the
repository-limited pull-requests read) and compareHeads orders the heads: a late
review of an OLDER head supersedes nothing; a verified newer head writes
SupersededByNewerHeadError, the only spelling counted as handled; an unknown
order (no App, a force-pushed head, an error) supersedes by arrival as before
and still raises the "not acted on" ask (the #413 invariant, its test restored).

D2: notes.Claimed deleted. D3: ReleaseClaims takes no session. D4: a dispatched
agent setting its own task back to open (its route and its tool) releases the
task's claims.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…te change, never the filer's stamp

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ded session; a question is not a pickup

Y1 (operator): a session claim is released (claimReleasedBy, on setNoteStatus,
the funnel every writer passes) by an open from a person, session or agent
(stored or queued), by the upstream reopening a reference (a stored snapshot
open), and by complete stored by any writer; and when the claiming session
ENDED (sessionEnded: its window gone from the host it claimed from while that
host is online, reports status and lists windows — muster's EndGone rule). The
ended check runs at the triage and task_route_attention before they read a
claim, and in a claim sweep every minute (RunClaimSweep; one system-scoped read
of the claims, each judged and released as its owner). The claim's host is now
the mirror's host when one of the owner's hosts lists the session.

Y2 (operator): a claim suppresses only the pick-up card. task_route_attention
takes an explicit `question`; with one, the card is raised on a claimed task,
naming the question and who is working it.

An upstream claimant must carry a valid session (agent_session): an agent or
name alone is no claim (round 1: a claim is a session's pickup). D4: an agent's
open releases every session claim on its task (documented: an agent names no
session, so none is its own). 0111's header no longer says a status is a claim.
Rebased on origin/main (#588); 0111 is still the next free migration.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…st is the mirror's; no dispatch on a claimed task

Y-a (operator): an upstream claim whose claimant session ended (its window gone
from the host the mirror listed it on, the sessionEnded rule) is released, at
the triage, task_route_attention and the claim sweep. The release is recorded on
the claimant's task link per snapshot revision (task_sessions.
upstream_released_rev, "rev:<rev>"), so the next snapshot still naming that
session at the same revision does not bring it back; a newer revision is judged
afresh. A claimant whose session no host listed is never judged ended.

Y-b: a claim's host (task_sessions.claim_host, new in 0111) is recorded only when
the mirror lists the claiming session; the request's host header is no longer a
fallback, and ended is judged on claim_host only (never task_sessions.host).

G: a claimed task starts no new agent: chiefDispatchTaskBlock (the one predicate
behind the card's offer and agent_dispatch's gate) refuses it, so a question card
on a claimed task offers no agent_dispatch button.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@ZacxDev
ZacxDev force-pushed the claims-hq-and-superseded-routes branch from d3cd253 to fcedc24 Compare November 11, 2026 05:52
@ZacxDev
ZacxDev marked this pull request as ready for review November 11, 2026 06:30
@ZacxDev
ZacxDev merged commit 52e4d32 into main Nov 11, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant