You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Backend exposes a SessionBlocker ledger so the frontend does not need to walk message parts and reconcile against question.list to know whether a session is genuinely blocked.
The first user-visible target is question recovery: a question remains a recoverable blocker until the user answers, skips, dismisses, the session is explicitly stopped, the instance shuts down, or a real unrecoverable state error happens.
Priority triage
P1.
Question and permission blockers sit on the core conversation loop. If PawWork asks the user a question and that question disappears, times out as if the user cancelled it, or is recoverable only through frontend snapshot heuristics, the assistant can get stuck or continue without the missing decision. That is a main-session correctness issue, not just architecture cleanup.
This priority is based on the exported unanswered-question case recorded in this issue thread: waiting for user input is not provider silence, and a no-response wait window is not a user decision.
Current P1 slice
Ship a question-only SessionBlocker ledger v0 before widening the design.
Add one active ledger entry per pending Question.Request.
Include sessionID, requestID, embedded request, tool identity, status: awaiting_user, armedAt, and updatedAt.
Wire ledger state into question lifecycle:
ask: create pending question and ledger blocker.
reply: remove pending question and ledger blocker.
explicit dismiss or skip: remove both and emit a terminal rejected reason.
session cancel, shutdown, or system error: remove both with an explicit terminal reason.
Make LLM silent-stream timeout blocker-aware. While a question blocker is awaiting user input for the stream session, silent-stream timeout must not abort the run.
Expose blocker state to the frontend. The frontend should prefer blocker state for rendering and recovering the question dock, then fall back to existing question sync and recovery paths.
Keep UI simple. If a hidden question is recovered, show the question. If recovery fails, show a visible failure state with a concrete action.
Scope
In scope:
New backend ledger keyed by session ID listing currently blocking question interactions with identity and monotonic timing.
Sync or list surface for active question blockers so the frontend can subscribe or recover.
Migration plan for the existing frontend snapshot and clock fallback in packages/app/src/pages/session/blockers/question-recovery-{snapshot,clock,reverify}.ts.
Lost-event coverage after the ledger exists.
Diagnostics or counters for ledger reconciliation outcomes where useful.
Out of scope:
Permission ledger in the first PR. Reserve the shape so permission can join later.
Persisted blockers across process restart.
Full observability dashboard work.
Removing old frontend fallback modules in the first ledger PR.
Configure a short stream timeout, start a question, do not answer, advance past timeout, and assert the question remains in blocker state and question.list().
Assert no question.rejected is emitted for no-answer timeout.
Answer after the timeout window and assert the assistant can continue with that answer.
Simulate missing question.asked or empty frontend question store while backend blocker remains active; assert the dock renders from blocker state.
Cancel, shutdown, and dismiss still clear the dock with the correct terminal reason and existing interrupted hint behavior.
E2E: simulate a dropped sync event and assert the ledger reader still surfaces the blocker.
Execution mode
Agent should investigate and propose a plan first.
Goal
Backend exposes a SessionBlocker ledger so the frontend does not need to walk message parts and reconcile against
question.listto know whether a session is genuinely blocked.The first user-visible target is question recovery: a question remains a recoverable blocker until the user answers, skips, dismisses, the session is explicitly stopped, the instance shuts down, or a real unrecoverable state error happens.
Priority triage
P1.
Question and permission blockers sit on the core conversation loop. If PawWork asks the user a question and that question disappears, times out as if the user cancelled it, or is recoverable only through frontend snapshot heuristics, the assistant can get stuck or continue without the missing decision. That is a main-session correctness issue, not just architecture cleanup.
This priority is based on the exported unanswered-question case recorded in this issue thread: waiting for user input is not provider silence, and a no-response wait window is not a user decision.
Current P1 slice
Ship a question-only SessionBlocker ledger v0 before widening the design.
Question.Request.sessionID,requestID, embedded request, tool identity,status: awaiting_user,armedAt, andupdatedAt.ask: create pending question and ledger blocker.reply: remove pending question and ledger blocker.Scope
In scope:
packages/app/src/pages/session/blockers/question-recovery-{snapshot,clock,reverify}.ts.Out of scope:
Relevant files or context
packages/app/src/pages/session/blockers/question-recovery-{snapshot,clock,reverify}.tspackages/opencode/src/question/index.tspendingmappackages/opencode/src/session/llm.tsVerification
question.list().question.rejectedis emitted for no-answer timeout.question.askedor empty frontend question store while backend blocker remains active; assert the dock renders from blocker state.Execution mode
Agent should investigate and propose a plan first.