3.6 KiB
id, title, status, wave, depends_on, plan, spec
| id | title | status | wave | depends_on | plan | spec | |
|---|---|---|---|---|---|---|---|
| 02-repository-upsert | Model and repository upsert | pending | 2 |
|
plan.md | ../../specs/agents/requirements/subagent-context-persistence.md |
Amendment 1 update: the shipped conflict key is
(task_session_id, agent_execution_id, tool_call_id), andSubagentContextcarriesAgentExecutionID— see the "Amendment 1 update" note near the top ofplan.md.
Task 02: Model and repository upsert
Add the SubagentContext model and the single atomic upsert that encodes every
merge, terminality, and ordering rule in SQL.
Acceptance
UpsertSubagentContextis oneINSERT ... ON CONFLICT DO UPDATEstatement with no read-then-write anywhere. Repeated calls for one(task_session_id, tool_call_id)produce exactly one row; concurrent calls for the same key and for different keys in one turn both hold. (AC-3, AC-14, AC-15)- The conflict clause satisfies fill-forward (AC-4, AC-5), write-once
settled_at(AC-11), post-settle immutability oftool_statuswith continued NULL-filling (AC-12), original-turn_idpreservation (AC-2a), stickyis_async, and write-oncesource/observed_at(AC-1a, AC-22). - Reads are ordered
observed_at ASC, tool_call_id ASC, and deleting the parent session cascades the rows away. (AC-13, AC-16)
Files likely touched
apps/backend/internal/task/models/subagent_context.go— new. Every nullable column is a pointer;ToolUseCount *int64specifically so a reported0is distinguishable from "not reported".apps/backend/internal/task/repository/sqlite/subagent_context.go— new:UpsertSubagentContext,ListSubagentContextsBySession,ListSubagentContextsByTurn.apps/backend/internal/task/repository/sqlite/subagent_context_test.go— new.apps/backend/internal/task/repository/sqlite/workspace_test.go— addtask_session_subagentstoassertNoWorkspaceCascadeDependents(line ~317).- The task repository interface in
apps/backend/internal/task/repository/, if the service consumes the repo through an interface rather than the concrete type — check before assuming either way.
Dependencies
Task 01 — the table must exist.
Parallelism
sequential — same package as task 01.
Inputs
- Plan § Model and repository upsert (task 02) — carries the full column-by-column conflict-expression table with its AC mapping. Implement from that table; do not re-derive it.
- Spec § Data model, AC-2a, AC-3 through AC-6, AC-10 through AC-16.
- Upsert patterns already in the package:
review.go:27,document.go:291,executor.go:171. Build the statement as a package-levelconstand pass it throughr.db.Rebind. git_snapshots.go— the sibling read/scan shape for a session-scoped child table.- Concurrency test shape:
messagequeue/repository_reorder_test.go:212.
Verification
cd apps/backend && gofmt -l internal/task/models internal/task/repository/sqlite && make lint && go test -run 'TestSubagentContext' ./internal/task/repository/sqlite/... && go test -race -run 'TestSubagentContext' ./internal/task/repository/sqlite/... && go test ./internal/task/repository/sqlite/...
The -race run is not optional: AC-14 and AC-15 are concurrency claims and a
non-race pass is not evidence for them.
Results
Pending. Before marking the task done, replace this with every exact command
actually run and its outcome/count, generated artifact paths, and cleanup or
teardown evidence. Record security/trust and external side-effect boundaries
when applicable, or explicitly state None.