Require attachment reads to resolve through an actor-visible item in the requested board space. Record authoritative attachment provenance so forged comment or transition refs cannot grant blob access, while preserving legacy refs through an atomic one-time migration.
BREAKING CHANGE: BoardDialect.attachment and the /v1/board/attachment endpoint now require a board space.
The repair was synthesising placeholder tool results for any dangling
call, including calls in the last assistant message that are simply
pending (interrupted for approval/question). This broke durable resume
because the provider saw the placeholder and thought the tool already
ran, so resolve_inbox could not re-execute the tool.
Now trailing calls (assistant tool_calls as the last message with no
result) are left untouched — the engine will resume them. Placeholders
are only injected when the thread has moved past the call, proving it
is corrupt rather than pending.
Fixes test_durable_resume_question and
test_durable_resume_approval_executes_tool.
When a turn is interrupted at the wrong moment, the append-only JSONL can
end up with a user message between an assistant tool_calls block and its
tool result. Providers reject this ordering (Anthropic 400/2013, OpenAI
'tool_call_ids did not have response messages'), making the session
permanently unrecoverable from the UI.
_repair_tool_pairing() runs in ConversationStore.load() and:
- moves a real tool result found later in the thread to sit right after its call
- synthesises a placeholder result for a call with no matching tool message
- is idempotent — well-formed threads pass through unchanged
Fixes#331
Specialists offer starting points as an ask_user (free text stays available) so a picked option briefs the reviewer; title generation adds the agent first reply as evidence with a third attempt window.
The title rides the user message the moment it lands — a long agentic turn no longer holds the session name hostage; an opener signature keeps the completion hook (background turns) from burning duplicate attempts.
Approval origins (reviewer/user/bypass, notes, grants) ride the tool message display sidecar so chips and deny blocks survive reload.
The Auto-Approve explainer is persisted once per session server-side with new shorter copy; later switches persist one-line markers.
Breaker pauses loudly and resets on non-deny verdicts and ask_user answers; replies reach the judge with the agent question marked as data.
Cardless runs annotate quietly: auto-approved (reviewer reason) or approval bypassed.
Brief lives in project memory; pushed wakes share the sweep pipeline; case-ledger dedup.
Board stays the lead's substrate; outward actions ride the normal approval settings.
Worktrees of one repo share one project; one-time path-to-git re-key (board chain honestly recomputed).
Session bindings persist like team ties; grant-time presence notice; menu/binding API.
Browser sign-in on the registered loopback port; tokens in the SecretStore profile.
Rides the Responses provider for conversion/streaming; refresh-on-expiry and on-401.
REST: signin (background + status poll), status, signout; oauth rows in providers list.
First session after a server breaks (or breaks differently) gets the transcript notice;
unchanged-broken stays quiet (Connectors carries it). Personas declaring the server keep
the every-session notice. Dedupe persists across restarts; recovery re-arms it.
verify() replaces cached-yes ensure for explicit tests; dead connections tear down and reconnect.
Connected rows show 'tested ⟨when⟩' so the click has a visible result.
Storage reports remaining token lifetime (issued_at persisted) and blanks stale access
tokens so the SDK refreshes before the request; discovered AS metadata is persisted and
seeded so refresh targets the real token endpoint, not <origin>/token.
Fast-fail with a shell steer before any prompt; docstring names the closed catalog
and stops citing semgrep (pip/brew, deliberately unmanaged) as an example.