4.5 KiB
id, title, status, wave, depends_on, plan, spec
| id | title | status | wave | depends_on | plan | spec | |
|---|---|---|---|---|---|---|---|
| 02-bundle-queries | Durable clarification bundle read queries | done | 2 |
|
plan.md | ../../specs/integrations/requirements/external-question-answering.md |
Task 02: Durable clarification bundle read queries
Read bundles from the durable clarification_request messages rather than the in-memory store, so
the list tool is correct after a restart and the resolver can resolve a bundle's identity. Both
queries treat "unresolved" as "has no clarification_resolutions row" (D4).
-
Acceptance:
ListUnresolvedClarificationBundlesreturns bundles ordered byMIN(created_at)thenpending_id, excludes any bundle with a resolution row regardless of message statuses, and includes a bundle whose per-question messages disagree on status.- Owner scoping happens inside the query: a scoped caller never sees a bundle whose task's
workspace has a different
owner_id, and still sees bundles whose task has an emptyworkspace_id(D5). A page is never short because rows were filtered afterLIMIT. - A cursor returns only bundles strictly after it under the L6 order;
updated_sinceandcursorintersect (L14);limitclamps to 1..200 with a default of 50 (L10). GetClarificationBundlereturnstask_id,session_id, and questions ordered byquestion_index, degrading a question with unparseablequestionmetadata to itsquestion_idwith empty prompt/options rather than dropping the bundle (L15).
-
Verification:
cd apps/backend && \ gofmt -l internal/task/repository/sqlite internal/task/models && \ go test -race ./internal/task/repository/sqlite/... && \ go build ./...Postgres coverage when a DSN is available:
cd apps/backend && KANDEV_TEST_POSTGRES_DSN="$KANDEV_TEST_POSTGRES_DSN" \ go test -race -run 'ClarificationBundle' ./internal/task/repository/sqlite/... -
Files likely touched:
apps/backend/internal/task/repository/sqlite/clarification_bundle.go(new)apps/backend/internal/task/models/clarification_resolution.go(addClarificationBundle,ClarificationBundleQuestion,ClarificationBundleFilter)apps/backend/internal/task/repository/interface.goapps/backend/internal/task/repository/sqlite/clarification_bundle_test.go(new)
-
Dependencies: 01 — the
NOT EXISTSsubquery needs the table. -
Parallelism:
sequential. -
Inputs:
- Spec L1–L15, D1, D3, D4, D5, D6, D7.
- Plan § Backend → Model and repository for the query shape and the keyset-cursor predicate.
- Existing queries to mirror:
FindMessagesByPendingID(sqlite/message.go:412) andFindPendingClarificationMessagesBySessionID(:432) for thedialect.JSONExtractidiom andscanMessageRows;pendingActionsBySessionQueryfor a driver-parameterized query builder. age_seconds(L3) and RFC3339 formatting are the caller's job, not the repository's — the repository returnstime.Time. D7's floor-at-0 belongs with the MCP handler in task 05.- Do not filter on
metadata.statusalone; it is a pre-filter at most (L2).
-
Output contract: summary, files changed, tests run with counts, blockers, risks, and the task/plan status update in the same conversation.
Results
Implemented across commits 8bccf9298, 842853393, 324677546. Added
internal/task/repository/sqlite/clarification_bundle.go with
ListUnresolvedClarificationBundles (owner-scoped NOT EXISTS against
clarification_resolutions, keyset cursor on (bundle_created_at, pending_id), updated_since
intersection, limit clamped 1..200 default 50) and GetClarificationBundle (per-question rows
ordered by question_index, degrading unparseable question metadata to id-only rather than
dropping the bundle), plus the ClarificationBundle* model types and the third
ensureMessageMetadataIndexes index.
Verified in this session's final gauntlet (Wave 10): go test ./internal/task/repository/sqlite/...
passes, including clarification_bundle_test.go (ordering, owner scoping never seeing empty-scope
rows, page-not-short-after-LIMIT, cursor exclusivity, updated_since/cursor intersection, mixed
per-question status, degraded-question case). gofmt -l reports no files; go build ./... succeeds.
Postgres-DSN coverage was not independently re-run in this session (no KANDEV_TEST_POSTGRES_DSN
available in this environment); the required SQLite path is green.
No external side effects.