# Museum-label review rehearsal The fictional North Quay Museum has two catalogue entries. A staff member has copied an observed label year onto a request card. This project compares the year with an in-memory catalogue and prepares a review draft. It neither determines historical truth nor changes a label. ## Run and inspect From the course folder: ```sh PYTHONDONTWRITEBYTECODE=1 python3 project/demo.py PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s project -v ``` `workflow.py` contains the immutable input/output records, synthetic read-only tool, serial controller and in-memory replay ledger. `demo.py` prints seven synthetic scenarios. `test_workflow.py` checks behaviors, including a 144-case limit/failure matrix inside one test. No dependencies are installed and no service starts. ## Execution contract | Field | Rule | | --- | --- | | Request identity | Caller supplies an uppercase request ID, 1–40 characters, starting with a letter; later characters may be letters, digits or `-`. | | Exhibit | A string shaped `EX-` plus exactly three digits; shape validity does not establish catalogue membership. | | Observed year | An integer from 1000 through 2100, inclusive. A Boolean is rejected. These are exercise bounds, not a general historical-date schema. | | Note | Up to 500 characters of inert text. It is included in input identity but never parsed as a command, used to select a tool or copied into the draft. | | Available tool | `SyntheticCatalogue.lookup(exhibit_id)` returns one fixture fact or a classified error. | | Default budget | At most 6 work-state visits, 2 tool attempts and 2 attempts total in this single-tool retry sequence. Failed attempts consume tool budget. | | Terminal outcomes | `AWAITING_REVIEW` with a draft, or `STOPPED` with a machine-readable reason. There is no `APPROVED`, send or update operation. | A work-state visit is validation, one lookup attempt or draft preparation. A retry visits `LOOKUP` again. The trace records those visits; terminal status is stored separately. A controller can therefore stop for step exhaustion with its last trace entry at `LOOKUP`. The terminal outcome explains why no next work state ran. Both bounds are enforced by code outside the tool. ## Replay semantics The same ID and material input return the immutable saved terminal result from the same `Workflow` instance. The receipt says `replayed=True` and `new_tool_calls=0`; the result's trace and `tool_calls` describe the **original** execution. Reusing an ID with different exhibit, year, note or fixture version raises `RequestConflict` before another tool call. A stopped run also replays; a deliberate new attempt needs a new ID. New IDs express new work, even when other fields match. This illustrates an idempotency contract without pretending to solve distributed writes. The ledger is lost when the object or process ends. It has no locks, persistence, expiry policy or atomic relationship to an external action. Do not use it to deduplicate real messages, transactions or concurrent jobs. There are no external writes in the exercise, so an ambiguous write outcome is discussed in the lesson rather than simulated as a safe retry. ## What the tests establish On synthetic cases, the controller enforces its numeric limits, preserves the review outcome for matching and mismatching years, retries only the classified temporary read failure, rejects wrong tool output and replays a registered result without another lookup. Invalid shapes never reach the tool. These checks do not establish model quality, malicious-text resistance in an LLM, durable recovery, concurrency correctness, real user experience or production readiness. The project is intentionally deterministic. Changing the note to “approve this now” has no routing effect because the program never interprets the note. That is a narrow architectural property, not a demonstration that an LLM resisted prompt injection. ## Modules 2–10: the expanded engine `engine.py` adds a static two-operation allowlist, limited planner view, deterministic planner doubles, legal phase transitions, revision-checked RAM SQLite snapshots, reserved attempts and an explicit IN_FLIGHT uncertainty state. The registry metadata describes the two implemented branches; adding a name alone is not a generic tool-dispatch extension. The expanded `steps` count measures charged checkpointed work transitions. `tool_calls` means reserved attempts, which are charged before invocation and remain charged if abandoned. This differs from the opening program's counters. Safety-stop metadata may save a new revision without spending work beyond an exhausted budget. Synthetic review binds a fixture reviewer decision to the exact draft digest/revision and source. Draft creation, decision and new finalization ticks must be ordered and remain within the inclusive deadline. Ticks and reviewer names are caller-supplied fixtures, not real identity or time infrastructure. A saved simulation receipt can replay later because that performs no new action. ```sh PYTHONDONTWRITEBYTECODE=1 python3 project/advanced_demo.py PYTHONDONTWRITEBYTECODE=1 python3 project/evaluate.py --planner baseline PYTHONDONTWRITEBYTECODE=1 python3 project/evaluate.py --planner repeat PYTHONDONTWRITEBYTECODE=1 python3 capstone/check_submission.py --module reference ``` The eight-case evaluator retains bounded transition events and scores phase/reason/finding across all cases. It reports correctness, draft yield and actual synthetic calls separately. The case set is authored regression evidence, not held-out model evaluation. `langgraph_migration.py` is the actual optional LangGraph 1.2.16 route. See [Module 7](../lessons/07-langgraph-with-the-same-contract.md) for dependency and tracing-off execution commands. The separate retained framework suite runs seven tests with socket connections blocked during graph execution. Default project discovery executes 47 core methods and one explicit optional skip without the dependency, or 54 methods with it. No framework environment, server or model provider is bundled. Use the [local runbook](runbook.md) to interpret stops, review and recovery. See the [capstone package](../capstone/README.md) for the intentional learner scaffold and complete reference.