Files
JesseMarkowitzandClaude Opus 5 b7005e6fdd M5: genre-neutral authoritative narrative state, with review corrections
Replaces AI-DnD's RPG relative-delta world state with the genre-neutral typed
narrative state of ADR 010: explicit, absolute, allowlisted events proposed by
the model, validated by the application, applied to one authoritative document,
and snapshotted per position so restore stays a row read.

This commit includes the corrective pass that followed the independent review
in planning/reports/M5-IMPLEMENTATION-REPORT.md. The invariant it exists to
hold is:

    visible active transcript position == stored head == authoritative state

Narrator editing (D10, STORY-BRANCH-SEMANTICS §§14-15)

  A narrator edit no longer rewrites a row. It returns to the state before the
  turn, takes the reader's exact text as the accepted narration, re-derives the
  state that text implies, and becomes a new active continuation — while the
  original narration keeps its words, its live flag and its whole future as
  retained history. At the tip the correction is another take; with story below
  it, it forks. No new history machinery: this is the existing fork/take/head
  path with the reader's text in place of a generated reply. The §14A refusal
  is therefore gone for narrator turns, and remains only for player input.

Pre-M5 positions

  Migration 88 backfills the empty narrative document onto every action written
  before M5, and a missing snapshot now restores the empty document instead of
  leaving the previous position's state standing. Restoring to an old Save
  Point no longer leaves a later position's entities and facts on screen.

Narrator context

  Replayed history carries prose only; the machine-readable block is no longer
  reconstructed into past turns, where it contradicted the authoritative state
  in the same prompt. A fact withdrawn by a manual correction is now named as
  no longer true, with the reader's reason, rather than silently dropped.

Also

  - state_changes joins the action-list bulk read, removing one query per row.
  - Extraction takes only the application's own protocol payload: an ordinary
    ```json or ```python block in a story survives, and a mangled proposal
    still does not reach the reader.

Planning: ADR 013 records the authoritative document shape; §§14-15/14A, D10,
C04 and BUILD-MILESTONES are updated to describe what exists. Debt is recorded
against M8 (scenario editor UX) and M9 (export of the audit trail).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PWU4gTfLYY6Qq9U7aa9Qw2
2026-09-05 07:01:50 -04:00

61 lines
2.1 KiB
Python

"""The adventure endpoints, split across one module per group of routes.
Importing this package registers every route, because each endpoint module
decorates its handlers with the shared `router` from `deps`. The import order
below is the order FastAPI matches paths in. No two routes here shadow each
other, so the order is for reading rather than for correctness.
Read the modules in this order to follow a turn from end to end:
deps the router and the ownership check every endpoint runs
paging reading a window of actions and numbering its attempts
nodes moving around the story tree
turns playing a turn, and the lock that allows only one at a time
takes retries and the attempts that collect at one coordinate
branches where a story splits
checkpoints Save Points: durable names for positions the head can return to
state the authoritative narrative state, and correcting it by hand
What this package re-exports, and what it deliberately does not:
Pure helpers and handlers are re-exported below, so `adventures.ACTION_PAGE` and
`adventures.undo_turn` keep working. The names a test replaces are not, and you
must reach those as `adventures.turns.<name>`. Rebinding a re-exported alias
changes only the alias, so patching `adventures.generate_turn` would leave every
caller reading the original. Leaving those names off raises `AttributeError`
instead, which is the failure you want.
"""
from .deps import router
# Imported for the side effect of registering routes. The names are unused here.
from . import ( # noqa: F401
crud,
turns,
takes,
branches,
checkpoints,
state,
bundle_io,
refresh,
insights,
memories,
actions,
)
from ... import limits # noqa: F401 `adventures.limits` is patched by tests.
from .crud import SNIPPET_MAX, _snippet
from .paging import ACTION_PAGE
from .takes import redo_turn, retry_action, undo_turn
from .turns import world_delta_of
__all__ = [
"ACTION_PAGE",
"SNIPPET_MAX",
"_snippet",
"limits",
"redo_turn",
"retry_action",
"router",
"undo_turn",
"world_delta_of",
]