Skip to content

fix: clamp derived state to the last written chapter on delete - #292

Merged
andyhsu6 merged 1 commit into
mainfrom
fix/derived-state-cutoff-after-delete
Nov 13, 2026
Merged

andyhsu6 merged 1 commit into
mainfrom
fix/derived-state-cutoff-after-delete

Conversation

@andyhsu6

Copy link
Copy Markdown
Owner

Closes #288.

Problem

Character profiles, world-setting facts and sync markers advance by chapter number but never roll back when chapters are deleted. On a real project, after removing chapters 58–70:

  • character state_updated_chapter / profile_synced_through_chapter still said 62;
  • profile_facts still cited chapters 58/59/61/62;
  • 612 world-setting claims, 118 of them sourced only from deleted chapters, still fed the world-building text that goes into every chapter prompt.

Change

  • Delete-time clamp (app/services/derived_state.py, called after DELETE /chapters/{id}):
    • drop character facts and world-setting claims whose source chapters are gone;
    • clear out-of-range current_state and reset its marker to NULL (setting it to the cutoff would make the update service skip re-analysis of earlier chapters — the anti-regression check is chapter < state_updated_chapter);
    • clamp profile_synced_through_chapter and world_profile_synced_through_chapter.
  • Cutoff = last chapter with content, not the last chapter row. Batch generation creates empty chapter shells; using shell numbers would keep facts from deleted chapters legal. This was caught while verifying: with the row-based cutoff, 118 stale claims survived.
  • Read-time guard: the 1-N context renderer skips state whose source chapter is after the chapter being written.
  • Consistency check: GET /project/{id}/consistency reports stale facts/markers (read-only, truncated list + total), so this drift is detectable rather than silent.

Verification

real project: cutoff=58, dropped_world_facts=118, remaining stale=0, ok=true
.venv/bin/python -m pytest tests/test_derived_state_cutoff.py -q  → 6 passed
.venv/bin/python -m pytest tests/ -q -p no:cacheprovider          → 1665 passed

Tests cover: clamping facts/state/markers, world-claim pruning (evidence kept when partly in range, claims without evidence preserved), idempotency, "clean data produces no UPDATE", and the read-time skip for both directions.

Independent review

Reviewed by a different model. One MAJOR was raised — world-setting facts were only watermark-clamped while their content stayed — and is addressed here (pruning + re-render avoided on purpose: re-rendering the four text columns from facts would rewrite existing content, verified by length comparison). Reviewer MINORs also handled: marker reset to NULL, unparseable facts counted in the report, endpoint list truncated to 20 with a total, and the no-op write path now has a test.

Not done here (deliberately): soft delete and an archive/trash table. If chapters should be recoverable, that is a separate change with its own read-path audit.

Desensitized per repository policy: no source text, titles or character names are included.

Character profiles, world facts and sync markers advance by chapter number but do
not roll back when chapters are deleted, so facts kept pointing at chapters that
no longer existed and were injected into later prompts.

- `clamp_derived_state_after_delete`: after a chapter delete, drop character facts
  and world-setting claims whose chapters are gone, clear out-of-range
  `current_state` (and reset its marker so re-analysis of earlier chapters can
  rebuild it), and clamp the sync markers.
- The cutoff is the last chapter **with content**, not the last chapter row:
  batch generation creates empty chapter shells, and using their numbers kept
  facts from deleted chapters legal.
- `find_stale_derived_state` + `GET /project/{id}/consistency`: read-only report of
  facts/markers that still reference missing chapters, so this class of drift is
  detectable instead of silent.
- 1-N context rendering skips state whose source chapter is after the chapter
  being written.

Verified on the affected project: 118 world-setting claims from deleted chapters
were pruned, 0 stale references remain.

Refs #288
@andyhsu6 andyhsu6 added the status:in-progress Work started, PR is open label Nov 13, 2026
@andyhsu6
andyhsu6 merged commit 11a1c0f into main Nov 13, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status:in-progress Work started, PR is open

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Deleting chapters leaves character state and profile facts pointing at deleted chapters

1 participant