Files
interactive-story/backend/app/routers/adventures/__init__.py
T
parththakkar106andClaude Opus 5 2c57b1ceab Remove three kinds of duplication in the backend
Stage 2, items 1, 4, and 5 of `plan/17-refactor.md`.

**One path resolver in `worldstate`.** `apply_delta` and `apply_override` routed
`flags.<name>`, `milestones.<id>`, `world.<stat>`, `player.<stat>`, and
`npc.<id>.<stat>` with parallel code, about 100 lines each. `_resolve` now says
what a path points at and returns either a target or the rejection to report.
Each function keeps its own write rule, because the rules genuinely differ: an
override sets a number rather than adding to it, ignores `cooldown`,
`max_delta_per_turn`, and the rule that a counter only counts up, and can un-set
a milestone.

A differential check ran both implementations over 3960 payloads: twenty paths,
fourteen values, three starting states, plus every three-path combination. The
results are identical except that 674 rejections from `apply_override` now carry
a `fix` string. `apply_delta` already worded those, and the world-state editor
renders them, so an override that names an unknown flag now explains itself the
way a delta does.

**`sse`, `SSE_HEADERS`, and `turn_error` move to `app/sse.py`.** Two routers
stream, and `chat.py` had to import from `routers.adventures` to reach them.

**`get_adventure_or_404` becomes the `current_adventure` dependency.** All 32
handlers repeated the call as their first statement. The ownership check now
reads in the signature and runs before the body. FastAPI caches a dependency for
one request, so the handler's `db` is the session the adventure came from.

The generated OpenAPI document is byte-identical except on `rename_branch`,
where `branch_id` is now listed before `adventure_id`, because that handler no
longer names `adventure_id` itself. Parameter order in the document is
cosmetic.

Six tests in `test_state_revert.py` call `undo_turn` and `retry_action`
directly rather than over HTTP. They pass the adventure they already hold
instead of an id.

549 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Dix4oGV3njgWRdu7P9t6r
2026-08-29 02:04:02 +05:30

57 lines
1.9 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
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,
bundle_io,
scripts,
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 retry_action, undo_turn
from .turns import world_delta_of
__all__ = [
"ACTION_PAGE",
"SNIPPET_MAX",
"_snippet",
"limits",
"retry_action",
"router",
"undo_turn",
"world_delta_of",
]