feat(store): add TaskCorrected compensating-event type (S8)

Implements §3.1's invariant that a wrong event is never edited, only
compensated for by a new appended event. TaskCorrected references the
event it repairs and can change state and/or amend-style fields;
Store.Append verifies the referenced event actually exists on the task.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1rkJ2hBMybnJctPbcy4tT
This commit is contained in:
kami
2026-07-27 23:48:19 +04:00
parent 86cc0b9276
commit e363a77ae9
5 changed files with 151 additions and 2 deletions
+28
View File
@@ -947,6 +947,34 @@ a source of transcript/tool-call data this repo doesn't have yet.
`go build ./...`, `go vet ./...`, `go test ./...` all pass.
## S8 — closed, 2026-07-27
No compensation-event mechanism existed — §3.1's own invariant ("a wrong
event is never edited; a compensating event is appended and replay sees
both") had nothing implementing it. `TaskAmended` was the closest analog but
only merges metadata fields forward with no reference to what it's
correcting and no way to touch `State`.
Added a new event type, `TaskCorrected`, generalizing that gap rather than
special-casing it: payload requires `corrects` (the `id` of the event being
repaired) plus at least one field to change — `state` (validated against the
same enum as `domain.TaskState`) and/or the existing amend-style fields
(`title`/`description`/`inherent_priority`/`due`). `domain.ValidatePayload`
checks shape; `Store.Append` checks that `corrects` actually names an event
belonging to the same task in the log (returning `ErrInvalid` otherwise) —
existence can only be checked where the log is visible, not in the
shape-only validator. `store.apply`'s new `TaskCorrected` branch clears
`Lease` whenever the corrected state isn't `leased`, matching every other
terminal-state branch. No new authz surface rule was needed — it slots into
the existing `TaskAmended`-shaped FullControl/GatedWrite policy unchanged.
Covered by `TestTaskCorrected` (`internal/store/store_test.go`): a mistaken
`TaskFailed` is reverted to `queued` by an appended `TaskCorrected`
referencing it, a correction naming an unknown/foreign event is rejected,
and both the original wrong event and its correction survive a full
snapshot+replay reopen (the log is never edited, only appended to). `go
build ./...`, `go vet ./...`, `go test ./...` all pass.
### Design consequences (not yet implemented)
1. **Percentages are a level, not a delta.**