Skip to content

Latest commit

 

History

History
151 lines (121 loc) · 22.5 KB

File metadata and controls

151 lines (121 loc) · 22.5 KB

The system model: where each fact lives

A map for a session that has to change one fact and know which test will notice. It says, for each thing the book-production loop reasons about, the module that holds it, the one function that reads it (the house rule the docstrings state dozens of times as "one source, no second answer"), what writes it, and the test that pins it. It points at ledger entries (plan/stage-0-decisions.md, cited as §N) and restates none of their numbers; the numbers about the code itself are regenerated by tools/maintainability_survey.py and recorded in plan/maintainability-survey.md.

Two checks keep this page from decaying: every backticked symbol below must exist in the package or the suite, and every backticked test_ name must be a real test (tests/test_architecture.py, the same checks that guard the docstrings).

Source-preservation amendment (§251): a system may have held-or-unheld capabilities with scale maximum one, while rank gates and ownership prerequisites retain their arithmetic. Completion does not ask for invented depth. The configuration scale label is not a printed column; column-label checks remain separate. The regression test_a_binary_system_preserves_rank_gates_and_does_not_offer_deepening exercises this case.

How a fact moves through the loop

A world is declared as StateRecord rows (litharness_contracts), each a subject, a predicate, a value or an object reference, a story position, and an authority. The Architect proposes rows; world accept makes them canon; the packet reads canon at a scene's position; the scene prompt renders the packet plus the lines the book prints; the writer returns prose; the gate ladder refuses or accepts it; the extractor reads the printed lines back into rows. Every table below is one stop on that path.

Where a name lives versus where it is imported. domain/extraction.py was split on 2026-09-03 into names, sheet, graphline and moves beneath it (stage-0 §215), and it re-exports every name they define, so an import site may still read extraction.sheet_for; the tables name the module that is the fact's home. gamesystem was split the same way (stage-0 §216): systems holds the definition and how it is written down, advancement the arithmetic, and gamesystem keeps the readers and re-exports the rest; all three sit below the five above.

The world's vocabulary

Fact Home The one reader Written by Pinned by
The predicates a world may use: node types, entity roles, the protagonist, what a person can do, what the system offers, rules and consequences, criteria and ladders, claims and disclosure, cardinality, change roles, views domain/worlds.py (constants, one section per family) the readers in the same module: entity_roles, criteria, ladder_of, rung_index, standing_of, capabilities_of, claims, disclosures, cardinality_shapes worlds.world_record (content-derived id, so a changed value is a new row: §11, §160) test_world_slots.py::test_every_documented_slot_is_the_slot_its_reader_reads reads the Architect's vocabulary line by line against these readers; test_the_documented_line_names_the_slots_its_own_record_fills
What the Architect is told the slots are application/world.py::vocabulary (the views in VIEWS) the CLI's world subcommands, one per view by hand, beside the reader it documents (§152, §153) the two tests above; test_the_vocabulary_an_architect_reads_names_the_domain_and_the_criterion
Whether a declaration is coherent worlds.validate, worlds.slot_warnings application/world.py::check never: they refuse or warn test_world_slots.py::test_the_check_view_carries_the_warnings_without_moving_its_verdict
A change of kind (a grant that becomes something else) worlds.CHANGE_ROLES, PARTICIPANT_ROLE, EFFECT_ROLE gamesystem.changes_of, folded into sheet_of and printed by moves.change_example (§212) the Architect, as a change subject with two roles test_gamesystem.py and test_world_slots.py under §212; the prompt-budget row change_line

The game system

Fact Home The one reader Written by Pinned by
What a system is: ranks, abilities and their needs, forks and their ways, the scale, the columns systems.SystemDef and its parts (Rank, Ability, Need, Choice, Option, Scale, Column) gamesystem.systems_of systems.records_for (§163.2: the scale and digest are minted, never declared by hand) test_gamesystem.py::test_a_system_read_back_out_of_canon_is_the_system_that_was_drawn (the round trip, by equality)
What a legal draw is: the ability, rank, option and scale bounds the bound constants in domain/gamesystem.py (MIN_ABILITIES through MAX_SCALE_MAXIMUM), each with its reason systems.check_draw (complaints about coherence, never a score: §61(5), §105.1) test_check_draw_never_ranks_and_the_module_exposes_no_score; test_seed_completion_bounds.py::test_the_seed_states_the_ability_bound_the_completion_enforces
Where a person starts systems.starting_sheet (derived: the first rung and its openers) the seed and completion_records at world accept (§165.2) test_accepting_a_drawn_system_mints_the_scale_its_own_numbers_imply
Where a person stands now systems.CharacterSheet gamesystem.sheet_of: from the stands_at, can_do and chose edges, never from the snapshot; canon only; one order-key space (§167) systems.records_for_sheet, which writes the edges and the snapshot as one fact test_the_sheets_keys_are_the_systems_own_and_the_declaration_matches_them; test_no_number_describes_the_person
What a person may do next, and what it costs advancement.legal_moves (declaration order), pending_choices, offered_options moves.movables (below) advancement.advance and the four moves it dispatches, all through one private builder so the four cannot drift test_choice_points.py::test_nothing_in_this_module_ranks_an_option; test_a_rise_moves_the_rung_column_and_nothing_else
A system that grew after the seed gamesystem.growth, unfinished_systems (§211, §212.1) application/world.py::check and the Architect's grow step the Architect test_seed_completion_bounds.py::test_the_check_says_a_system_has_grown_and_complains_only_when_the_growth_is_broken

The sheet, the snapshot, and the printed line

Fact Home The one reader Written by Pinned by
Which columns a book prints sheet.Sheet, SheetField, declared under status_sheet sheet.sheet_for: the declaration, else the columns the book's snapshots imply, else nothing (§205); an owner's sheet for an owner (§206); a system's columns as they stand for a sheet that names its system (§211) a drawn system (records_for), world declare, or the first line an undeclared book prints (§205) test_extraction.py::test_a_declared_sheet_round_trips, test_a_book_reads_the_sheet_it_declared_and_not_the_default_one, test_two_sheet_declarations_fall_back_to_the_columns_the_snapshots_hold, test_a_malformed_sheet_declaration_is_refused_rather_than_defaulted
A subject's whole state at a position status_snapshot records (sheet.STATUS_PREDICATE) sheet.snapshot_at chooses which record stands (the protagonist's first, §170; one key space, §165); sheet.state_as_it_stands folds forward across that subject's snapshots (§161) the seed, every advancement, and the extractor test_page_contract.py::test_the_fold_never_crosses_from_one_character_to_another, test_an_advancement_that_restates_nothing_still_renders_a_whole_sheet; test_order_key_spaces.py::test_the_pilot_fifteen_key_set_folds_scene_one_to_the_opening_state
The line as the page prints it sheet.render_status_lineSheet.render, projecting to the held columns (§203) sheet.system_voice_example is the filled line the writer is shown test_extraction.py::test_a_rendered_line_round_trips_through_extraction_itself, test_a_sheet_declared_without_the_flag_shows_every_column_as_it_always_did
What a subject is called on the page names.display_name (§169: the book's own is_a first, the humanised id last, the raw id never) every renderer above, through Sheet.render's resolve canon is_a rows test_display_names.py::test_the_status_line_never_prints_a_raw_subject_id, test_the_printed_name_reads_back_as_the_subject_it_stands_for, test_a_subject_id_that_cannot_round_trip_is_still_never_raw
The graph line (the world announcing a rung or a gain in its own words) graphline.GraphLine, declared under worlds.GRAPH_LINE_PREDICATE graphline.graph_line_for (no default: a book that declares none has no line) the Architect test_extraction.py::test_a_declared_graph_line_round_trips, test_a_printed_rung_on_a_declared_ladder_is_canon_at_that_position
The fork line advancement.offer_line under OFFER_TAG moves.offered_line, with every guard moves.offered_choice has test_two_systems.py::test_two_declared_systems_and_the_one_printing_the_line_is_the_one_read; the prompt-budget row offer_line

Positions

Fact Home The one reader Pinned by
Where a scene sits in story time the book's own evidence: a canon record citing the scene extraction.attested_position, which abstains on ambiguity; extraction.stated_position accepts the planner's key only for a book with no vocabulary of its own (has_story_vocabulary, OWN_POSITION_VERSIONS) test_extraction.py::test_the_obvious_order_key_scheme_is_wrong_and_here_is_the_measurement; test_context_cutoff.py::test_an_undeclared_coordinate_cannot_turn_abstention_into_future_state
The two order-key spaces (a scene's, and the schedule's) state.key_space, state.comparable (§165, §167) state.records_before in memory and litharness_key_space in SQL, tested against each other test_order_key_spaces.py::test_key_space_names_two_spaces_and_refuses_everything_else; test_packet_order_key_spaces.py::test_a_scheduled_record_does_not_pass_a_scene_cutoff

Reading the page back

Fact Home Rule Pinned by
Status lines become snapshots extraction.extract_state mints nothing: the subject must be one canon uses, the position attested or stated, the value verbatim; a partial line is completed from the fold (§203); the first line of an undeclared book is its declaration (§205); an identical restatement is dropped test_extraction.py::test_a_contradiction_fires_the_detector_and_a_repair_silences_it, test_the_record_id_is_value_sensitive_so_a_contradiction_is_a_second_row
Graph lines become proposals, and later reuse promotes them extraction.extract_graph_facts, extraction.promotions a proposal reaches no packet; the one canon-writable shape is a declared subject at a declared rung; promotion is a later scene naming the thing under a different predicate test_a_printed_rung_on_a_declared_ladder_is_canon_at_that_position; test_worlds.py under the graph-line entries
Record identity extraction.record_id_for (value-sensitive), graph_record_id_for (position-sensitive) a replay converges rather than duplicates; two disagreeing readings are two rows the detector can see the two tests above

The moves a scene may be asked for

Fact Home The one reader Pinned by
What a scheduled beat may name as moving moves.movables (two arms, no mode flag: the declared system's legal_moves, else the columns the line holds) and its projections movable_names, moved_values, moved_to genre.beat_text composes the beat; progression.moved_example shows the line after the move (§186); progression.gate_progression checks that the named column moved, never a direction (§184) test_progression_gate.py::test_the_names_a_beat_may_use_are_a_projection_of_the_columns_they_move, test_the_gate_asks_for_a_change_and_never_for_a_direction, test_a_fork_is_never_a_named_quantity_even_beside_a_beat_that_did_fire
Which system prints the line when a book declares two the private guard moves._printing_system (§197) every arm above, through one function test_two_systems.py::test_two_systems_both_declaring_a_sheet_print_the_one_the_person_stands_in
Whether the beat fires at all, and how often genre.beat_ordinals, genre.EVERY, genre.with_beat (§155.3, §157) application/outline.py and planner.render_prompt test_genre_floor.py::test_scene_one_always_carries_a_beat, test_the_cadence_is_regular_after_the_opening

The floor, the house rules, and the leak rail

Fact Home The one reader Pinned by
The genre floor: a book that cannot speak system voice is not drafted genre.has_starting_sheet, genre.system_gap, genre.genre_block the selector, before the spend test_genre_floor.py::test_the_mystery_fixture_is_refused_by_the_floor, test_the_floor_refuses_in_front_of_the_spend, test_a_prose_valued_snapshot_does_not_clear_the_floor
The house rules (the three tiers of §129) house.CLARITY (the floor), house.READER, house.ACCUMULATION, joined as house.HOUSE_RULES house.with_house_rules and house.with_clarity_floor, the only two routes into a system prompt test_architecture.py::test_every_role_that_writes_for_a_reader_carries_the_house_rules, test_the_house_rules_are_not_a_judgment_about_a_story; test_house_genre_promise.py::test_the_house_genre_reaches_a_prompt_as_its_floor_and_never_as_its_name
The words of this system that may never reach a reader house.MACHINERY_WORDS schema_words (names and titles), moves.counted_names and moves._named_moves (beat vocabulary), moves.offered_choice (a fork abstains whole), the concept redraw test_prompt_budget.py::test_a_reader_facing_prompt_never_speaks_in_this_system_s_own_vocabulary, test_the_house_floor_itself_is_reader_facing; test_schema_words.py::test_the_word_list_is_the_one_house_already_owns
How many demands each prompt makes house.demands counts; the ceilings live in the suite tests/test_prompt_budget.py (BUDGET, HOUSE_BUDGET, SCENE_CONDITIONAL_BUDGET, SCENE_MAXIMAL_BUDGET), one row per role with the reason beside every change test_a_role_prompt_stays_inside_its_declared_budget, test_the_maximal_assembled_scene_prompt_stays_inside_its_declared_budget, test_the_scene_floor_row_is_what_the_planner_actually_assembles

The packet, the prompt, and the gate ladder

Concepts are intended book plans. World creation receives Concept.render_for_world, which selects setting, background and mechanics while omitting fields for future scene actions, arc endings and promised answers. The supplied listing remains a promise to support, and the original author brief still constrains world building. Chapter reconciliation receives the chapter and author brief without replaying the treatment. The full concept remains with the planner. This is field routing, not a semantic classifier of arbitrary prose; existing accepted world rules are not automatically reclassified. The boundary is covered by test_future_story_fields_cannot_return_as_world_declaration_material in test_concept.py.

An accepted change's component records depend on its occurrence record's time and visibility, even when the components carry no position themselves. Drafting, planning and repair apply this dependency before projecting prose. Drafting uses an evidence-attested scene coordinate when available; an unknown coordinate admits only unplaced records. The cases are covered in test_change_context.py and test_repair_workflow.py.

Fact Home The one reader Pinned by
What a scene is told about its book domain/context.py::assemble (sections, the summary share, the token budget of §132) application/model_context.py::at_scene applies the position cutoff; extraction.CONFIGURATION_PREDICATES is the one list of rows that configure the telling and never reach a packet (§161) test_context.py::test_the_packet_carries_no_forbidden_item, test_the_packet_never_exceeds_its_budget; test_model_context.py::test_an_unknown_story_coordinate_abstains_instead_of_leaking_future_state; test_planner.py::test_raw_reader_rows_do_not_enter_a_scene_prompt
The scene prompt application/planner.py::render_prompt over packet_for litharness prompts renders every role's prompt for a stored book, which is how a budget is read test_planner.py::test_the_prompt_carries_the_context_packet_and_ends_with_the_instruction; the budget tests above
What happens to a returned draft application/handlers.py::make_scene_draft_handler: strip the em dash and markup, the tells pass (§199), draft.gate_draft (shape, canonical text), the integrity detectors (integrity.CONTRADICTION_RULE, DUPLICATE_RULE, OVERDUE_RULE), progression.gate_progression, then extraction before acceptance the handler; every verdict is a recorded PolicyDecision (domain/policy.py) test_integrity.py::test_contradictory_records_at_one_story_position_are_a_finding, test_a_rerun_over_the_same_duplicate_converges_to_one_finding; test_progression_gate.py::test_a_scene_that_does_not_move_the_named_quantity_is_refused; test_tells_pass.py::test_a_rewrite_that_trades_one_family_for_another_is_refused
The reviser (opt-in since §196) application/reviser.py, domain/reviser.py (the containment band of §185) the handler, one draft in and one revision out (§105.1) test_reviser.py

Persistence and the outside

Fact Home Rule Pinned by
The store adapters/sqlite_store.py (facade) and the capability repositories beside it a migration is never edited; state and its events commit together; state_records slices inside one order-key space with litharness_key_space test_store.py; test_architecture.py::test_dependencies_only_point_outward_to_inward, test_internal_module_graph_has_no_cycles
The model providers/cli.py (the frontier pin, and the two flags that keep CLAUDE.md out of every call: §109) providers/registry.py, bound only in cli.py test_providers.py under LITHARNESS_LIVE_PROVIDERS=1; test_architecture.py::test_the_registry_still_satisfies_the_port_the_application_asks_for
The agent surface (stage-0 §241) mcp_server.py (an entrypoint beside cli.py: READ_TOOLS, PROPOSE_TOOLS, TIERS, FENCE), over application/dossier.py, application/views.py, application/operations.py, status.report and world.view — the one source both surfaces print reads open SqliteStore.open_read_only, writes open_existing; no profile creates, migrates, accepts, spends or posts; the tier table equals the parser test_mcp_server.py::test_no_profile_registers_an_operator_or_paid_verb, test_the_server_exposes_no_accept_tool, test_a_read_tool_opens_the_store_read_only_and_leaves_no_file_behind, test_a_marker_shelf_never_appears_in_a_tool_result, test_the_views_the_server_returns_are_the_dicts_the_cli_prints
A scene's stored text stages application/scene_trace.py, exposed as MCP scene_trace; SqliteStore.read_job_log filters by book, branch and job before decoding payloads joins decision/revision identities; frozen job input is not a provider transport capture; no prose scoring or reconstruction; bounded stage excerpts and explicit missing/withheld evidence test_mcp_server.py::test_scene_trace_reads_frozen_input_in_explicit_bounded_pages, test_events_withhold_raw_candidate_text_without_altering_the_stored_record; test_scene_trace.py

The evaluator boundary and the release queue (stage-0 §221)

Fact Home The one reader Written by Pinned by
Who is reading: a reader with its framing sentence, the two pools, the three specs a read takes domain/audience.py (Reader, StopRule, AudienceSpec, CurrencySpec) application/instrument.py::SimulatedReadership.read; readers.render_choice_request renders the reader's system a pack test_instrument.py::test_a_reader_needs_a_framing_and_a_pool_it_can_be_in
What a domain supplies: the genre set, the framing, the rosters, the stop rule, the tier-3 essays packs/__init__.py (DomainPack, Pack); packs/litrpg and packs/plain packs.select, from the audience's pack_id; only cli.py and the tests name a pack by hand, one module per pack test_architecture.py::test_the_application_reaches_a_pack_only_through_its_protocol; test_instrument.py::test_the_litrpg_readers_render_the_system_text_the_pipeline_has_always_rendered
The record: content-addressed, self-describing, no verdict slot at any depth instrument.Readout (record_id, to_jsonable), Validity, Rail instrument.report the port, once per read test_a_record_refuses_a_verdict_slot_added_later, test_the_report_carries_the_record_hash_and_nothing_the_record_does_not, test_the_validity_block_names_every_rail_and_says_what_it_did_not_do
Which readers a checkpoint panel is frozen over the pack's steering roster, passed to editorial.mechanism_spec_digest, reader_jobs_for_checkpoint and make_reader_observation_handler handlers.make_scene_draft_handler (reader_roster) cli.py composes packs.litrpg in test_editorial.py::test_reader_jobs_freeze_the_request_and_record_exact_provenance
The release queue: one chapter's pastable copy by hash, four states, an operator's name past staged domain/release.py (ReleaseEntry, transition), migration 039 release_queue application/release.py (stage_chapter, approve, record_posted, withdraw); library.write_release_copy writes the copy under its hash the operator, through litharness release — never a program past staged test_release.py::test_the_command_line_has_no_post, test_approval_refuses_when_the_book_moved_under_the_entry, test_a_republish_never_touches_the_staged_copy, test_the_ai_generated_tag_is_required_not_defaulted

Where the load-bearing numbers are

Every numeric constant with a reason beside it is listed by tools/maintainability_survey.py constants, with its home and the first line of its reason; the prompt ceilings are the rows of tests/test_prompt_budget.py; the draw bounds are the constants from MIN_ABILITIES to MAX_SCALE_MAXIMUM in domain/gamesystem.py; the packet budget is context.DEFAULT_TOKEN_BUDGET; the seed's wall-clock ceiling is world_agent.SEED_TIMEOUT_SECONDS. A number copied out of one of those homes into prose is the number to distrust first (PLAN.md header).