Simulation - Implementation Checklist¶
Package: mineproductivity.simulation
Governing specification: docs/architecture/09_Simulation_Design_Specification.md
Architecture Decision Record: docs/adr/ADR-0009-Simulation.md
Status: Not started
Binding, locked implementation contract for simulation - the fourth package built on top of the Foundation Layer, sitting directly above the now-locked digital_twin. Nothing described here may be implemented before this checklist and its governing specification exist in reviewed form, and nothing may be implemented that is not represented by an item on this list. Complete in order; every box must be checked or explicitly deferred with a linked issue and Chief Software Architect sign-off before merge.
Pre-Implementation Gate¶
- Design specification (
09_Simulation_Design_Specification.md) read in full by the implementer, including every cross-reference to specs 01–08. - ADR-0009 read in full; the rationale for
simulationexisting as a separate package abovedigital_twin(and for the interface-only treatment of Monte Carlo, discrete-event, system-dynamics, and calibration methodologies) is understood, not merely accepted. -
core,events,ontology,registry,plugins,connectors,kpis,analytics,decision,digital_twinavailable and importable, exactly as released; no lower package file is modified as a side effect of this work. - Confirmed:
simulationwill not importoptimization,agents, orvisualizationunder any circumstance - none of those packages exist yet (design spec §5, §37). - Confirmed: no lower package (
corethroughdigital_twin) will be modified to import or otherwise referencesimulation(design spec §5).
Package Structure¶
-
src/mineproductivity/simulation/created matching design spec §6 exactly:abstractions.py,metadata.py,scenario.py,run.py,state.py,clock.py,replay.py,montecarlo.py,discrete_event.py,system_dynamics.py,calibration.py,executor.py,experiment.py,comparison.py,sensitivity.py,discovery.py,persistence.py,caching.py,result.py,_registry.py,exceptions.py,__init__.py,README.md. -
simulation/README.mdwritten following thecore/README.mdtemplate. - Confirmed
montecarlo.py,discrete_event.py,system_dynamics.py, andcalibration.pyeach contain zero concrete, non-test subclasses ofMonteCarloModel/DiscreteEventModel/SystemDynamicsModel/CalibrationModel(mechanical grep/AST check - design spec §13–§16, §35's interface-purity proof). - Confirmed no module under
src/mineproductivity/simulation/performs direct KPI, statistical, decision, or twin-state computation of its own - every such value arrives via the corresponding lower package's public API (design spec §3.2, §35's no-fact-recomputation proof). - Confirmed
comparison.py/sensitivity.pycontain zero direct mean/percentile/correlation arithmetic - every such computation is a call intoanalytics(design spec §35's no-statistics-reimplementation proof).
Public API¶
-
simulation/__init__.pyexports exactly the symbol list in design spec §7, alphabetized__all__. -
test_public_api.pymirrorstests/unit/core/test_public_api.pyand every existing package's own copy of it. -
TestNoForbiddenDependenciesAST-walks everysimulationsubmodule for a forbidden import (optimization,agents,visualization) - mirrors every existing package's own copy of this test (design spec §5). - A second, reverse-direction test asserts no file under
src/mineproductivity/{core,ontology,events,registry,plugins,connectors,kpis,analytics,decision,digital_twin}/importsmineproductivity.simulation(design spec §5) - theanalytics/decision/digital_twin-package precedent for this test extended one layer up.
Simulation Abstractions (§8)¶
-
SimulationModel(§8) -meta: ClassVar[SimulationMetadata]; deliberately no shared abstract execution method. -
SimulationContext(§8) -event_store,kpi_results,analytics_results,decision_results, each defaulting to an empty sequence. - Confirmed
SimulationModelsubclasses (of every category) are stateless - no instance attribute is mutated by any category method (§29, §32). -
CalibrationModel(§16) confirmed not aSimulationModelsubclass anywhere in the codebase.
Scenario Management (§9)¶
-
ScenarioStatusenum (§9) - exactlyProposed/Active/Superseded/Retired. -
Scenario(§9) - frozencore.BaseValueObject;code,version(default"1.0.0"),status,model_code,parameters,time_horizon,initial_state(TwinSnapshot | None),as_of(AsOf | None);validate()rejects an emptycodeormodel_code. - Confirmed an
ActiveScenariois never edited in place anywhere in the codebase - a changed scenario is published as a new version, with the prior version transitioned toSuperseded(§9, §25, §34's recorded anti-pattern). -
ScenarioConflictErrorraised for a materially-different re-registration under an existing,Activescenario code without a version bump (§9, §25).
Simulation Execution (§10)¶
-
RunStatusenum (§10) - exactlyScheduled/Running/Paused/Completed/Failed. -
SimulationRun(§10) - subclassescore.BaseEntity[str]directly;scenario_code,state: SimulationState,status: RunStatus;with_state()non-overridden, produces a new instance viadataclasses.replace, never mutatesself. - Identity/equality proven:
SimulationRun.__eq__/__hash__inherited unchanged fromBaseEntity(identity-based onid, ignoringstate/status); no override anywhere in the package. - Lifecycle transitions proven to match design spec §10's state diagram exactly;
CompletedandFailedproven terminal (no transition out of either). -
SimulationExecutor(§6, §10) - dispatches to the registeredSimulationModel's category-specific method (_trial/_advance/_step) based on theScenario.model_code's registeredSimulationCategory, never by branching on the model's concrete Python type; advancesSimulationClock; persists the resulting state via aremove-then-addpair againstSimulationRunRepository. -
SimulationExecutor's dispatch and persistence sequence tested against design spec §10's sequence diagram exactly, including the cache-hit and cache-miss seeding paths (§26).
Time Progression and Event Replay (§11, §12)¶
-
TimeProgressionModeenum (§11) - exactlyFixedTimestep/NextEvent/TrialBased. -
SimulationClock(§11) -mode, optionaldt;advance(current, *, delta=None); confirmed to hold no dependency on any specificSimulationModelcategory. -
seed_from_replay()(§12) implemented as a thin wrapper overevents.EventStore.replay(as_of); confirmed no second replay mechanism exists anywhere in the package. -
seed_from_replay()proven to reconstruct the identicalSimulationStatea hand-computed fold over the same event history would produce (§12, §35).
Monte Carlo, Discrete Event, System Dynamics, Calibration Interfaces (§13–§16)¶
-
MonteCarloModel(§13) -_trial(scenario, *, context, random_seed) -> SimulationResultabstract; zero concrete subclasses shipped. -
DiscreteEventModel(§14) -_advance(state, *, context) -> tuple[SimulationState, timedelta]abstract; zero concrete subclasses shipped. -
SystemDynamicsModel(§15) -_step(state, *, context, dt) -> SimulationStateabstract; zero concrete subclasses shipped. -
CalibrationModel(§16) -_calibrate(model_code, ground_truth, *, context) -> Mapping[str, Any]abstract;ground_truthtyped asSequence[TwinSnapshot]; zero concrete subclasses shipped. - Confirmed no concrete
MonteCarloModelimplementation anywhere in the test suite holds mutable random-number-generator state across_trialcalls - every trial's randomness derives solely from the suppliedrandom_seed(§33, §34's recorded anti-pattern). - Reproducibility proven mechanically: identical
random_seedinputs toMonteCarloModel._trialproduce identical outputs, verified across every registered Monte Carlo model in the test suite (§35's reproducibility proof).
Experiment Management (§17)¶
-
Experiment(§17) - frozencore.BaseValueObject;name,scenario,run_ids: tuple[str, ...]. -
ExperimentRunner(§17) - composesSimulationExecutorfor each individual run rather than duplicating its dispatch/persistence logic;run_trials(scenario, *, trials, context) -> Experiment. - Confirmed independent
SimulationRuns (differentids) execute fully in parallel with no contention;ExperimentRunnerdispatches trials concurrently in the reference implementation (§33). - The design spec §17 worked example (500-trial Monte Carlo experiment seeded from a
digital_twin.TwinSnapshot) reproduced end-to-end as an integration test.
Simulation Outputs, Scenario Comparison, Sensitivity Analysis (§18, §19, §20)¶
-
SimulationResult(§18) - frozencore.BaseValueObject;run_id,computed_at,warnings,final_state. -
ExperimentResult(§18) - subclassesSimulationResult; addstrial_results: tuple[SimulationResult, ...]; confirmed to perform no statistical characterization oftrial_resultsitself. - Confirmed
SimulationStateis not aSimulationResultsubclass (it represents the run's condition itself, not the outcome of an orchestration call about it). -
ScenarioComparator.compare()(§19) implemented; confirmed every comparison delegates toanalytics.describe()/StatisticalSummary- no mean/percentile computation exists in this package's own code. -
SensitivityAnalyzer.sweep()(§20) implemented; confirmed every sweep hands itsSimulationResults toanalytics'DistributionSummary/confidence_intervalfor distributional treatment - no correlation/regression computation exists in this package's own code. - Delegation tests assert the actual
analyticsprimitive invoked byScenarioComparator/SensitivityAnalyzer(e.g.describe), never re-derive the expected statistic independently inside the test (§35).
Simulation Registry and Discovery (§21, §22)¶
-
simulation._registry.REGISTRY/register(§21) -Registry[str, type[SimulationModel]], raisingSimulationValidationErrorfor an empty code andSimulationVersionConflictErrorfor a materially-different re-registration under an existing code. -
EntryPointSpec(group="mineproductivity.simulation", target_registry="simulation")discovery wired viaregistry.EntryPointDiscovery(§31). -
by_category()/by_scope()(§22) - plaincore.PredicateSpecificationfactories; composed withSimulationRunRepository.list(); confirmed to return an empty sequence (never raise) for a filter matching nothing. - Confirmed the three-way distinction (
REGISTRY= which model types are known;SimulationRunRepository= which run instances currently exist;discovery.py= query facade over the instance store) is never conflated anywhere in the codebase (§21).
Serialization and Persistence (§23, §24)¶
- Every
SimulationState/Scenario/SimulationResultsubclass andSimulationRunitself confirmed to serialize viacore.serialization(DataclassSerializer/to_dict) with no bespoke per-type serializer. -
SimulationRunRepositoryimplemented astype SimulationRunRepository = BaseRepository[SimulationRun, str]- a type alias, not a new ABC or subclass. - Reference implementation uses
core.InMemoryRepository[SimulationRun, str]()directly, with zero new persistence code. - Test suite for
SimulationRunRepositorybehavior written against thecore.BaseRepository[SimulationRun, str]contract alone, never againstInMemoryRepository-specific internals (§35's repository-substitutability proof).
Versioning (§25)¶
-
SimulationMetadata.version(a registered model type's own SemVer) andScenario.version(a governed configuration artifact's own SemVer) confirmed to vary independently - no code path derives one from another. -
SimulationVersionConflictErrorraised at registration time for a materially-different re-registration under an existingSimulationMetadata.code;ScenarioConflictErrorraised at publication time for the equivalentScenariocase - both never deferred.
Caching (§26)¶
-
SimulationStateCache.get()/put()(§26) implemented, keyed by(scenario_code, as_of). - Confirmed not a reuse of
kpis.ResultCacheordigital_twin.TwinStateCache- cache key shape independently designed for replay-seeded simulation state, not KPI-result or twin-evidence semantics. - Confirmed a cache miss (
get()returningNone) never raises and always falls back toseed_from_replay()directly. - Confirmed
SimulationExecutor/ExperimentRunnernever treatSimulationStateCacheas authoritative for "what is this run's current state" - onlySimulationRunRepositoryis.
Validation (§27)¶
-
SimulationMetadata.validate()- non-emptycode, category matches the closedSimulationCategorynamespace. -
Scenario.validate()- non-emptycodeandmodel_code. -
SimulationState.validate()- non-emptyattributes. - Each
SimulationModelcategory subclass's own namespace-convention check implemented.
Error Handling (§28)¶
- Full exception hierarchy (design spec §6
exceptions.py):SimulationValidationError,SimulationRunNotFoundError,SimulationExecutionError,SimulationVersionConflictError,ScenarioConflictError- each subclassing the matchingcoreexception. - Confirmed no
SimulationModelcategory method raises for a legitimately incomplete input (e.g. zero trials requested) - returns aSimulationResult/Experimentcarrying a warning instead.
Metadata (§29)¶
-
SimulationCategoryenum (§29) - exactlyMonteCarlo/DiscreteEvent/SystemDynamics/Calibration; a closed enum, adding a member is a governance-reviewed change. -
SimulationMetadata(§29) -code,category,description,version(default"1.0.0");validate()rejects an emptycode. - Confirmed
SimulationMetadata.codenames a model type and is never confused with aSimulationRun.idanywhere in the codebase.
Thread Safety & Concurrency (§32, §33)¶
-
SimulationModelinstances (of every category) confirmed stateless and safe to share/read across threads with no locking. -
SimulationRuninstances confirmed immutable and safe to share/read across threads with no locking. -
SimulationRunRepository's per-id write serialization contract documented and tested against any production-grade implementation candidate; confirmed the barecore.InMemoryRepositoryreference implementation provides no locking of its own - any concurrent-write test against it must add external synchronization itself. -
simulation.REGISTRYconfirmed read-only and thread-safe after startup discovery, inheritingRegistry's own contract. - Independent
SimulationRuns (differentids) proven to execute fully in parallel without contention; concurrent trials for the same run proven to serialize correctly (no lost update) once a conforming productionSimulationRunRepositoryis used. -
SimulationStateCachereads/writes keyed by(scenario_code, as_of)proven non-contending across independent keys.
Tests¶
-
tests/unit/simulation/mirrorssrc/mineproductivity/simulation/1:1. - Coverage ≥95%.
- Unit tests per concrete model category - at least one flagship model per category, each against a scripted scenario with a known expected
SimulationStatetrajectory (§35). - Reproducibility tests, identity/equality tests, event-replay seeding tests, delegation tests, registry/discovery isolation tests, interface-only ABC contract tests, and concurrency stress tests as enumerated in design spec §35.
- The six package acceptance proofs in design spec §35 (no-fact-recomputation, no-statistics-reimplementation, immutability, interface-purity, no-architectural-drift, reproducibility) each independently verified and recorded in the PR description.
Documentation¶
-
simulation/README.mdcomplete. - Every registered
SimulationModeltype's docstring restates itsSimulationMetadata.descriptionfor source-level readability.
Examples¶
-
examples/simulation/01_monte_carlo_experiment.py- the design spec §17 worked example (500-trial experiment seeded from aTwinSnapshot), end-to-end. -
examples/simulation/02_scenario_comparison.py-ScenarioComparatorcomposed over two named scenarios' trial outcomes. -
examples/simulation/03_sensitivity_sweep.py-SensitivityAnalyzer.sweep()over a singleScenarioparameter. -
examples/simulation/04_plugin_simulation_model.py- a third-party-styleSimulationModelcategory subclass registered via entry points, mirroringexamples/registry/01_register_and_discover.py's pattern. - All examples pass
mypy --strict+ruff.
Benchmarks¶
-
SimulationRunRepository.get()/list()latency at representative run-population scale, recorded inbenchmark/reports/simulation/. -
SimulationStateCachehit-rate and time saved across a representative Monte Carlo experiment's repeated trials, recorded.
Certification¶
- Design spec §35's six package acceptance proofs pass and are recorded in the PR description (duplicated here from Tests for merge-gate visibility).
Type Hints, Mypy, Ruff, Coverage¶
- 100% type-hinted;
mypy --strictclean. -
ruff checkandruff format --checkclean. - Coverage report attached; ≥95%.
Release¶
-
CHANGELOG.mdupdated. - Root README dependency diagram cross-checked - confirm no forbidden import (
optimization,agents,visualization) was introduced, and confirm no lower package gained a newsimulationimport. - Version bump proposed and reviewed.
- Design spec §35's acceptance proofs re-verified as final merge gate.
Derived from 09_Simulation_Design_Specification.md. Keep in sync with the governing specification and with ADR-0009-Simulation.md.