Implements non-negotiable #1 — the AI assistant never writes autonomously. Design note: docs/design/change-proposal.md.
Structural guarantee: a proposal's ops reach the DB only via apply(), which requires the actor be an editor and dispatches each op through the normal editing services (person/name/event/relationship/source/citation) — so every change hits the privacy engine and is audited as the approving human. propose() only inserts a pending row. Model providers stay read-only.
ChangeProposal model + migration (status, origin, JSONB operations, reviewer, apply_error).
Service: propose/list/get/apply(+edits)/reject/delete + op→service dispatcher. v1 applies ops in order (single-op atomic; cross-op not yet transactional — documented).
/trees/{id}/proposals API + a review page (editor-gated approve/reject) + sidebar entry.
Implements non-negotiable #1 — the AI assistant never writes autonomously. Design note: `docs/design/change-proposal.md`.
**Structural guarantee:** a proposal's ops reach the DB **only** via `apply()`, which requires the actor be an editor and dispatches each op through the normal editing services (person/name/event/relationship/source/citation) — so every change hits the privacy engine and is audited as the approving human. `propose()` only inserts a pending row. Model providers stay read-only.
- `ChangeProposal` model + migration (status, origin, JSONB operations, reviewer, apply_error).
- Service: propose/list/get/apply(+edits)/reject/delete + op→service dispatcher. v1 applies ops in order (single-op atomic; cross-op not yet transactional — documented).
- `/trees/{id}/proposals` API + a review page (editor-gated approve/reject) + sidebar entry.
Tests: not-applied-until-approved, reject, non-editor-can-see-not-apply, multi-op, approve-with-edits, apply-error-keeps-pending. **87 passed**, single alembic head.
Closes #214
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Implements non-negotiable #1: the AI assistant never writes autonomously. Every
assistant/contributor "write" emits a ChangeProposal — a structured diff a human
approves, edits, or rejects. Design: docs/design/change-proposal.md.
Structural guarantee: a proposal's operations reach the DB ONLY via
change_proposal_service.apply(), which requires the actor be an editor and
dispatches each op through the normal editing services (person/name/event/
relationship/source/citation create/update/delete) — so every change passes the
privacy engine and is audited as the approving human. propose() only inserts a
pending row; it performs no domain mutation. Model providers stay read-only, so
no model response can mutate tree data.
- ChangeProposal model + migration (status pending|applied|rejected, origin
assistant|contributor, JSONB operations, reviewer + apply_error).
- Service: propose / list / get / apply (with optional edited ops) / reject /
delete; a dispatcher mapping ops → editing services. v1 applies ops in order,
not cross-op transactional (single-op is atomic; documented).
- API /trees/{id}/proposals + a frontend review page (approve/reject; editor-
gated) and sidebar entry.
Tests: proposal doesn't apply until approved; reject doesn't apply; non-editor
member can see but not apply; multi-op; approve-with-edits; apply-error keeps it
pending. Full suite 87 passed; single alembic head.
Closes#214
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Signed-off-by: Justin Paul <[email protected]>
justin
merged commit 9187c0a791 into main2026-06-09 15:44:42 -04:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements non-negotiable #1 — the AI assistant never writes autonomously. Design note:
docs/design/change-proposal.md.Structural guarantee: a proposal's ops reach the DB only via
apply(), which requires the actor be an editor and dispatches each op through the normal editing services (person/name/event/relationship/source/citation) — so every change hits the privacy engine and is audited as the approving human.propose()only inserts a pending row. Model providers stay read-only.ChangeProposalmodel + migration (status, origin, JSONB operations, reviewer, apply_error)./trees/{id}/proposalsAPI + a review page (editor-gated approve/reject) + sidebar entry.Tests: not-applied-until-approved, reject, non-editor-can-see-not-apply, multi-op, approve-with-edits, apply-error-keeps-pending. 87 passed, single alembic head.
Closes #214
🤖 Generated with Claude Code