Enforce book planning scope boundaries
This commit is contained in:
parent
ca045c02d7
commit
8a6eab543f
7 changed files with 442 additions and 3 deletions
|
|
@ -42,7 +42,10 @@ End with [[END_OF_SCENE]] on its own line."""
|
|||
|
||||
STANDALONE_SCENE_PLAN_SYSTEM = """You are a fiction scene architect. Return one valid JSON object only.
|
||||
Preserve every supplied canon fact and source boundary. Plan a complete dramatized scene, not a synopsis.
|
||||
Do not invent authority for provisional or planning sources, and do not silently resolve contradictions."""
|
||||
Do not invent authority for provisional or planning sources, and do not silently resolve contradictions.
|
||||
When the cited context contains an APPROVED BOOK-STATE PLANNING PACKET, that packet is the hard narrative
|
||||
scope boundary. Other source context may prevent contradictions but cannot authorize additional events,
|
||||
agreements, state changes, explanations, or exact values."""
|
||||
|
||||
STANDALONE_SCENE_PLAN_TEMPLATE = """Plan one complete scene titled {title}.
|
||||
|
||||
|
|
@ -70,7 +73,10 @@ Return strict JSON in this shape:
|
|||
"boundary_constraints":[],"continuity_questions":[]}}
|
||||
|
||||
Use 3-8 concrete beats. Mark only indispensable events required:true. Preserve unresolved continuity questions
|
||||
instead of guessing. The ending state and final image must define where the scene stops."""
|
||||
instead of guessing. The ending state and final image must define where the scene stops. For a book-bound
|
||||
scene, repeat the approved chapter beats without expanding their events or state changes. Include an exact
|
||||
value only when it appears explicitly in the approved chapter packet or scene brief; omit numbers and
|
||||
classifications found only in background source context."""
|
||||
|
||||
SCENE_IDEA_TYPES = {
|
||||
"quiet_connection": "A short, low-stakes character moment whose meaning comes from attention or choice.",
|
||||
|
|
|
|||
|
|
@ -642,6 +642,13 @@ class StandaloneSceneService:
|
|||
)
|
||||
)
|
||||
plan = self._normalize_plan(extract_json_object(response.content), scene)
|
||||
discarded_exact_values = []
|
||||
discarded_proposed_beats = []
|
||||
if scene.book_state_id:
|
||||
plan, discarded_proposed_beats = self._apply_book_chapter_scope(scene, plan)
|
||||
plan["exact_values"], discarded_exact_values = self._scoped_exact_values(
|
||||
scene, plan["exact_values"]
|
||||
)
|
||||
requirements = self._requirements(plan)
|
||||
scene.plan = plan
|
||||
scene.contract_requirements = requirements
|
||||
|
|
@ -654,12 +661,62 @@ class StandaloneSceneService:
|
|||
"prompt_sha256": text_sha256(prompt),
|
||||
"response_sha256": text_sha256(response.content),
|
||||
"context_pack_sha256": pack["sha256"],
|
||||
"discarded_out_of_scope_exact_values": discarded_exact_values,
|
||||
"discarded_model_proposed_beats": discarded_proposed_beats,
|
||||
**self._book_state_metadata(scene),
|
||||
}
|
||||
scene.generation_metadata = metadata
|
||||
scene.save()
|
||||
return scene
|
||||
|
||||
def _apply_book_chapter_scope(
|
||||
self, scene: StandaloneScene, plan: dict[str, Any]
|
||||
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
|
||||
chapter, _chapters, _index = self._book_chapter(
|
||||
scene.book_state.content or {}, scene.book_chapter_key
|
||||
)
|
||||
approved_beats = [
|
||||
{
|
||||
"text": str(item.get("text") or "").strip(),
|
||||
"required": bool(item.get("required")),
|
||||
}
|
||||
for item in chapter.get("beats") or []
|
||||
if isinstance(item, dict) and str(item.get("text") or "").strip()
|
||||
]
|
||||
approved_text = {item["text"].casefold() for item in approved_beats}
|
||||
discarded = [
|
||||
item
|
||||
for item in plan["beats"]
|
||||
if str(item.get("text") or "").strip().casefold() not in approved_text
|
||||
]
|
||||
plan["beats"] = approved_beats
|
||||
plan["purpose"] = str(chapter.get("purpose") or "").strip()
|
||||
plan["ending_state"] = str(chapter.get("ending_state") or "").strip()
|
||||
return plan, discarded
|
||||
|
||||
def _scoped_exact_values(
|
||||
self, scene: StandaloneScene, exact_values: list[Any]
|
||||
) -> tuple[list[Any], list[Any]]:
|
||||
scope = "\n".join(
|
||||
[
|
||||
self._book_planning_packet(scene),
|
||||
scene.brief,
|
||||
json.dumps(scene.constraints, ensure_ascii=False),
|
||||
json.dumps(scene.forbidden_events, ensure_ascii=False),
|
||||
json.dumps(scene.boundary_constraints, ensure_ascii=False),
|
||||
]
|
||||
).casefold()
|
||||
allowed = []
|
||||
discarded = []
|
||||
for item in exact_values:
|
||||
value = item.get("value") if isinstance(item, dict) else item
|
||||
candidate = "" if value is None else str(value).strip().casefold()
|
||||
if candidate and candidate in scope:
|
||||
allowed.append(item)
|
||||
else:
|
||||
discarded.append(item)
|
||||
return allowed, discarded
|
||||
|
||||
def prepare_context(
|
||||
self,
|
||||
scene: StandaloneScene,
|
||||
|
|
|
|||
139
docs/fortune-below-bootstrap.md
Normal file
139
docs/fortune-below-bootstrap.md
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
# The Fortune Below Bootstrap
|
||||
|
||||
This bootstrap registers source evidence only. It does not extract facts or promote generated prose into canon.
|
||||
|
||||
## 1. Apply Artifex Migrations
|
||||
|
||||
Use the configured PostgreSQL database so scene state and model-request provenance survive across processes.
|
||||
|
||||
```bash
|
||||
python manage.py migrate
|
||||
```
|
||||
|
||||
## 2. Register Shared Hard Canon
|
||||
|
||||
Shared canon belongs to a series-reference work. Book scene retrieval automatically includes this work.
|
||||
|
||||
```bash
|
||||
python manage.py story_sources register \
|
||||
--root S:/PycharmProjects/wraith-book-projects/AuthorCompanion/manuscripts/the-fortune-below \
|
||||
--series-slug labyrinth-hero \
|
||||
--series-title "Labyrinth Hero" \
|
||||
--work-slug fortune-below-series-reference \
|
||||
--work-title "Fortune Below Series Reference" \
|
||||
--work-type series_reference \
|
||||
--authority canon \
|
||||
--document-type canon \
|
||||
--include-glob "canon/fortune-below-world-lore.md"
|
||||
```
|
||||
|
||||
`fortune-below-restructure-canon.md` and `fortune-below-series-arc.md` identify themselves as planning
|
||||
documents. Archive them under `fortune-below-book-two-legacy` with `planning` authority; never register them
|
||||
as shared canon.
|
||||
|
||||
## 3. Register Mainline Book Two Planning
|
||||
|
||||
Planning documents are retrievable only when a scene command explicitly includes `planning` authority. Do
|
||||
not use a broad `book-two/planning/*.md` glob: that directory also contains Sabine/Corin side-novel material.
|
||||
|
||||
```bash
|
||||
python manage.py story_sources register \
|
||||
--root S:/PycharmProjects/wraith-book-projects/AuthorCompanion/manuscripts/the-fortune-below \
|
||||
--series-slug labyrinth-hero \
|
||||
--series-title "Labyrinth Hero" \
|
||||
--work-slug the-fortune-below \
|
||||
--work-title "The Fortune Below" \
|
||||
--authority planning \
|
||||
--document-type planning \
|
||||
--include-glob "book-two/planning/fortune-below-book-two-cerys-*.md" \
|
||||
--include-glob "book-two/planning/fortune-below-book-two-corins-*.md" \
|
||||
--include-glob "book-two/planning/fortune-below-book-two-male-friends-*.md" \
|
||||
--include-glob "book-two/planning/fortune-below-book-two-missing-scene-roadmap.md" \
|
||||
--include-glob "book-two/planning/fortune-below-book-two-permission-slips-*.md"
|
||||
```
|
||||
|
||||
Register the current folded-route plan and approved pilot artifacts separately from the Artifex artifact root.
|
||||
|
||||
## 4. Register The Sabine/Corin Side Novel
|
||||
|
||||
The side novel is a distinct work even though its chronology overlaps Books Two and Three.
|
||||
|
||||
```bash
|
||||
python manage.py story_sources register \
|
||||
--root S:/PycharmProjects/wraith-book-projects/AuthorCompanion/manuscripts/the-fortune-below \
|
||||
--series-slug labyrinth-hero \
|
||||
--series-title "Labyrinth Hero" \
|
||||
--work-slug fortune-below-sabine-corin \
|
||||
--work-title "Fortune Below: Sabine and Corin" \
|
||||
--authority planning \
|
||||
--document-type planning \
|
||||
--include-glob "book-two/planning/*sabine-corin*.md"
|
||||
```
|
||||
|
||||
Register selected Sabine/Corin scenes and manuscript drafts against this side work, never against
|
||||
`the-fortune-below`.
|
||||
|
||||
## 5. Archive Existing Mixed Book Two Material
|
||||
|
||||
The old planning and scene directories mix mainline, side-novel, alternate, and superseded structures. Preserve
|
||||
the complete directories under the `fortune-below-book-two-legacy` work before selecting any source into a
|
||||
current work. The legacy work is `other`, so its documents are not visible to mainline or side-novel retrieval.
|
||||
|
||||
Pure Sabine/Corin sources may then be registered separately under `fortune-below-sabine-corin`. Do not leave
|
||||
active copies attached to `the-fortune-below`.
|
||||
|
||||
## 6. Register Existing Mainline Scenes Safely
|
||||
|
||||
Existing mainline scenes enter as provisional evidence. Do not use a broad `book-two/scenes/*.md` glob because
|
||||
that directory contains side-novel drafts and noncanon experiments.
|
||||
|
||||
```bash
|
||||
python manage.py story_sources register \
|
||||
--root S:/PycharmProjects/wraith-book-projects/AuthorCompanion/manuscripts/the-fortune-below \
|
||||
--series-slug labyrinth-hero \
|
||||
--series-title "Labyrinth Hero" \
|
||||
--work-slug the-fortune-below \
|
||||
--work-title "The Fortune Below" \
|
||||
--authority provisional \
|
||||
--document-type scene \
|
||||
--include-glob "book-two/scenes/fortune-below-book-two-cerys-*.md" \
|
||||
--include-glob "book-two/scenes/fortune-below-book-two-selka-cerys-*.md" \
|
||||
--include-glob "book-two/scenes/fortune-below-book-two-estate-*.md" \
|
||||
--include-glob "book-two/scenes/fortune-below-book-two-short-ch01-*.md"
|
||||
```
|
||||
|
||||
Register the ten-turn manuscript under the side work as provisional manuscript material until its assembly is
|
||||
approved.
|
||||
|
||||
```bash
|
||||
python manage.py story_sources register \
|
||||
--root S:/PycharmProjects/wraith-book-projects/AuthorCompanion/manuscripts/the-fortune-below \
|
||||
--series-slug labyrinth-hero \
|
||||
--series-title "Labyrinth Hero" \
|
||||
--work-slug fortune-below-sabine-corin \
|
||||
--work-title "Fortune Below: Sabine and Corin" \
|
||||
--authority provisional \
|
||||
--document-type manuscript \
|
||||
--include-glob "book-two/ten-turn-manuscript/*.md"
|
||||
```
|
||||
|
||||
## 7. Plan A Scene
|
||||
|
||||
The default context includes hard canon only. This example deliberately includes planning material and pins the
|
||||
two current rule documents.
|
||||
|
||||
```bash
|
||||
python manage.py fiction_scene run \
|
||||
--series-slug labyrinth-hero \
|
||||
--work-slug fortune-below-sabine-corin \
|
||||
--title "New Scene Title" \
|
||||
--brief S:/path/to/new-scene-brief.md \
|
||||
--target-words 2200 \
|
||||
--include-authority canon \
|
||||
--include-authority planning \
|
||||
--pin-document book-two/planning/fortune-below-sabine-corin-turn-and-household-rules.md \
|
||||
--pin-document book-two/planning/fortune-below-sabine-corin-physical-continuity-reconciliation.md
|
||||
```
|
||||
|
||||
Inspect the returned plan before approving it. Final scene approval registers the artifact as `provisional`, not
|
||||
`canon`. Mainline scene commands must not pin side-work documents.
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
# Book Two Mainline Fact Ledger
|
||||
|
||||
## Authority
|
||||
|
||||
This ledger separates mainline planning facts from the overlapping Sabine/Corin side novel. The approved
|
||||
two-chapter book state is authoritative only for its pilot run. Everything beyond Chapter 2 remains
|
||||
provisional until approved in a later book-state version.
|
||||
|
||||
## Approved Pilot Scope
|
||||
|
||||
### Chapter 1: Two Markers
|
||||
|
||||
- Selka accepts a survey under an independently terminable contract.
|
||||
- Selka retains ownership of her maps.
|
||||
- Records appear to place the same damaged marker in two incompatible locations.
|
||||
- Selka refuses to certify an explanation before controlled field testing.
|
||||
- The doubled-marker contradiction remains unresolved at the chapter ending.
|
||||
|
||||
### Chapter 2: What The Profit Means
|
||||
|
||||
- The bank confirms the million-crown windfall.
|
||||
- Corin proposes protected allocations; the allocations are not yet accomplished facts.
|
||||
- Cerys recognizes that the proposal could make legal freedom financially possible.
|
||||
- Cerys asks whether freedom can coexist with her chosen home, work, and belonging.
|
||||
- Cerys is not released in the pilot.
|
||||
|
||||
### Pilot Exclusions
|
||||
|
||||
- Do not resolve the folded-route anomaly.
|
||||
- Do not establish additional financial, contractual, legal, or route facts merely because they appear in
|
||||
legacy planning.
|
||||
- Do not introduce the Sabine/Corin game.
|
||||
|
||||
## Provisional Mainline Scope After Chapter 2
|
||||
|
||||
The current folded-route restructure proposes, but has not canonized:
|
||||
|
||||
- Selka's investigation as Book Two's external spine.
|
||||
- Cerys's meaningful freedom as the emotional co-spine.
|
||||
- Corin learning that money and equipment cannot replace another person's authority.
|
||||
- Sabine's later rescue, prompt release, independent alternatives, chosen return, and paid estate work.
|
||||
- Short externally observable Sabine/Corin background beats after Sabine is free.
|
||||
- The folded-route crisis as the mainline climax.
|
||||
- Hallow Street becoming independently owned while Corin becomes a guest rather than landlord.
|
||||
|
||||
## Material Not Owned By Mainline
|
||||
|
||||
The mainline does not own Sabine and Corin's private turns, scores, physical progression, declarations,
|
||||
sealed-envelope events, threshold sequence, or ending. Those belong to the side novel and may enter a
|
||||
mainline book only through a separately reviewed shared-continuity bridge.
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
# Fortune Below Mainline And Sabine/Corin Source Boundary
|
||||
|
||||
## Status
|
||||
|
||||
This is an operating source-ownership boundary. It controls retrieval and planning scope; it does not
|
||||
promote provisional events into canon.
|
||||
|
||||
## Mainline Books Two And Three
|
||||
|
||||
The mainline owns events that change the expedition, household, legal, financial, or publicly observable
|
||||
state of the ensemble. This includes:
|
||||
|
||||
- Selka's folded-route investigation and professional authority.
|
||||
- Cerys's allocation, release, employment, belonging, and mainline relationship choices.
|
||||
- The estate, Hallow Street, route-office, equipment, and expedition arcs.
|
||||
- Sabine's rescue, legal release, independent alternatives, chosen return, paid work, and relationships with
|
||||
the household when those events are selected into an approved mainline book state.
|
||||
- Only the minimum externally observable Sabine/Corin continuity needed by another mainline scene.
|
||||
|
||||
The approved two-chapter Book Two pilot contains no Sabine/Corin side-novel event. Chapter 1 excludes the
|
||||
game entirely, and Chapter 2 concerns Cerys's freedom question.
|
||||
|
||||
## Sabine/Corin Side Novel
|
||||
|
||||
The side novel owns the private progression of Sabine and Corin's relationship across the time occupied by
|
||||
Books Two and Three. This includes:
|
||||
|
||||
- The game, turns, scores, rules, private experiments, and physical thresholds.
|
||||
- Private declarations, the sealed envelope, its handling, and its resolution.
|
||||
- The ten-turn manuscript, the preferred threshold sequence, alternate follow-ups, and rejected experiments.
|
||||
- Scene-level causes and consequences that are not independently visible to the mainline ensemble.
|
||||
|
||||
Mainline planning must not retrieve these materials. A side-novel event does not become a mainline fact merely
|
||||
because its chronology overlaps a mainline book.
|
||||
|
||||
## Shared Continuity
|
||||
|
||||
Shared continuity must be deliberately copied into a reviewed bridge document. It is limited to public or
|
||||
externally observable resulting states, never private scene choreography.
|
||||
|
||||
Current provisional Book Two bridge candidates are:
|
||||
|
||||
- Sabine and Corin begin a private game after Sabine is free and has chosen to remain at the estate.
|
||||
- Mainline scenes may show brief, unexplained evidence that the game continues.
|
||||
- The game remains unresolved at the end of the current provisional Book Two structure.
|
||||
|
||||
Potential Book Three bridge facts remain unapproved until the side-novel chronology and manuscript assembly
|
||||
are approved. Mainline work must not assume declarations, envelope resolution, physical completion, standing
|
||||
permission, or expected repetition.
|
||||
|
||||
## Retrieval Rules
|
||||
|
||||
- Mainline work may retrieve mainline sources and reviewed shared-continuity bridges.
|
||||
- Side-novel work may retrieve side-novel sources and reviewed shared-continuity bridges.
|
||||
- Neither work may retrieve the other work's planning, scenes, or manuscript drafts directly.
|
||||
- Series reference is reserved for genuinely shared world and established continuity material.
|
||||
- A document labelled provisional or planning cannot be registered with canon authority.
|
||||
- Mixed documents must be superseded and replaced by separately scoped documents rather than shared broadly.
|
||||
|
||||
## Current Ownership Decisions
|
||||
|
||||
- `book-two-folded-route-restructure-plan.md` belongs to mainline Book Two planning.
|
||||
- `book-two-folded-route-pilot-2-chapters.json` and its chapter briefs belong to mainline Book Two.
|
||||
- `sabine-corin-side-book-30-chapter-plan.md` belongs to side-novel planning.
|
||||
- Sabine/Corin scene menus, turn plans, private-scene drafts, the ten-turn manuscript, threshold prose, and
|
||||
optional follow-ups belong to the side novel.
|
||||
- The complete `No Final Move` experiment belongs to the side work as a noncanon experiment, not as a
|
||||
provisional mainline manuscript.
|
||||
- `fortune-below-book-two-expanded-friendship-and-hallow-arcs.md` is mixed legacy planning and must be
|
||||
superseded rather than used as a governing source by either work.
|
||||
- `fortune-below-series-arc.md` and `fortune-below-restructure-canon.md` identify themselves as planning and
|
||||
must not retain canon authority.
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
# Sabine And Corin Side-Novel Ledger
|
||||
|
||||
## Authority
|
||||
|
||||
This is a provisional scope and continuity ledger for the side novel. It does not canonize its candidate
|
||||
assembly or determine exactly which events fall during Book Two versus Book Three.
|
||||
|
||||
## Mainline Prerequisites
|
||||
|
||||
The side novel may rely on these events only after their corresponding mainline book states are approved:
|
||||
|
||||
- Sabine is removed from an imminent coercive sale.
|
||||
- Her legal claim is cancelled at the first practical opportunity.
|
||||
- She receives real alternatives and can leave without losing safety or subsistence.
|
||||
- She investigates those alternatives and returns by choice.
|
||||
- She undertakes paid estate work with independent authority.
|
||||
|
||||
The side novel must not rewrite the folded-route investigation, Cerys's freedom, expedition outcomes, estate
|
||||
ownership, or other mainline state transitions.
|
||||
|
||||
## Side-Novel Ownership
|
||||
|
||||
The side novel exclusively owns:
|
||||
|
||||
- Shopping With Corin, Full Attention, Two Cups, and the expensive dance lessons.
|
||||
- The six-turn sequence and its individual chapter candidates.
|
||||
- The First Journey, Envelope After, Long Game, and He Likes Her Happy.
|
||||
- The ten-turn manuscript and all private relationship developments inside it.
|
||||
- The preferred threshold run from Reserved Accidental Threshold Escalation through Fortunately.
|
||||
- Optional follow-ups, rejected experiments, and candidate menus as noncanon alternatives.
|
||||
|
||||
## Current Preferred Ending State
|
||||
|
||||
The preferred ending material currently proposes that Sabine and Corin openly love one another, continue the
|
||||
game, and have deliberately completed one previously deferred physical threshold. It does not create standing
|
||||
permission, expected repetition, or an end to the game. These remain side-novel outcomes until the manuscript
|
||||
and chronology are approved.
|
||||
|
||||
## Mainline Export Rule
|
||||
|
||||
No private side-novel event is exported automatically. A reviewed bridge may expose only the minimum public
|
||||
result another book requires, such as the household noticing that the relationship has changed. Scores,
|
||||
private choreography, envelope contents, and scene-level causality remain private to the side novel.
|
||||
|
|
@ -20,7 +20,7 @@ from control_plane.authoring.models import (
|
|||
)
|
||||
from control_plane.authoring.standalone_scenes import StandaloneSceneService
|
||||
from control_plane.projects.models import Project
|
||||
from model_router.router import ModelResponseContract
|
||||
from model_router.router import ModelCapability, ModelResponseContract
|
||||
|
||||
pytestmark = pytest.mark.django_db(transaction=True)
|
||||
|
||||
|
|
@ -38,6 +38,42 @@ class FakeReviewRouter:
|
|||
)
|
||||
|
||||
|
||||
class OutOfScopePlanningRouter:
|
||||
def complete(self, request):
|
||||
assert str(request.purpose) == str(ModelCapability.STORY_PLANNING)
|
||||
return ModelResponseContract(
|
||||
model="test-planner",
|
||||
content=json.dumps(
|
||||
{
|
||||
"purpose": "Perform the approved turn.",
|
||||
"pov_character": "Protagonist",
|
||||
"tense": "past",
|
||||
"location": "Records room",
|
||||
"time_context": "Chapter 1",
|
||||
"present": ["Protagonist"],
|
||||
"beats": [
|
||||
{"text": "The chapter performs turn 1.", "required": True},
|
||||
{
|
||||
"text": "The planner adds a Floor ninety-six inspection.",
|
||||
"required": True,
|
||||
},
|
||||
],
|
||||
"exact_values": [
|
||||
{"label": "approved turn", "value": "turn 1"},
|
||||
{"label": "background floor", "value": "Floor ninety-six"},
|
||||
],
|
||||
"constraints": [],
|
||||
"forbidden_events": [],
|
||||
"ending_state": "Turn 1 is complete.",
|
||||
"final_image": "The protagonist closes the record.",
|
||||
"boundary_constraints": [],
|
||||
"continuity_questions": [],
|
||||
}
|
||||
),
|
||||
metadata={},
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def work(tmp_path: Path) -> Work:
|
||||
series = Series.objects.create(title="Test Series", slug="test-series")
|
||||
|
|
@ -338,6 +374,42 @@ def test_run_sync_uses_only_scenes_bound_to_exact_state_and_chapter(work: Work)
|
|||
assert revision.revision == 2
|
||||
|
||||
|
||||
def test_book_scene_plan_discards_exact_values_outside_approved_scope(work: Work) -> None:
|
||||
state_service = BookStateService(FakeReviewRouter())
|
||||
state = state_service.create(work=work, content=book_content(1))
|
||||
make_review_ready(state)
|
||||
state = state_service.approve(state)
|
||||
state_service.start_run(state)
|
||||
scene_service = StandaloneSceneService(OutOfScopePlanningRouter())
|
||||
scene = scene_service.create(
|
||||
work=work,
|
||||
title="Chapter 1",
|
||||
brief="Perform turn 1 and stop.",
|
||||
book_state=state,
|
||||
book_chapter_key="chapter-1",
|
||||
)
|
||||
|
||||
scene = scene_service.plan(scene)
|
||||
|
||||
assert scene.plan["exact_values"] == [
|
||||
{"label": "approved turn", "value": "turn 1"}
|
||||
]
|
||||
assert scene.plan["beats"] == [
|
||||
{"text": "The chapter performs turn 1.", "required": True}
|
||||
]
|
||||
assert scene.plan["purpose"] == "Advance turn 1."
|
||||
assert scene.plan["ending_state"] == "Turn 1 is complete."
|
||||
assert scene.generation_metadata["planning"][
|
||||
"discarded_out_of_scope_exact_values"
|
||||
] == [{"label": "background floor", "value": "Floor ninety-six"}]
|
||||
assert scene.generation_metadata["planning"]["discarded_model_proposed_beats"] == [
|
||||
{
|
||||
"text": "The planner adds a Floor ninety-six inspection.",
|
||||
"required": True,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_lifecycle_persists_audit_metadata_and_run_policy(work: Work) -> None:
|
||||
service = BookStateService(FakeReviewRouter())
|
||||
state = service.create(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue