Skip to content

Revision, retcon, and rollback

NovelGraph separates narrative surface from approved story state. A chapter edit changes the current chapter content, creates a row in revisions, and records an author event. A retcon changes an established fact or commitment and therefore needs an approval decision before it becomes canon. A rollback restores earlier chapter content; it does not erase the event history.

ChangeRequired authorityGate
Typo, wording, or local continuity repairAuthor editSave a revision, inspect the diff, rerun relevant audit
Outline or scene-plan changeAuthor decisionApproval kind outline-change, then rerun dependent work
Identity, death, major arc, or established fact changeAuthor decisionApproval kind character-major-change or canon-retcon, rationale, then re-audit
Research admissionAuthor decisionApproval kind research-admission; retain source and rationale
Hard-gate exceptionAuthor decisionApproval kind gate-waiver; closure records the waiver as a warning

Agents can propose a change through canon:propose or a review finding. They cannot silently promote the proposal. The core schema carries approval kinds, but the current Studio HTTP surface exposes approval resolution, not a generic approval-creation route. Do not describe a review-card click or an agent completion as approval unless the approval record exists.

The Studio editor sends a PATCH to /api/v1/chapters/:chapterId. The payload accepts an optional title, optional contentMarkdown, and a non-empty reason:

Terminal window
curl -X PATCH http://127.0.0.1:4567/api/v1/chapters/<chapter-id> \
-H 'content-type: application/json' \
-d '{"contentMarkdown":"Updated chapter text","reason":"Repair stopped-clock testimony"}'

The store operation is transactional: it saves the new content in revisions, updates the chapter, and records chapter.updated. GET /api/v1/books/<book-id> returns current chapters plus revision metadata (id, chapter, reason, actor, and timestamp). The current route does not return revision content.

After a canon-affecting change:

  1. Capture the current book and the approval rationale.
  2. Apply the approved chapter or graph change with a specific reason.
  3. Mark dependent audit results stale when the owning service supports invalidation; a mystery policy, solution, or typed mystery record change does this in MysteryEngine.
  4. Run validation and the relevant audit again.
  5. Resolve the originating finding only after the re-audit shows that the issue is gone.
  6. Check /api/v1/books/<book-id>/closure before export.

The core store has StudioStore.restoreRevision(revisionId). It updates the chapter from the selected revision and records revision.restored with actor author. It does not delete old rows. It also does not create a new revisions row, and the current HTTP server does not expose this method as a restore route.

Therefore the supported operational boundary today is:

  • use the editor or PATCH route to apply a known-good chapter body with a reason such as Rollback to revision <id>;
  • preserve the original database backup before any direct recovery work;
  • do not edit studio.sqlite by hand or invent /restore API calls;
  • rerun audits and closure after the rollback.

If the only copy of the desired old content is inside SQLite, stop Studio and use a read-only SQLite inspection tool to retrieve revisions.content_markdown. Keep the original .novelgraph/studio.sqlite unchanged, write the recovered text to a controlled local file, then apply it through the normal chapter update path. A database-level restore is a recovery operation, not a canon approval.

For an interrupted workflow, cancel the job and inspect its durable event log:

Terminal window
curl -X POST http://127.0.0.1:4567/api/v1/jobs/<job-id>/cancel
curl http://127.0.0.1:4567/api/v1/jobs/<job-id>

After resolving the external cause, resume only a blocked or failed job:

Terminal window
curl -X POST http://127.0.0.1:4567/api/v1/jobs/<job-id>/resume

Do not start a duplicate run with a new idempotency key merely because a worker stopped. Use a new key only when the prior run is intentionally abandoned and the new run has a distinct reason.

StateMeaningNext action
Capability deniedThe caller lacks the node’s declared capabilityNarrow or correct the worker capability set; do not grant solution access as a shortcut
Job cancellation was requestedThe job cannot accept more node workInspect events, then resume after the cause is resolved
Node is not pending or Node is not runningA duplicate or out-of-order transition was attemptedRead job state and use the durable node status
validation_runs.status = staleA mystery policy, solution, or typed record changed after validationRun /mystery/validate and inspect new findings
Closure publishable: falseA critical finding or unresolved hard obligation remainsResolve, defer with a target, or waive with rationale; then recheck closure
Missing chapter content for recoveryThe public API exposes revision metadata onlyStop, preserve the database, recover through a read-only local SQLite inspection path

Open the rendered revision-invalidation diagram · Read the canonical Mermaid source

Accessible equivalent: a proposed change enters Studio; the system calculates affected records; the user reviews the diff and consequences; approval appends the new version; dependent audits become stale; downstream workflow nodes block until re-audit.