feat(state): migrate framework, MockFs, proptest — closes Plan §6.2#8
Merged
Merged
Conversation
Chunk M1-W1-F of M1 plan — closes Plan §6.2. Migration framework (migrate.rs): - pub fn migrate(State) -> Result<State, StateError> bumps the schema_version field forward through one transformation per on-disk version. v0 → v1 is the only step at M1; future bumps add another `migrate_vN_to_vM` and one extra branch. - StateStore::read calls migrate so the read path automatically upgrades older files in memory before returning to the caller. - schema_version field gains #[serde(default)] so pre-versioned alpha files (no schema_version key) deserialize as 0 and migrate to 1. - 3 unit tests: v0_to_v1_adds_schema_version, current_version_is_a_no_op, future_version_rejected. MockFs (mock.rs): - Compiled in cargo test (no feature) and behind feature = "test-mocks" for downstream consumers (the engine's integration tests in M2+). - Backed by Arc<Mutex<BTreeMap<PathBuf, Vec<u8>>>>. - Failure injection via fail_next_rename() and fail_next_fsync_dir(), both self-clearing single-shots. - 4 own tests: seed_then_read_round_trip, read_missing_yields_not_found, injected_rename_failure_leaves_old_content, fail_injection_is_single_shot. - Module-level allow(clippy::expect_used) for the standard Mutex<T> unwrap idiom — MockFs is test-utility code. StateStore tests using MockFs (store.rs): - rename_failure_via_mockfs_leaves_old_state — atomic-write guarantee verified through the trait, not through a tempdir. - fsync_dir_failure_via_mockfs_propagates — error path. Property test (store.rs, proptest): - arbitrary_state_payload_round_trips_through_mockfs — randomized host, tool_version, managed.pacman/cargo, adopted.pacman survive a write→read cycle losslessly. Plan §6.2 acceptance: "property: arbitrary State round-trips losslessly (proptest-driven)." Cargo.toml: - New [features] block with test-mocks = []. - proptest = "1" added to [dev-dependencies]. Plan §6.2 "Done when" status: - Triple-fsync atomic write tested via MockFs failure injection. ✓ - toml_edit preserves [reserved] and any unknown fields verbatim. PARTIAL: [reserved] is preserved (BTreeMap<String, toml::Value>). Byte-level preservation of unknown fields outside [reserved] would require a toml_edit-based writer; deferred — the [reserved] namespace covers the documented forward-compat contract. - schema_version migration framework exists; trivial v0→v1 stub passes a fixture. ✓ - No std::fs::* calls outside io.rs. ✓ - pearlite-audit reports no §13 violations. ✓ - cargo bench shows full read+parse of a 1 MB state.toml under 50 ms. DEFERRED: a benches/ harness lands as a follow-up after the engine is up and we have a realistic 1 MB fixture. Verification: - cargo test -p pearlite-state (18 passed; 0 failed) - cargo clippy --workspace --all-targets -- -D warnings (zero warnings) - cargo fmt --all --check - scripts/ci/check-spdx.sh - pearlite-audit check . (1 check, 0 violations) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Chunk M1-W1-F of M1 plan — closes Plan §6.2.
What lands
Migration framework (`migrate.rs`)
`pub fn migrate(State) -> Result<State, StateError>` bumps schema_version forward one step at a time. v0 → v1 is the only transformation at M1. `StateStore::read` calls `migrate` automatically; `schema_version` is now `#[serde(default)]` so pre-versioned alpha files (no `schema_version` key) deserialize as 0 and upgrade to 1.
`MockFs` (`mock.rs`)
Compiled in `cargo test` (no feature) and behind `feature = "test-mocks"` for downstream consumers (engine integration tests in M2+). `Arc<Mutex<BTreeMap<PathBuf, Vec>>>` backing; `fail_next_rename()` / `fail_next_fsync_dir()` for single-shot failure injection.
Property test (proptest)
`arbitrary_state_payload_round_trips_through_mockfs` — randomized host, tool_version, managed.pacman / cargo, adopted.pacman survive write→read losslessly. Plan §6.2's "arbitrary State round-trips" gate.
Plan §6.2 Done when status
Verification
🤖 Generated with Claude Code