simdrive 1.0.0a9 — recording state contracts
Pre-release[1.0.0a9] — 2026-05-11 (alpha — recording state contracts)
Closes a class of replay failure where a divergent app state silently
executed dozens of blind taps. Implements the recording state contract
proposed by the Palace dogfood team
(simdrive/docs/FEATURE_REQUEST_STATE_CONTRACT_2026_05_11.md): every
recording now carries a requires: block captured automatically at
record_start and verified at replay step −1.
The bug a9 closes: in the Palace a8 dogfood, a replay of the
SAML-signin recording against a fresh-install state (with a notifications
permission alert visible at step 0) silently executed 23 taps at SSIM
0.014, reporting ok: true. First tap meant for "Add Library" actually
hit "Don't Allow"; the remaining 22 landed on arbitrary UI. Drift detection
was post hoc — by the time the user knew, the app had been touched 23
times. Third occurrence of this failure mode against Palace; a9 closes it
at the schema level so individual projects no longer have to invent local
workarounds.
Added — state contract on recordings
requires:schema in recording YAML. Captured, not authored. Three
sub-blocks:requires.app.{bundle_id, version, version_match}—version_match
is one ofexact | minor | major | any(defaultminor).requires.sim.{device, ios_version}—ios_versionaccepts a raw
string or a semver predicate (>=18.0,<19.0,!=18.2, etc.).requires.initial_state.{foreground, text_subset_required, text_subset_forbidden, primary_button_label}— captured by OCRing
the step-0 screen via the existingsom.detect_marks()pipeline.
- Auto-population at
record_start— observes the live screen,
captures app bundle/version + sim device/iOS + top text marks. User
doesn't write the contract; the camera does. - Verification at replay step −1 — before any step executes, observes
the live state and compares againstrequires:. On mismatch, halts
withhalt_reason: "state_contract_mismatch",halted_at: 0,
structuredexpected/actual/remedypayload. If a permission
alert is detected inactual.text_subset_present(heuristic on
"Don't Allow" / "Allow" / "OK" / "Cancel"), theremedyfield points
atxcrun simctl privacy ... grant ...rather than generic advice. - New
replayparameter:halt_on_state_mismatch(defaultTrue).
Today's "run anyway" behavior is now opt-in by passingFalse— a
warning is still emitted in the response. - Deprecation warning for unannotated recordings: recordings without
arequires:block replay with_simdrive_warning: "Recording has no \requires:` block. State contract not verified. Run
`simdrive migrate-recording ` to capture one."` Existing
recordings continue to work.
Added — lint + migrate tooling
simdrive lint-recordings [--path DIR] [--quiet] [--json]CLI
command. Walks the directory tree, prints[OK]/[FAIL]per
recording, exits non-zero on any FAIL.--jsonfor programmatic
consumption (CIverify-pr.shintegration).simdrive migrate-recording <name> [--force] [--dry-run]CLI
command. Re-OCRs the step-0pre_screenshotof a pre-a9 recording,
builds aRequiresBlock, writes the YAML back in place with a
.pre-migrate.baksibling for recovery.- Both also exposed as MCP tools (
lint_recordings,migrate_recording)
for agentic / CI consumers. Tool count: 30 → 32.
Internal
- Refactored
_capture_state_contractto share_build_requires_block
(pure transform onmarks + app/sim metadata) with
migrate_recording— capture-time and migrate-time paths produce
identical RequiresBlock shapes by construction. robustness.validate_replay()now tolerates the newrequires:
top-level key (was: would have rejected as unknown field).- One e2e test (
test_replay_halts_on_drift_when_screen_diverges)
updated to opt out of the new step-0 contract check — it deliberately
diverges state to test the per-step drift path, which a9.0 would
otherwise short-circuit.
Tests
- 38 new tests across a9.0 (17) + a9.1 (21).
- Suite total: 1.0.0a8 baseline 711 → 1.0.0a9 749 passing, 1 skipped.
Known (carried forward from a8)
- D6 (empty
idevicesyslogoutput on real devices) still investigation-
only — awaiting Palace dogfood capture of
idevicesyslog -u <udid> 2>&1for root-cause selection. See
simdrive/docs/D6_LOGS_INVESTIGATION.md.