Files
kandev/docs/plans/clarification-active-lifecycle/plan.md
T
2026-08-23 17:34:52 +00:00

511 lines
37 KiB
Markdown

---
spec: docs/specs/tasks/requirements/clarification-active-lifecycle.md
created: 2026-08-14
status: completed
---
# Implementation Plan: Active Clarification Lifecycle
## Overview
Make current-turn ownership the single definition of an active clarification, then use that
authority in workflow guarding, detachment, response validation, task-summary projection, chat
state, and task navigation. Black-box desktop/mobile regressions land and fail first. Backend
authority and summary convergence follow, then frontend current-turn and pending-session selection,
then production-build E2E returns green.
No schema migration, task-summary shape, new HTTP route, or direct mutation of the reported main
instance is required.
### Confirmed root cause and reproduction evidence
- `task_session_messages.metadata.status` retained older detached rows as `pending`, by design.
- `FindPendingClarificationMessagesBySessionID` scanned every historical turn. Every later turn
completion found those rows, wrote `agent_disconnected=true` again, and republished
`message.updated`.
- The workflow clarification guard used that same unbounded finder, so an inert historical row could
block `on_turn_complete`.
- `GetPendingActionsBySessionIDs` already scoped task/session API projections to the latest surviving
message turn. The live main instance therefore returned no flat pending action for two affected tasks
while their persisted `status_summary.pending_action` still said `clarification`. Review also found
that message deletion could move this provisional boundary backward despite a newer durable turn.
- The status projector restored that cached summary and tracked one request identity per session.
Existing-summary hydration repaired only missing rows, so a stale pending field survived restart
and reload.
- Frontend loaded-message discovery scanned all turns, allowing an older pending bundle to reappear
after the visible newer bundle was rejected.
- One reported task had a genuine current clarification in a non-primary secondary session. The
task-level icon was correct, but desktop and phone activation preferred remembered/primary session,
hiding the question.
Read-only inspection found three projected clarification tasks: two stale projections with no
current-turn action and one genuine secondary-session action. Logs for the first case showed an old
bundle detached, a newer bundle rejected, and a later completion republishing the old pending row.
---
## Backend
### Current-turn clarification authority
- In `apps/backend/internal/task/repository/sqlite/message.go`, replace the historical pending finder
with `FindActiveClarificationMessagesBySessionID`. Select the newest durable
`task_session_turns` row per session, then restrict messages to that turn. Reuse this derivation in
`GetPendingActionsBySessionIDs` so SQLite and PostgreSQL agree and message deletion cannot reactivate
an older bundle.
- Treat missing clarification status as pending for parity with the existing compact projection.
Return only current-turn `clarification_request` rows whose status is empty or `pending`.
- Rename the repository interfaces and focused mocks in
`apps/backend/internal/task/repository/interface.go`,
`apps/backend/internal/clarification/handlers.go`, and
`apps/backend/internal/orchestrator/service.go`.
- In `apps/backend/internal/clarification/canceller.go`, use the active finder for detach and expiry
fallback. Skip an already-terminal or already-detached row before writing or publishing. Count only
bundles that changed so repeated completion is a semantic no-op.
- In `apps/backend/internal/orchestrator/clarification_guard.go`, guard only on active current-turn
rows. Preserve fail-closed behavior when the authoritative query fails.
- In `apps/backend/internal/clarification/handlers.go`, validate the database-fallback response
against active current-turn ownership before any fallback write. A detached current-turn answer
succeeds only after one synchronously acknowledged orchestrator resume; a detached current-turn
rejection persists without resuming the agent. Any answer or rejection for a superseded/terminal
bundle returns conflict without a write or agent resume.
- Order durable turns identically by `started_at`, `created_at`, then `id` descending. Add an
environment-gated PostgreSQL behavior test for the changed query. No migration or persisted-row
rewrite.
### Authoritative summary convergence
- Add a `PendingActionLoader` to `apps/backend/internal/task/statussummary/projector.go`. It returns
current actions keyed by input-capable session for one task.
- Wire the loader in `apps/backend/internal/backendapp/gateway.go` using the task repository's bounded
session list and `GetPendingActionsBySessionIDs`; do not load transcripts.
- In `apps/backend/internal/task/statussummary/projector_events.go`, refresh pending state from the
loader after message, permission, and clarification occurrences. Ordinary messages in a newer turn
therefore clear superseded questions. Event ordering and `pending_id` memory no longer define the
production projection.
- Refresh authoritative pending state while restoring a persisted projection and after compare-and-set
rejection. On loader failure, retain last known state and surface the error; never optimistically
clear a question.
- Rename/extend `HydrateMissingTaskStatusSummaries` in
`apps/backend/internal/task/service/service_status_summary_rebuild.go` to reconcile existing summary
pending fields as well as build absent rows. For an existing row, clone the latest stored summary,
replace only `PendingAction`, advance revision/time on semantic change, and use bounded CAS
reload/retry.
- Publish `task.status_summary.updated` after a successful existing-row repair so other connected
clients converge. Preserve unrelated primary, activity, error, Git, PR, and queued-prompt fields.
- Update the task-list and boot callers in
`apps/backend/internal/task/handlers/task_http_handlers.go` and
`apps/backend/internal/backendapp/boot_state.go` to use the reconciler.
---
## Frontend
### Current-turn transcript discovery
- In `apps/web/lib/utils/pending-clarification.ts`, accept the newest durable turn ID from the existing
`turns.bySession` state and bound clarification discovery to it. While turn history is unavailable,
use the session's authoritative `pending_action` rather than treating the latest surviving message
as current; retain latest-message fallback only for legacy sessions with no turn records.
- Build the selected bundle only from that turn and exact `pending_id`; terminal or older-turn rows
cannot become the overlay fallback after a newer bundle is skipped.
- Extend `apps/web/lib/utils/pending-clarification.test.ts` with older-pending/newer-turn, same-turn
bundle, missing-turn legacy, newer-rejected-bundle, and deleted-newer-turn-message cases.
### Pending-owner task navigation
- Add a pure resolver in `apps/web/components/task/task-select-helpers.ts` that selects the first
(newest, because the API order is newest-first) input-capable session whose `pending_action`
matches the task summary action. When a task advertises pending input but no matching input-capable
owner exists, fail closed and navigate only to the task; never guess the remembered, primary, or
first session. Clean tasks retain those normal session fallbacks.
- When a desktop task advertises pending input, always finish the existing session-list load before
switching, even if the preferred session already has an environment mapping. Apply the same rule
when the global sidebar starts from a non-task route. Keep the existing selection-token and
external-navigation race guards, and invalidate deferred off-route selection when the pathname
changes or its initiating sidebar callback unmounts.
- Reuse the resolver in
`apps/web/components/task/mobile/session-task-switcher-sheet-hooks.ts`; remove the synchronous
primary fast path for a pending task, close the drawer only after the selected session/URL is set,
and preserve failure fallback.
- Permission uses the same navigation rule because `TaskPendingAction` is shared. This plan does not
change permission lifecycle semantics.
### Mobile design contract
- **Desktop outcome / phone entry:** both task-row surfaces lead to the session that owns the visible
pending indicator. Phone entry stays the existing Tasks control and task-switcher drawer.
- **Nearest exemplar:** `session-task-switcher-sheet.tsx` remains the shipped inset bottom drawer with
its existing safe-area, focus, dismissal, and internal-scroll behavior.
- **Hierarchy:** task row remains the only choice. No second question-specific control is added.
- **Presentation:** no visual, copy, geometry, or route redesign.
- **Shared logic:** one pure pending-owner resolver drives desktop and phone selection.
- **Touch behavior:** phone E2E uses `.tap()`, asserts drawer dismissal, task URL, active chat, and
question visibility.
---
## Tests
- **What:** only newest-durable-turn clarification rows are active; older-turn pending rows are
excluded, missing status remains pending only in that turn, deleting every newer-turn message does
not reactivate history, and SQLite/PostgreSQL agree.
- **Files:** `apps/backend/internal/task/repository/sqlite/message_active_clarification_test.go`,
`apps/backend/internal/task/repository/sqlite/message_crud_coverage_test.go`, and
`apps/backend/internal/task/repository/sqlite/message_pending_postgres_test.go`
- **How:** real database tests create two turns with pending rows, delete every message in the newer
turn, and verify the active finder and compact pending projection still exclude the older bundle.
- **What:** repeated detach does not update or publish an already-detached bundle; a new turn prevents
old-row detachment; query failure keeps the workflow guard closed.
- **Files:** `apps/backend/internal/clarification/canceller_test.go`,
`apps/backend/internal/orchestrator/clarification_guard_test.go`, and
`apps/backend/internal/orchestrator/event_handlers_agent_clarification_test.go`
- **How:** focused fakes count writes/events, plus orchestrator service tests around the repository
boundary.
- **What:** current-turn detached response still resumes or dismisses as appropriate, but a stale
older-turn response returns conflict and initiates no agent resume.
- **File:** `apps/backend/internal/clarification/handlers_test.go`
- **How:** handler-to-repository integration with persisted bundles and an empty in-memory store.
- **What:** projector restore, newer ordinary message, terminal events, and CAS races converge to the
authoritative pending map without losing another summary field.
- **Files:** `apps/backend/internal/task/statussummary/projector_test.go` and
`apps/backend/internal/task/service/service_status_summary_rebuild_test.go`
- **How:** table-driven projector loader tests and real summary-store CAS tests, including loader
failure and a competing revision.
- **What:** boot/task-list repair an existing stale summary, not only an absent row.
- **Files:** `apps/backend/internal/backendapp/status_summary_boot_test.go` and the task HTTP handler
tests nearest existing status-summary coverage.
- **How:** persisted stale summary plus current-turn messages, then assert returned/persisted revision
and complete replacement event.
- **What:** loaded transcript discovery follows durable turn identity through message deletion and
preserves legacy no-turn data.
- **File:** `apps/web/lib/utils/pending-clarification.test.ts`
- **How:** pure Vitest message arrays.
- **What:** pending owner outranks remembered/primary selection on desktop and phone while clean tasks
preserve existing preference and async race behavior.
- **Files:** `apps/web/components/task/task-select-helpers.test.ts` and
`apps/web/components/task/mobile/session-task-switcher-sheet-hooks.test.ts`
- **How:** pure resolver cases plus asynchronous selection harnesses.
---
## E2E Tests
- **Scenario:** an older detached question survives, a newer question is asked and skipped, then the
old question never resurfaces.
- **File:** `apps/web/e2e/tests/task/sidebar-pending-question.spec.ts`
- **What to verify:** current overlay closes, sidebar question icon clears, a later turn completion
cannot re-arm it, and reload preserves the clear state.
- **Scenario:** a secondary session owns the task's only current clarification.
- **File:** `apps/web/e2e/tests/task/sidebar-pending-question.spec.ts`
- **What to verify:** clicking the task row from another task loads sessions, activates the secondary
session instead of the clean primary, and displays the clarification.
- **Scenario:** the same secondary-session task is selected from the phone task drawer.
- **File:** `apps/web/e2e/tests/task/mobile-sidebar-task-actions.spec.ts`
- **What to verify:** touch activation selects the pending secondary, closes the inset drawer,
updates `/t/:taskId`, shows the task title and question, and creates no horizontal overflow.
- **Scenario:** a detached current-turn question remains answerable.
- **File:** existing `apps/web/e2e/tests/chat/clarification.spec.ts`
- **What to verify:** the existing timeout/deferred-answer regression still resumes before a newer
turn supersedes the question.
Use API helpers only to establish sessions/turns; all outcomes are asserted through task/chat UI and
survive reload. Managed runner builds production artifacts. No fixed sleeps or widened timeouts.
---
## Verification Results
All locally available task gates passed; environment-gated parity also passed in PR CI:
- Backend authority: repository, clarification, and orchestrator packages passed. PostgreSQL parity
was skipped locally without `KANDEV_TEST_POSTGRES_DSN` and passed in PR CI.
- Summary convergence: status-summary, task-service, backend-app, and focused handler tests passed.
- Frontend: 3 Vitest files / 68 tests, TypeScript, i18n, and frozen install passed.
- Final review remediation: backend repository, clarification, and orchestrator packages passed;
changed backend lint reported zero issues. Six focused web files passed 108 tests, followed by
TypeScript, zero-warning full lint, i18n check, and i18n ratchet.
- Dispatch-acknowledgement remediation: repository, clarification, and orchestrator packages passed;
focused detached-resume tests and changed backend lint reported zero issues.
- Restart-window remediation: unpublished successor reservations persist exact clarification recovery
identity, startup restores empty unattempted reservations before executor reconciliation, and
message-backed reservations become accepted authority. Focused SQLite/startup tests passed;
PostgreSQL parity is environment-gated.
- Session-selection review remediation: six shared desktop/mobile selection and removal suites passed
75 tests; typecheck, zero-warning full lint, and the i18n ratchet passed.
- Rejected-load review remediation: both primary-session and sessionless pending-selection fallbacks
revalidate click-time summary ownership; the focused race suite passed 14 tests.
- Review remediation: agentctl acceptance is not reported as success until the reserved successor is
durably published; post-acceptance publication failure remains terminal and rollback-ineligible.
Clarification overlay and transcript filtering now share the same optional authority scope.
- At-most-once remediation: immediately before external executor dispatch, a durable attempt marker
makes restart recovery fail closed across the acceptance/publication crash window. Marker-write
failure rolls back before dispatch; known synchronous rejection remains safely rollback-eligible.
- Ready-race remediation: live reservations broadcast acceptance or rollback. An overlapping ready
event waits for that result and revalidates prompt generation, preventing a delayed predecessor from
completing the successor or running workflow completion against it.
- Final bot-review remediation: clarification persistence operations use fresh bounded contexts;
post-acceptance transport failures run cleanup without reopening the answer; reservation rollback
lets the still-authoritative predecessor ready event complete; startup recovery errors fail before
prompt admission; and stale pending-owner loads still open the requested task on desktop and mobile.
Full clarification/orchestrator tests, ten race-enabled focused repetitions, changed-code backend
lint, 55 focused web tests, zero-warning focused web lint, typecheck, and i18n ratchet passed.
- Follow-up bot review: clarification detach database failures are error-level diagnostics, and pause
cancellation treats a wired no-active-turn snapshot as explicit authority. A first turn created
during detachment is preserved, matching the existing successor-turn protection; both focused races
passed ten repetitions, followed by the full orchestrator and clarification suites and zero-issue
changed-code backend lint.
- Later bot review: failed reserved-turn rollback keeps its live reservation unresolved, blocking ready
handling and prompt admission until restart recovery; clarification detach and expiry repository
operations now use fresh detached 30-second contexts. Both focused regressions passed ten times.
- Final Codex review: task-session HTTP and WebSocket lists fail closed when authoritative pending-action
projection fails. Desktop and mobile async selection also remain inert when the selected task
projection disappears, avoiding navigation to a deleted task. The dual-transport handler regression,
57 focused web tests, zero-warning focused web lint, typecheck, i18n ratchet, and changed-code Go lint
passed.
- Claude review remediation removes a dead clarification lookup, narrows handler and cancellation
repository interfaces, keeps caller metadata unchanged across any partial completion failure, and
documents the transaction-local claim timestamp. Durable-turn ordering coverage now includes the
created-time tie-break alongside existing load-state, start-time, ID, and nanosecond cases.
- Final Codex review returns restored clarification rows through the repository/service boundary and
publishes their pending state after commit, closing the interleaved task-summary refresh race. The
durable selection overview now matches task-only fallback for changed summaries and inert handling
only when the task projection disappears. Broader clarification-focused handler, repository, and
orchestrator tests passed, followed by zero-issue changed-code Go lint.
- CodeRabbit and Claude follow-up align recovery's attempted-flag decoder with tolerant SQL semantics
and pin the generationless-ready drop after a reservation rollback. Boolean, string, and numeric flag
encodings plus adjacent recovery/ready races passed ten focused repetitions; broader recovery and
ready suites passed, followed by zero-issue changed-code Go lint.
- Latest review remediation bounds synchronous detached resume, acknowledges restored pending state
through the live projector before promising retryability, removes wall-clock polling from repository
timestamp tests through an injected clock, and clarifies why every successor-turn message shares the
PostgreSQL session lock.
- New bot-review remediation uses bounded detached rollback after admission-time context cancellation,
normalizes pending/attempted flag predicates across SQLite and PostgreSQL, surfaces exhausted pending
summary CAS retries, and makes atomic clarification bundle operations a compile-time repository
contract. Focused race checks and all six affected backend package suites passed; the environment-gated
PostgreSQL predicate case skipped locally and remains enabled in CI.
- Follow-up review bounds terminal bundle publication with the same fresh persistence context. The
cancelled-caller regression, full clarification suite, and focused lint passed.
- Latest Codex review makes superseded forced-load aborts inert across desktop and phone and records
initial task-projection presence so deletion also invalidates legacy pending-action selections. This
changes shared selection state only, not mobile composition or touch behavior; focused desktop/mobile
unit coverage satisfies mobile parity without another Playwright case.
- Four focused task-selection suites passed 61 tests; full web lint, typecheck, and the i18n ratchet
passed.
- Late Claude review normalizes the detached-message guard across boolean, numeric, and string truthy
encodings. Restored clarification rows now publish even when synchronous summary acknowledgement is
unavailable or fails, while the acknowledgement error still reaches the caller.
- Full SQLite repository and task-service suites passed in 49s and 129s; changed-code Go lint reported
zero issues.
- Later Codex/Claude review rehydrates keyed Git observations after a nil-aggregate restart and rejects
malformed clarification recovery message-ID arrays instead of silently restoring a subset. Review
wording now distinguishes hidden unattempted reservations from authoritative attempted or
message-backed turns and preserves terminal clarification siblings during rejection.
- Full status-summary and SQLite repository suites passed; changed-code Go lint reported zero issues.
- Final review follow-up applies the same nil-aggregate restart and CAS-rebase guarantee to keyed pull
requests, makes unpublished-turn startup recovery a compile-time turn-repository requirement, and
adds PostgreSQL coverage for string truthy detached flags. Go RFC3339 parsing coverage confirms the
fixed nanosecond wire format remains compatible with existing Go consumers.
- The focused regressions and full status-summary, SQLite repository, task-service, and backend-app
suites passed; changed-code Go lint reported zero issues. The PostgreSQL case skipped locally without
`KANDEV_TEST_POSTGRES_DSN` and remains enabled in PostgreSQL CI.
- Final bot-review remediation rehydrates configured source observations before the first summary row,
keeps a durably restored clarification retryable when live convergence fails, and replaces mobile's
module-global selection sequence with one controller per mounted sheet. Focused Go suites, 42 web
selection tests, typecheck, zero-warning focused lint, and the i18n ratchet passed.
- Latest Codex review fails orchestrator startup when its turn service is absent instead of silently
bypassing reservation recovery, and aligns the remaining stale-summary scenario with task-only phone
navigation. The focused startup regressions and full orchestrator suite passed.
- Follow-up Codex review invalidates the phone task-sheet selection controller on unmount so a deferred
load cannot mutate or close a replacement tablet sheet. The 48-test focused selection run passed.
- Fresh Claude review wires the existing expiry path into every accepted terminal-session transition.
Fresh Codex review propagates status-summary repair failures and removes each failed stale summary from
task-list and boot responses so their authoritative coarse pending action remains visible. Focused
regressions, all five affected backend suites, and changed-code lint passed.
- Managed production E2E: Chromium 3/3 plus detached recovery 1/1; mobile Chrome 12/12.
- Current-head review remediation keeps unpublished attempted reservations out of client turn history
and propagates watchdog cancellation through fallback database and prompt work. Focused SQLite and
orchestrator regressions passed; PostgreSQL parity remains environment-gated in CI.
- Follow-up review adds scheduled positive and superseded watchdog coverage and makes primary and
detached clarification delivery share one claimed-bundle turn-identity rule.
- Late CodeRabbit review restores the planned deterministic tie-breaks to ascending turn-history reads
and adds same-timestamp batch-list coverage.
- Latest CodeRabbit review passes terminal session state through every loaded-message pending-input
selector, so stale current-turn clarification rows cannot re-arm task or session UI after completion
or cancellation. The 55 focused web tests, typecheck, zero-warning focused lint, and i18n ratchet
passed.
- Fresh Claude review makes cancellation and live response delivery choose one winner under the pending
entry lock. A cancellation that wins after response lookup now returns not-found immediately, while
an already-resolved response keeps its durable confirmation path. The deleted-task mobile selection
comment was rejected because the reviewed spec deliberately keeps that stale continuation inert. The
focused races passed ten repetitions, the full clarification race suite passed, and changed-code Go
lint reported zero issues.
- Fresh Codex review makes an explicit clean or permission session projection outrank filtered visible
turn history. This keeps a predecessor clarification inert while an authoritative attempted successor
is intentionally unpublished from client history, across both chat discovery and pending indicators.
The 108 focused web tests, typecheck, zero-warning focused lint, and i18n ratchet passed.
- Follow-up Codex review adds authoritative per-session `pending_action` to semantic message events and
narrowly mirrors it into loaded by-ID and per-task session projections. A newly streamed question now
advances a cached clean projection, while terminal and successor mutations clear it without waiting
for a session-list refresh. Event-arrival ordering stays authoritative across batched message updates.
The 73 focused web tests, full task-service suite, targeted race suite (10 repetitions), web typecheck,
zero-warning focused lint, i18n ratchet, backend changed-lines lint, and public-doc validators passed.
- A second Codex pass orders REST and WebSocket pending-action snapshots with a shared process-epoch
logical revision reserved before each authoritative read. Store merges now reject older projections,
including a deferred task-session list response that resolves after a newer message event.
- Follow-up Claude review separates degraded turn-service warnings from graceful missing-identity debug
logs, rejects partial clarification response-delivery intents, and adds context-aware 1 ms / 2 ms
backoff between startup summary compare-and-set retries.
- Follow-up CodeRabbit review keeps the independent response-delivery recovery pass running after a
malformed reservation while excluding delivery IDs still owned by unresolved prompt reservations.
Detached-bundle recovery coverage also confirms the disconnect marker survives restoration.
- An intermediate exact-head Codex review replaced timestamp generations with opaque UUID epochs and
a bounded client retirement list. A later review exposed the remaining client-reload hole, so the
final design uses a database-backed monotonic generation and numeric client comparison instead.
- Exact-head CodeRabbit review bounds a responder waiting for an already-started durable confirmation
while leaving the waiter to retain its eventual result, and consolidates HTTP and WebSocket session
summary projections behind one helper.
- Latest exact-head Codex review rejects unseen stale epochs after client state rebuild and routes
canceller-originated message updates through the task service's projection-aware publisher. The
former uses an atomic `kandev_meta` generation allocated at backend startup; the latter covers both
expired bundles and first-observed detachment.
- This remediation batch passed all affected backend package suites (backend app, clarification,
MCP handlers, orchestrator, task handlers, SQLite repository, and task service), focused race
tests with three final repetitions, 39 focused web tests, the superseded-clarification E2E
regression, web typecheck, zero-warning frontend and Go changed-lines lint, format and i18n
checks, and both public-doc validators.
- The final review follow-up passed all affected backend suites again, both projection-focused web
suites (21 tests), web typecheck, zero-warning frontend and Go changed-lines lint, i18n checks and
ratchet, and the public-doc validator's 61 tests plus 41-page scan.
- Latest Claude review bounds the sidebar's message-only clarification fallback to the newest visible
turn. A second suggestion was already covered by `clearPendingLocked`; explicit restart coverage now
proves stale dismissal clears and persists an aggregate-only restored pending action. The three
affected web suites (110 tests) and full status-summary package pass.
- Exact-head Codex review makes that sidebar fallback use durable message creation order rather than
WebSocket arrival order. A delayed predecessor-turn frame can no longer hide the active current-turn
clarification; the five affected web suites (127 tests), typecheck, and zero-warning focused lint pass.
- Exact-head CodeRabbit review makes clarification restore contingent on the still-live durable delivery
marker, so a late successful confirmation cannot be reopened after the responder times out. It also
keeps aborted sessionless mobile selection inert, guarantees blocked test cleanup, and covers deleted
message pending-action projections. All affected backend packages, 81 focused web tests, typecheck,
zero-warning frontend lint, and changed-code Go lint pass.
- Exact-head Claude review replaces a watchdog test's hot polling loop with a bounded eventual assertion
and documents why the legacy event path intentionally relies on the repository's current-turn SQL
authority guard. Both focused orchestrator regressions pass.
- The next Claude and CodeRabbit pass keeps a stale-dismiss event from clearing a different pending
identity in event-only projector mode and makes an active-turn lookup error outrank any partial turn
returned alongside it. Both review regressions pass.
- The latest review pass bounds terminal clarification expiry before state publication, patches active
turn metadata under session authority, exposes mixed-turn bundle corruption in warning logs, and
makes blocking test cleanup failure-safe. PostgreSQL-gated epoch coverage now checks monotonic and
corrupt-value behavior. All affected backend suites and changed-code lint pass; the PostgreSQL case
is locally skipped when `KANDEV_TEST_POSTGRES_DSN` is unavailable.
- Exact-head Greptile and Codex follow-up closes the remaining writer and deadline gaps: full turn
snapshots now lock and reject stale versions, active metadata patches return their committed state
without a fallible post-commit read, and terminal expiry preserves the orchestrator's shorter deadline
through the cancellation-detached persistence layer. Cross-dialect lock coverage is PostgreSQL-gated;
all locally runnable affected suites pass.
- The accompanying CodeRabbit pass makes reserved-turn waiter cleanup immediate and idempotent in five
agent-ready regressions. A later exact-head summary regression enforces the feature spec by invalidating
a known-stale status-summary row when pending-action CAS repair exhausts its retries, preserving the
authoritative coarse fallback. A later exact-head authority regression supersedes the delayed suggestion
to restrict add/delete events: all message additions and deletions refresh pending action because they
can add or remove the evidence that makes a reserved successor authoritative; only ordinary content
updates omit the projection.
- A final Codex race follow-up routes late prompt-usage and agent-identity fields through an atomic
metadata patch that works after reservation publication and turn completion. The stale full-snapshot
CAS remains as a clobber guard, while publication-winning usage metadata now persists on retry-free
merge semantics. Full SQLite repository, task-service, and orchestrator suites pass.
- Exact-head Codex and Greptile follow-up preserves both client and restart convergence. Ordinary message
add/delete events now carry the authoritative pending-action projection when message evidence changes
turn authority. Reserved-turn publication keeps recovery markers durable until `turn.started` is
accepted, surfaces bus failure, then clears only reservation metadata under session authority even if
a fast provider completed the turn during publication. Focused regressions and the full task,
integration, backend-composition, and orchestrator suites pass.
- Delayed CodeRabbit and Claude follow-up makes anomalous nil session loads fail turn reservation without
a panic, documents all three pending-clarification scope states (including the empty-object trap), and
emits a structured warning with task, attempt count, and last revision when summary reconciliation
exhausts its compare-and-set retry budget. Focused backend tests and focused frontend formatting/lint
pass.
- Exact-head Codex follow-up sanitizes private prompt-dispatch recovery fields from every public turn
event. The concurrent publication regression now completes through the task service and proves a fast
`turn.completed` payload cannot retain the pre-cleanup metadata.
- A further exact-head Codex regression marks the known-stale summary invalid in partial boot/task-list
results when all pending-action compare-and-set retries lose. The authoritative coarse task fallback
remains visible instead of being shadowed by the stale summary.
- Exact-head Greptile review adds a durable startup event outbox for accepted and ambiguous prompt
reservations. Recovery replaces private reservation fields with a start-event marker, replays
`turn.started` and any already-required `turn.completed` event in order, and clears the marker only
after successful publication. Failed replay now fails startup and remains retryable across restarts.
- Replay regressions, the full task subtree, backend composition, integration, orchestrator, executor,
and changed-code Go lint pass. PostgreSQL parity remains CI-gated when no local DSN is configured.
- Delayed Codex and Claude review makes summary invalidation explicit across the Go DTO and shared
desktop/mobile snapshot merge while protecting a newer in-flight WebSocket revision. It also caps
post-deadline delivery-confirmation waiting at five minutes, treats partial clarification recovery
metadata as a startup error, suppresses expected cancellation warnings, documents the duplicated SQL
bind, and pins numeric epoch ordering across `"9"` to `"10"`.
- All affected Go package suites, 31 focused web tests, frontend typecheck, zero-warning frontend and
changed-code Go lint, and the i18n ratchet pass.
- Latest Claude and CodeRabbit follow-up reports corrupt persisted projection epochs with an actionable
metadata diagnostic and makes a response claim immutable across an outliving confirmation callback.
Callback output remains locally owned, while durable finalization and recovery continue to serialize.
- Focused confirmation, immutable-snapshot, projection-epoch, and race-enabled regressions pass, along
with the full clarification and SQLite repository suites.
- `git diff --check` passed. All runners used isolated test state and exited cleanly.
---
## Implementation Waves And Parallel Candidates
Execution remains sequential in the primary conversation.
Wave 1:
- [x] [task-01-clarification-regression-red](task-01-clarification-regression-red.md)
Wave 2:
- [x] [task-02-current-turn-backend-authority](task-02-current-turn-backend-authority.md)
Wave 3:
- [x] [task-03-summary-pending-convergence](task-03-summary-pending-convergence.md)
Wave 4:
- [x] [task-04-pending-owner-navigation](task-04-pending-owner-navigation.md)
Wave 5:
- [x] [task-05-clarification-regression-green](task-05-clarification-regression-green.md)
No task is marked parallel-safe. Tasks 02 and 03 share pending repository contracts; tasks 03 and 04
share projection semantics; task 05 spans all layers. Waves do not authorize subagents.
---
## Risks
- Durable-turn ordering must agree across dialects. Use the same `started_at`, `created_at`, and `id`
ordering in scalar and batch queries, then run the env-gated PostgreSQL behavior test.
- Clearing on any ordinary message without checking its turn would recreate event-order bugs. The
repository refresh, not message type, decides whether pending state changed.
- Read-time summary repair can race the live projector. Bounded CAS reload/retry and projector
resynchronization must preserve newer unrelated summary fields.
- A stale tab can submit after another tab starts a new turn. The response guard must run before any
fallback persistence or resume request.
- Pending task selection adds an async list request to a formerly synchronous fast path. Existing
selection tokens, external-navigation guard, layout cleanup, URL ordering, and phone drawer failure
fallback must remain intact.
- Historical rows remain `pending` in raw metadata. Any future consumer must use the active repository
projection rather than inventing another all-history scan.
## Out of scope
- Direct cleanup/backfill of the main instance database.
- Schema changes or unrelated task-summary wire-shape changes.
- Clarification UI redesign or new user-facing copy.
- Historical notification retraction.
- Permission lifecycle changes beyond shared pending-owner navigation.