Co-authored-by: nova28 <17953305+nova28@users.noreply.github.com> Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
3.9 KiB
id, title, status, wave, depends_on, plan, spec
| id | title | status | wave | depends_on | plan | spec | |
|---|---|---|---|---|---|---|---|
| 03-service-writer | Service writer and expvar counters | pending | 3 |
|
plan.md | ../../specs/subagent-context-persistence/spec.md |
Amendment 1 update: the writer's request/identity-gate shape carries an
ExecutionIDand the attempted/anomalous counters aggregate as documented in the "Amendment 1 update" note near the top ofplan.md— read that note for the current writer-health invariants before implementing from this file.
Task 03: Service writer and expvar counters
Add the single service entry point that owns identity gating, value normalization, terminality, and the five writer-health counters.
Acceptance
RecordSubagentContexthas no error return. A repository failure logsWARNwith the session id and tool call id, incrementsfailed, and returns normally — so it is structurally impossible for this writer to fail the enclosing message write, turn, or agent stream. (AC-27)- Normalization is exact: every empty nullable TEXT field becomes NULL, never
''; an unreportedtotal_tokens/tool_use_count/duration_msbecomes NULL, never0; a reportedtool_use_countof0stays0; a negative value becomes NULL and incrementsanomalous_valuewhile leaving the frame's other fields intact. (AC-7, AC-8, AC-9) settled_atis set only from a terminal ACPtool_status.agent_statusis stored verbatim and never consulted for terminality — includingasync_launchedand any value Kandev does not recognise. (AC-10a, AC-11)expvarexposesattempted,persisted,skipped_no_identity,anomalous_value,failed. Missing session id, task id, or tool call id writes nothing and incrementsskipped_no_identity. (AC-2, AC-26)
Files likely touched
apps/backend/internal/task/service/subagent_context.go— new:RecordSubagentContextRequest,RecordSubagentContext,nilIfEmpty, the metric normalizer, and a localisTerminalToolStatus.apps/backend/internal/task/service/subagent_context_metrics.go— new: oneexpvar.NewMap("subagent_context_total").apps/backend/internal/task/service/subagent_context_test.go— new.
Dependencies
Task 02 — the repository upsert is the collaborator under test.
Parallelism
sequential.
Inputs
- Plan § Service writer and counters (task 03) — carries the request struct, the no-error-return rationale, and the counter names.
- Spec § Column-level normalization rules, § Two statuses, deliberately separate, AC-2, AC-7 through AC-11, AC-26, AC-27.
streams.SubagentTaskPayloadatinternal/agentctl/types/streams/tool_payload.go— noteToolUseCount *intis a pointer specifically to carry a reported0, and thatDurationMs/TotalTokensareomitempty int64so a reported0is indistinguishable from absent for those two. Preserve that asymmetry rather than smoothing it.internal/orchestrator/event_handlers_streaming.go:762— the peerisTerminalToolStatus. It is unexported, so copy the set (complete,completed,success,error,failed,cancelled), name the peer in a comment, and add a test that pins the two lists together so a future edit to one fails.- Counter convention:
internal/office/scheduler/metrics_vars.go(package-levelexpvar.NewMap, counters only) and its testmetrics_vars_test.gofor the name-assertion shape.
Verification
cd apps/backend && gofmt -l internal/task/service && make lint && go test -run 'TestRecordSubagentContext|TestSubagentContext' ./internal/task/service/... && go test ./internal/task/service/...
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.