feat(api): undo an agent write by proposing its inverse (ADR-0031) #354
Labels
No labels
area:auth
area:ci
area:db
area:infra
area:native
area:pwa
area:service
epic
feature
foundation
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
james/carol#354
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Second half of epic #47's "audit log + undo": the backend to reverse an audited agent write. Design is ADR-0031 (this PR adds it). The activity-screen undo button is a follow-up.
Design (ADR-0031, in this PR)
An undo is not a new mutator path — it loads an audit event, computes the inverse tool + input, and runs that tool's
execute, which persists a normalagent_proposalsrow. The client confirms it through the existing proposal read (#342) + commit/reject (#51) flow. So an undo is itself confirmed, audited, and undoable, and gets conflict re-validation for free at commit time.Inverse keyed by
entity_type+ the originalactionclass (the tool name isn't a cleanverb_noun—add_note_to_person→ entityTypeperson_note):safe_create→delete_<entityType>({ id })safe_update/destructive_update→update_<entityType>({ id, ...changed fields restored to before })destructive_delete→ not undoable (the snapshot doesn't capture cascading children, and the original id can't be restored through create) → 409not_undoableScope
docs/adr/0031-agent-write-undo.md.POST /api/agent/audit/{id}/undo→ builds + persists the inverse proposal, returns it asProposalDto(the shape the confirmation UI already consumes). Per-user 404; 409not_undoablefor deletes / missing inverse tool.lib/agent/undo.ts(buildUndoProposal),AuditEventsRepository.findById.Out of scope (follow-ups)
Part of epic #47.