Export carries binned wiki pages, and import puts them straight into the bin (#1009) #1016

Merged
claude-bot merged 9 commits from feat/x1-binned-pages-travel into main 2026-10-02 21:13:22 +00:00
Collaborator

Closes #1009. The owner's answer to Q134 (X1), over the recommendation; recorded as D262.

#998 (for #990) left everything in a bin out of the export, because before it a binned page was exported and an import brought it back as a live page players could read. This carries binned wiki pages again, safely: they arrive in the bin.

The file (export schema 16)

  • A new section, wiki_bin: {audience: {kind: "gm"}, pages: [...]}. Each page is what a lore_entries page is, plus deleted_at (the time it was binned) and deleted_by_member_key. A binned page is never in lore_entries, so a reader that knows nothing of the bin finds none in "the wiki".
  • Nothing a person reads holds one: no file under wiki/, no count in README.md (it gains one line saying how many pages are in the bin, in data.json only), its picture under media/bin/lore/.
  • An older file imports as before. A wiki_bin in a file claiming schema ≤15 is ignored. An older server refuses a schema 16 file by version; this one now says "made by a newer Quest Board … update, then import it again" for a file newer than it reads.

The import

  • Binned in the INSERT. A binned page goes through the same loop as a live one with deleted_at in the constructor. A test listens to before_insert/before_update during a real import and asserts every binned row carried its stamp at insert and none changed afterwards.
  • One door. Only wiki_bin.pages at schema ≥16 bins a page; a marked row anywhere else is skipped. A binned page claiming a live page's key cannot take it over, and keeps its own owners.
  • One transaction. A failure after the pages are flushed leaves no row. (A picture already written to the private store is left there with nothing to serve it, as for a live page today.)
  • The original binned time is kept, so the bin's days carry across the move. An unreadable, missing, future or pre-2000 time becomes now.
  • What hangs off a page comes back as it stands on the source while the page is binned: aliases, owners, relationships, name targets, facts and timeline pointers, the thread about it, shelf cards, pins and prep items (GM-only), its reveals. The readers already filter a binned page; a round-trip test walks a player through about sixty routes on both servers and finds none of the page's words, then restores on both and compares the page.
  • D265 holds across the move: what was shown at the table stays shown. A card shown from the page, and the page's own reveal when it was shown in a session, are still listed under that session by the title they were shown with (an unidentified item's true name is absent); the handouts list drops the page until it is restored. Pinned by a test.

Reviewed

An independent read-only review tried to make a binned page reach a player or come back live, through hand-edited archives, ordering, the purge and every reader. It found no such path. Its hardening points are fixed here: owners resolved by page rather than by key on a key collision (was a 500), non-object lore_entries rows and odd schema_version strings refused with a 422 rather than a 500, absurd binned times normalised, and tests for the forward-reference change.

Also changed, for live pages

A fact or timeline row naming a page further down the file now keeps its link on import; it used to be dropped silently. Page ids are assigned up front, which binned targets needed.

For the owner (Q142)

A page already past the bin's days when it arrives is deleted by the next daily purge, which could be minutes after the import. Recommended: give an overdue page a week from arrival. Built as "original time kept"; a follow-up either way.

Not in this change

Binned transcripts, audio and campaigns keep their behaviour. Attachments, sheets, quest details, map markers and page history still do not travel for any page (#991).

Checked

  • Full backend suite: 6712 passed, 14 skipped; ruff 0.4.4 clean. After the review fixes and merging main: tests/export and the reader allowlists, 46 passed (350 across the wider targeted set).
  • No frontend change, no migration, no bot API change. docs/API.md and the bin ledger in tests/export/test_export_bin.py updated; changelog fragment.

🤖 Generated with Claude Code

Closes #1009. The owner's answer to Q134 (X1), over the recommendation; recorded as D262. #998 (for #990) left everything in a bin out of the export, because before it a binned page was exported and an import brought it back as a live page players could read. This carries binned **wiki pages** again, safely: they arrive in the bin. ## The file (export schema 16) - A new section, `wiki_bin: {audience: {kind: "gm"}, pages: [...]}`. Each page is what a `lore_entries` page is, plus `deleted_at` (the time it was binned) and `deleted_by_member_key`. A binned page is never in `lore_entries`, so a reader that knows nothing of the bin finds none in "the wiki". - Nothing a person reads holds one: no file under `wiki/`, no count in `README.md` (it gains one line saying how many pages are in the bin, in `data.json` only), its picture under `media/bin/lore/`. - An older file imports as before. A `wiki_bin` in a file claiming schema ≤15 is ignored. An older server refuses a schema 16 file by version; this one now says "made by a newer Quest Board … update, then import it again" for a file newer than it reads. ## The import - **Binned in the INSERT.** A binned page goes through the same loop as a live one with `deleted_at` in the constructor. A test listens to `before_insert`/`before_update` during a real import and asserts every binned row carried its stamp at insert and none changed afterwards. - **One door.** Only `wiki_bin.pages` at schema ≥16 bins a page; a marked row anywhere else is skipped. A binned page claiming a live page's key cannot take it over, and keeps its own owners. - **One transaction.** A failure after the pages are flushed leaves no row. (A picture already written to the private store is left there with nothing to serve it, as for a live page today.) - **The original binned time is kept**, so the bin's days carry across the move. An unreadable, missing, future or pre-2000 time becomes now. - What hangs off a page comes back as it stands on the source while the page is binned: aliases, owners, relationships, name targets, facts and timeline pointers, the thread about it, shelf cards, pins and prep items (GM-only), its reveals. The readers already filter a binned page; a round-trip test walks a player through about sixty routes on both servers and finds none of the page's words, then restores on both and compares the page. - **D265 holds across the move:** what was shown at the table stays shown. A card shown from the page, and the page's own reveal when it was shown in a session, are still listed under that session by the title they were shown with (an unidentified item's true name is absent); the handouts list drops the page until it is restored. Pinned by a test. ## Reviewed An independent read-only review tried to make a binned page reach a player or come back live, through hand-edited archives, ordering, the purge and every reader. It found no such path. Its hardening points are fixed here: owners resolved by page rather than by key on a key collision (was a 500), non-object `lore_entries` rows and odd `schema_version` strings refused with a 422 rather than a 500, absurd binned times normalised, and tests for the forward-reference change. ## Also changed, for live pages A fact or timeline row naming a page further down the file now keeps its link on import; it used to be dropped silently. Page ids are assigned up front, which binned targets needed. ## For the owner (Q142) A page already past the bin's days when it arrives is deleted by the next daily purge, which could be minutes after the import. Recommended: give an overdue page a week from arrival. Built as "original time kept"; a follow-up either way. ## Not in this change Binned transcripts, audio and campaigns keep their behaviour. Attachments, sheets, quest details, map markers and page history still do not travel for any page (#991). ## Checked - Full backend suite: **6712 passed, 14 skipped**; ruff 0.4.4 clean. After the review fixes and merging main: `tests/export` and the reader allowlists, 46 passed (350 across the wider targeted set). - No frontend change, no migration, no bot API change. `docs/API.md` and the bin ledger in `tests/export/test_export_bin.py` updated; changelog fragment. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Export schema 16: data.json gains wiki_bin, the wiki pages in the bin, each
with the time it was binned. An import writes each one with deleted_at set
in the row's own INSERT, so it is never live, and keeps the original time so
the bin's days do not start again. What stands beside a binned page comes
back as it stands in the old campaign, so Restore brings it back whole.
Every other bin still stays behind (#990).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A page's owners are the page's, not its key's: a wiki_bin page that claims a
live page's key, or two live pages sharing one, no longer put their owners on
the other page (a member on both was a duplicate row and a 500). A
lore_entries that is not a list of objects, and a schema version like
'²', are the import's ordinary refusal again. A binned time that is not a
moment in UTC, or is before 2000, is read as now. Tests pin the
forward-reference change on its own, two live pages sharing a key, and a
page's own reveal staying on its session while the page is binned (D265).
Docs say a failed import leaves no row, and that a picture can be left.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Merge remote-tracking branch 'origin/main' into feat/x1-binned-pages-travel
All checks were successful
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m2s
CI / Docker image build (pull_request) Successful in 21s
CI / Bot/backend version sync (pull_request) Successful in 29s
CI / Feedback site tests (pull_request) Successful in 46s
CI / Does this change touch the recording path? (pull_request) Successful in 28s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Backend lint (ruff) (pull_request) Successful in 1m23s
CI / Bot tests and audit (pull_request) Successful in 5m11s
CI / Frontend (shard 2/2) (pull_request) Successful in 7m30s
CI / End-to-end journeys (desktop) (pull_request) Successful in 10m29s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 11m31s
CI / Backend migration, tests, and audit (pull_request) Successful in 22m8s
CI / Frontend (shard 1/2) (pull_request) Successful in 10m48s
CI / End-to-end journeys (phone) (pull_request) Successful in 15m50s
f02d865b05
claude-bot deleted branch feat/x1-binned-pages-travel 2026-10-02 21:13:23 +00:00
Sign in to join this conversation.
No description provided.