---
id: simulation-semantics-version-contract
depends_on: [simulation-compute-provenance, mcp-country-comparison-applied-assumptions]
area: backend
description: Make assumptions_version a persisted and governed result-semantics contract instead of a duplicated response-time constant.
---

## Goal

Restore `assumptions_version` as a useful reproducibility boundary. The same
`2026-06-23.v1` literal is currently duplicated in MCP and REST code and is attached at response
time even though country coverage, comparison transforms, and result materialization have changed.
New runs must carry the version of the semantics under which they were accepted and computed;
legacy runs must not be relabeled with today's version.

## Version Policy

Keep the existing field name for compatibility, but define it as the version of observable
simulation-result semantics. A bump is required when a change can alter numeric output for the
same normalized input, changes a server-applied default or transform, changes a tax/COL/country
policy registry, or changes the meaning/materialization of persisted result facts used by a
consumer. A bump is not required for copy-only edits, telemetry, performance changes proven
result-equivalent, or adding provenance without changing the represented result.

At implementation cutover, publish a new version (at least `2026-08-11.v1`) whose release note
names the expanded country registry, country-comparison assumption receipts/COL semantics, and
compact sweep-point materialization. Later implementations use their actual cutover date and
increment the suffix for multiple semantic releases on one date.

## Acceptance Criteria

- [ ] One backend version manifest owns `assumptions_version`, its effective timestamp, and a
      short machine-readable list of included semantic changes. MCP discovery, enqueue metadata,
      REST serializers, workers, and tests import that authority; no second production literal is
      allowed.
- [ ] Every newly enqueued simulation, sweep, grid, comparison, and deterministic replay that
      performs worker compute persists the current manifest version before that compute begins.
      `get_run_result`, `wait_for_run`, run history, shares, and MCP provenance return the run's
      stored version rather than the process's current version.
- [ ] Responses add `assumptions_version_state` with exact values `declared` or
      `legacy_unverified`. A row with no persisted version is returned as
      `assumptions_version: null, assumptions_version_state: legacy_unverified`; it is never
      assigned the current version during serialization.
- [ ] Runs from the known ambiguous `2026-06-23.v1` production window are not silently certified.
      They retain the stored string for auditability but return
      `assumptions_version_state: legacy_unverified`; new cutover-version runs return `declared`.
- [ ] Non-compute derivations—creating a share, saving a scenario from a run, copying an existing
      result artifact, or returning an idempotent enqueue hit—preserve the source run's version
      and state. A deterministic replay that actually performs new worker compute is a new run:
      its `assumptions_version` is the current enqueue-time version, while separate
      `source_assumptions_version` and `source_assumptions_version_state` provenance fields retain
      the source run's receipt (including `legacy_unverified`). No operation relabels the source
      run itself.
- [ ] `get_supported_inputs` returns the current manifest version, effective timestamp, and change
      list so a caller can distinguish the current contract from a stored legacy run.
- [ ] A documented CI guard covers the governed semantic modules and registries and fails when
      their tracked fingerprint changes without a new version-manifest entry. Tests also reject
      duplicate `assumptions_version` literals in production modules.

## Test Plan

- Unit-test the central manifest format, governed fingerprint, duplicate-literal guard, and all
  response builders that previously owned a local constant.
- Enqueue and complete a run, then switch the process's current manifest in a test fixture. Assert
  reads, share creation, scenario saving, artifact copies, and idempotent enqueue hits still return
  the persisted original version. Assert a newly computed deterministic replay records the new
  current version as its own receipt and the original run's version/state in the separate source
  fields.
- Seed a run with no version and a run from the ambiguous `2026-06-23.v1` window; assert the exact
  `legacy_unverified` contracts without a backfilled current value.
- Cover simulations, sweeps/comparisons, deterministic replay, and worker failure so successful
  and failed terminal artifacts retain the enqueue-time version.
- Add an MCP contract test comparing `get_supported_inputs` (current manifest) with an older
  stored run (persisted manifest) and asserting both are reported independently.

## Out of Scope

- Recomputing old runs, claiming exact reproducibility for runs that lack a stored random seed,
  or inferring an old version from today's code.
- Replacing `compute_provenance`; deploy SHAs remain the code-level receipt and complement this
  semantic contract.
- Versioning purely presentational frontend layout or prose.

## Danger Zones

- Do not mutate historical rows merely to make the response look complete. Missing provenance is
  an observable legacy state.
- Capture the version before enqueue and copy it through retries. Reading a module constant only
  when the worker finishes can mix API and worker deploys during a rolling release.
- The fingerprint guard is a tripwire for review, not a substitute for judgment. Its governed set
  must include simulation math, validation/default registries, tax/COL policies, and result
  materializers without treating unrelated logging code as semantic.

## Agent Notes

- The current duplicate literals are `api/mcp/router.py:ASSUMPTIONS_VERSION` and
  `api/routers/runs.py:_MCP_ASSUMPTIONS_VERSION`.
- Reuse the persistence/copy patterns established by `simulation-compute-provenance`; do not fold
  the semantic version into deploy SHA or mark old `compute_provenance.state=legacy` as declared.
