7.2 KiB
id, title, status, wave, depends_on, plan, requirements, acceptance_criteria, system_design
| id | title | status | wave | depends_on | plan | requirements | acceptance_criteria | system_design | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-align-semantic-equality | Align semantic equality and prove persistence behavior | done | 1 | plan.md |
|
|
|
Task 01: Align semantic equality and prove persistence behavior
Summary
Make valid summary equality agree with persisted canonical JSON. Prove that a PR detail change with an unchanged aggregate and a retained session error is a successful no-op, while a later real change still persists and publishes.
In scope
- Add model and real-SQLite regression tests before production edits.
- Replace representation-sensitive equality with successful canonical-payload comparison, preserving the existing validation/error path.
- Match the test store's rejection semantics to the production repository.
- Record red/green command results and update the plan/work-order status.
Out of scope
All exclusions in the plan, especially repository SQL changes, global normalization changes, UI work, log-level suppression, and retry tuning.
Acceptance
- Every tested valid summary compares equal to its canonical JSON round trip, including nil/empty error actions and causes. Metadata-only differences are equal; real semantic differences are unequal. Failed encodings are never accepted as equality.
- Against real SQLite, both live and recreated projectors process an aggregate-preserving PR change with no handler error, exhaustion warning, revision change, or extra summary publication. Preserve unrelated fields.
- A subsequent true aggregate change advances exactly one revision and publishes exactly one complete replacement with task/workspace identity. Existing store-error and contention behavior stays observable and bounded.
TDD sequence and regression construction
- Mark this work order
in_progress. AddTestTaskStatusSummaryProjectorPRNoopWithRetainedErrorusingnewTaskStatusSummaryTestRepoandseedTaskForStatusSummary. - Configure
LoadSessionObservationswith one primary waiting session,ErrorsObserved: true, and an error withScope: "session", matchingSessionID, fixed UTCOccurredAt, stamp, and preview. Leave optional collections absent so production normalization creates the relevant shape. - Call
Projector.HandleEventwithGitHubTaskPRUpdated, fixed task/workspace and repository identity, one open PR,checks_state: "failure", andpending_review_count: 1. Load the first summary, then send count 2. The unchangedfailureaggregate must succeed; current code instead returnsexhausted CAS retries ... after 3 attempts. Assert payload and revision as well as event counts. Capture the projector logger for the warning check. - Include a recreated-projector case using the same temporary repository and authoritative session/PR loaders. Keep loader keys aligned with event keys. Register bus/logger/database cleanup immediately. No sleeps are needed.
- Add model cases named in the plan. Independently exercise empty actions and
empty causes in both
ActiveErrorandTaskError, plus valid populated changes, removal, queue count, Git/PR state, and last activity changes. - Implement canonical equality and faithful fake-store semantics. Never
return equality when either
SemanticJSONcall fails. Do not swallow CAS exhaustion or increase its attempt count. - In
TestTaskStatusSummaryProjectorPRNoopWithRetainedError, after the no-op cases, change aggregate status to a distinct state, assert one write/event, then replay and assert no extra event. Verify the retained error and complete replacement survive. Run the targeted packages and record results.
Verification
Run from the repository root. The first command is the red test before the production edit and must be rerun green afterward. The second covers every changed suite and the existing contention regressions.
(cd apps/backend && go test ./internal/task/repository/sqlite -run '^TestTaskStatusSummaryProjectorPRNoopWithRetainedError$' -count=1 -v)
(cd apps/backend && go test -race ./internal/task/statussummary ./internal/task/repository/sqlite -count=1)
python3 scripts/list-docs.py validate
python3 scripts/lint-spec-files.py --all
git diff --check
git status --short -- docs/plans/task-summary-semantic-equality
Do not claim PostgreSQL was exercised by the SQLite suite. There is no SQL or dialect change in this scope; if one becomes necessary, reconcile the package and add the required real PostgreSQL check before implementing it.
Files likely touched
apps/backend/internal/task/statussummary/model.goapps/backend/internal/task/statussummary/model_semantic_equality_test.go(new)apps/backend/internal/task/statussummary/projector_test.go(test-store helper)apps/backend/internal/task/repository/sqlite/task_status_summary_projector_noop_test.go(new)- This work order and
plan.md(status and results).
Read-only integration points: projector.go, projector_events.go,
projector_derive.go, rebuild.go, and repository/sqlite/task_status_summary.go.
Dependencies
None.
Risks
Invalid data must reach existing validation rather than being hidden by failed serialization. The revised fake store can reveal tests that previously assumed every higher revision was accepted. Diagnose those assumptions rather than weakening the persistence contract.
Parallelism
sequential
Inputs
- Plan and reproduction evidence.
- Requirements, AC .2 and .3.
- Design, derivation and persistence guarantees.
- Existing
model_test.go,projector_pending_authority_test.go,projector_queued_test.go, andtask_status_summary_test.gopatterns. .agents/skills/tdd/references/backend-tests.md.
Results
Implemented on 2026-09-17.
TaskStatusSummary.SemanticEqualcompares successful canonical semantic JSON encodings and rejects invalid summaries as equal.- The projector test store now models both production rejection conditions: stale revisions and unchanged workspace/semantic payloads.
- Added model coverage for nil/empty omitted collections, metadata-only changes, real semantic changes, JSON round trips, and invalid values.
- Added a real SQLite projector regression covering live and recreated no-op PR updates and one true aggregate change.
Verification passed:
(cd apps/backend && go test ./internal/task/statussummary ./internal/task/repository/sqlite \
-run 'TestTaskStatusSummarySemanticEquality|TestTaskStatusSummaryProjectorPRNoopWithRetainedError' -count=1)
2 packages passed.
(cd apps/backend && go test -race ./internal/task/statussummary ./internal/task/repository/sqlite -count=1)
2 packages passed.
python3 scripts/list-docs.py validate
Validated 285 decisions and 986 specifications.
python3 scripts/lint-spec-files.py --all
All specification files passed.
git diff --check
passed.