recon_gen.common.spine.training
Training/docs scenarios that self-validate.
The audit’s “training scenarios become declarative (and can’t lie)” payoff (docs/audits/date_range_model_audit.md): today, docs prose describes what a demo scenario shows, but the prose ⟷ data link is developer-memory. If the seed changes shape and the planted drift moves off the documented day, the docs silently lie. AS.7 closes that gap.
TrainingScenario is the typed object docs can import + the test suite can validate:
name / description — author-facing prose; renderers (mkdocs, walkthroughs) read these.
emitters — the ViolationGenerator / LedgerSimulation rows the scenario plants.
invariants — the detectors the scenario claims will fire.
intended — the specific `Violation`s docs say the analyst will see. The claim that has to hold.
self_validate(conn) emits the scenario, refreshes matviews, runs detect across each invariant, and asserts intended ⊆ detected. If the docs claim violations the data doesn’t produce, the test fires loud — the documented scenario can’t silently fail to demonstrate.
What this REPLACES (eventually): hand-written prose in docs/handbook/ that lives parallel to the seed; the docs walk ASSUMED to match the data. With TrainingScenario, the docs mkdocs-macros can render scenario.description next to a mkdocs-side scenario.intended summary, and a CI test runs scenario.self_validate(conn) against the live seed every build.
AT.6 reuses this mechanism for L2’s anomaly + money_trail scenarios (parallel structure, different surface).
Functions
|
Validate a batch of scenarios, each against its OWN fresh DB. |
Classes
|
A docs-renderable, self-validating scenario. |
- class recon_gen.common.spine.training.TrainingScenario(name, description, emitters, invariants, intended=<factory>, prefix='spec_example', instance_path=None)[source]
Bases:
objectA docs-renderable, self-validating scenario.
Authors construct one of these per documented case; docs read name + description (free prose); the test suite calls self_validate(conn) to make sure the data the scenario emits actually produces the violations the prose claims.
intended is the load-bearing field — it’s the claim. A Violation in this set says: “the analyst will see this row in the matview after the scenario applies.” Implementation churn (different leg shapes, account IDs) is fine as long as the intended Violations still fire; that’s the same flexibility AS.5’s semantic_lock gives.
- Parameters:
- description: str
- emitters: tuple[_Emitter, ...]
- instance_path: Path | None = None
- name: str
- prefix: str = 'spec_example'
- self_validate(conn)[source]
Apply the scenario; assert every intended Violation fires.
Raises AssertionError with the missing-Violation diff if the docs claim violations the data doesn’t produce.
intended ⊆ detectedis the contract — extra detected Violations (e.g., the secondary ledger_drift edge from a drift plant) are fine; missing claimed Violations are the failure mode.- Return type:
None- Parameters:
conn (SyncConnection)
- recon_gen.common.spine.training.validate_all(scenarios, conn_factory)[source]
Validate a batch of scenarios, each against its OWN fresh DB.
conn_factory is a no-arg callable returning a fresh sqlite3. Connection with the schema already applied (the in-process harness pattern). Each scenario gets its own connection so prior emissions don’t bleed into the next test’s detect set.
Useful in a docs-build hook: collect every registered TrainingScenario and validate them all in one shot before rendering the prose. A failure halts the build with the missing- violation diff.
- Return type:
None- Parameters:
scenarios (Iterable[TrainingScenario])
conn_factory (Callable[[], SyncConnection])