Patterns

Spoiler-safe narrative memory

Ingest full canon; answer only as far as the user has read or watched.

Spectron can hold an entire story — novel, series, franchise, training curriculum released in modules — while an agent answers only from what the user has reached so far. The pattern combines three ideas you already have elsewhere in the docs:

  1. observed_at / observedAt on ingest — stamp each chapter, page, or episode with a synthetic known time on the narrative axis (not wall-clock import time).

  2. asOf on recall — the user's current position ("I'm on chapter 8", "I've finished episode V") selects which stamped facts and relations are visible.

  3. labels and lens (optional) — navigate by chapter=3 / page=5, or narrow a query to a scope region without changing permissions.

See Temporal validity for the model; this page is the recipe.

ScenarioWhy bulk ingest breaksWhat you gate
Novel with a late revealHyde/Jekyll-style identity twistRelation edges and attributes stamped at the reveal
TV or film franchiseViewing order ≠ story chronologyPer-episode stamps in the order the user chose
Long book seriesReader is on book 3 of 14asOf at book 3's end — later books ingested but hidden
Policy / curriculum modulesUser certified on module 2 onlyModule-scoped stamps + labels

The Strange Case of Dr Jekyll and Mr Hyde is a useful mental model: the whole plot is a single-entity reveal. Ingest each chapter (or page) with its own synthetic observedAt, monotonically increasing through the book. Stamp the Hyde↔Jekyll same_as relation only when you ingest the reveal chapter.

  • Query with asOf at “chapter 8” → Hyde and Jekyll stay separate in the graph; no spoiler link.

  • Query with asOf at “book finished” → the same_as edge is visible.

Same reader, same permissions — only the asOf instant changes.

Release order and story order are different problems with the same mechanism: stamps follow discovery order, not calendar dates.

Star Wars (release order IV → V → VI, then I → II → III)

Ingest each film with observedAt (or per-scene triples with observed_at) on a timeline that matches when a release-order viewer learns each fact. "Darth Vader is Luke's father" gets a stamp after Empire — not after A New Hope. A separate reader profile watching I → II → III first would use a different stamp sequence on the same ingested records (or separate scope paths per viewing track), because for this viewer there is no spoiler in Episode V that Anakin Skywalker and Darth Vader are the same person. However, a viewer following this path does have a reveal in Episode III that Anakin is no longer a hero, a fact that a user watching in release order would already be aware of when viewing the prequels.

Wheel of Time (reader on book 3)

Ingest all books if you want one substrate, but stamp facts extracted from book N with monotonically increasing known times per book (or per chapter). Recall with asOf set to "end of book 3" so prophecies, deaths, and alliances from later books stay out of answers.

POST /api/v1/{context_id}/facts
Content-Type: application/json

{
"text": "… excerpt from The Dragon Reborn …",
"infer": "full",
"scopes": [["org/my-app/reader/alice"]],
"observed_at": "2000-10-15T00:00:00Z",
"labels": ["book=3", "chapter=12"]
}
POST /api/v1/{context_id}/query
Content-Type: application/json

{
"query": "Who is Rand al'Thor allied with?",
"k": 10,
"asOf": "2000-10-15T00:00:00Z",
"lens": [["org/my-app/reader/alice"]],
"labels": ["book=3"]
}

Adjust the synthetic timeline to your ordering scheme; the important part is that later books never share an earlier stamp.

For PDFs or markdown split into pages:

spectron documents upload ./chapters/ch08.txt \
--scope org/my-app/reader/alice \
--label chapter=8 --label page=5 \
--as-of 1886-03-01T00:00:00Z

Metadata JSON equivalent:

{
"title": "Dr Jekyll and Mr Hyde — ch. 8",
"scopes": [["org/my-app/reader/alice"]],
"labels": ["chapter=8", "page=5"],
"observedAt": "1886-03-01T00:00:00Z"
}

Then query with matching asOf and optional labels for passage-only navigation.

On a single full-access reader you can combine three independent slices:

NarrowingQuestionMechanism
TimeHow far have I read?asOf — gates facts and relations by known time
Scope lensLimit this query to chapters 1–8lens: [["chapter/1"], …] — involvement filter within grant
LabelsWhat's on page 3?labels: ["page=3"] — row filter; use with include: ["passages"] for text

Use time for spoiler boundaries on the graph; use labels for locating content within what time already allows.

  • Passages are not reading-time-gated by asOf alone — they retain upload-time metadata. Rely on the structured graph for spoiler-safe answers, or filter passages with labels.

  • asOf on /query walks known-time on attributes and relations; pair with Temporal validity for valid_from / valid_until on in-world dates.

  • Response caching is bypassed when temporal filters are present — correct for narrative playback, slightly higher latency.

Was this page helpful?