Purpose
The iOS runner writes snapshot quality state as a free Swift String, while the host accepts only healthy | recovered | sparse. A typo or one-sided addition can make the host drop the verdict and its disclosure. The contracts annotation reader is looser still: it casts any string state to the verdict type.
Implementation plan
- Next to
SnapshotQuality in RunnerTests+SnapshotCapturePlan.swift, define SnapshotQualityState: String, Codable, CaseIterable with exactly .healthy, .recovered, .sparse. Use it for SnapshotQuality.state, stampedSnapshotPayload(state:), every producer in that file and RunnerTests+Snapshot.swift, and the sparse/truncation comparison. Raw-value Codable keeps the JSON values unchanged. Keep reasonCode open.
- In
packages/kernel/src/snapshot.ts, replace the repeated handwritten state union with one exported readonly state tuple and derive SnapshotQualityVerdict['state'] from it. Export a small state predicate from that owner. Have both packages/capture-kit/src/snapshot-quality-verdict.ts and packages/contracts/src/snapshot-capture-annotations.ts use it; do not import capture-kit into contracts or retain a second accepted-state set. The annotation reader must reject an unknown state rather than type-cast it. Keep its other projection behavior unchanged.
- Add
contracts/fixtures/ios-snapshot-quality-states.json containing only the three wire strings. Add a Swift runner unit test comparing fixture order to SnapshotQualityState.allCases.map(\.rawValue) and a TS test comparing the fixture to the kernel tuple. Reuse the existing fixture-path pattern in RunnerTests+SnapshotBackendCapabilitiesTests.swift; do not add a generator or a larger schema for a three-value vocabulary.
Proof and completion
- Before changing production code, demonstrate red on a one-sided extra/renamed fixture state, and red on a malformed annotation state. Keep focused tests for all three raw JSON values and for unknown-state rejection in both TS readers.
- The runner compiles with no verdict
state: String or state string literals in producers, and existing encoded verdict tests retain the same wire JSON. Run the focused Swift runner unit tests and pnpm check:affected --run; report exact commands/results.
- Limit the change to the verdict vocabulary, the shared fixture, and their existing tests. No new compatibility parser, warning channel, or reason-code registry.
Purpose
The iOS runner writes snapshot quality
stateas a free SwiftString, while the host accepts onlyhealthy | recovered | sparse. A typo or one-sided addition can make the host drop the verdict and its disclosure. The contracts annotation reader is looser still: it casts any string state to the verdict type.Implementation plan
SnapshotQualityinRunnerTests+SnapshotCapturePlan.swift, defineSnapshotQualityState: String, Codable, CaseIterablewith exactly.healthy,.recovered,.sparse. Use it forSnapshotQuality.state,stampedSnapshotPayload(state:), every producer in that file andRunnerTests+Snapshot.swift, and the sparse/truncation comparison. Raw-valueCodablekeeps the JSON values unchanged. KeepreasonCodeopen.packages/kernel/src/snapshot.ts, replace the repeated handwritten state union with one exported readonly state tuple and deriveSnapshotQualityVerdict['state']from it. Export a small state predicate from that owner. Have bothpackages/capture-kit/src/snapshot-quality-verdict.tsandpackages/contracts/src/snapshot-capture-annotations.tsuse it; do not import capture-kit into contracts or retain a second accepted-state set. The annotation reader must reject an unknown state rather than type-cast it. Keep its other projection behavior unchanged.contracts/fixtures/ios-snapshot-quality-states.jsoncontaining only the three wire strings. Add a Swift runner unit test comparing fixture order toSnapshotQualityState.allCases.map(\.rawValue)and a TS test comparing the fixture to the kernel tuple. Reuse the existing fixture-path pattern inRunnerTests+SnapshotBackendCapabilitiesTests.swift; do not add a generator or a larger schema for a three-value vocabulary.Proof and completion
state: Stringor state string literals in producers, and existing encoded verdict tests retain the same wire JSON. Run the focused Swift runner unit tests andpnpm check:affected --run; report exact commands/results.