feat(schema): land DeclaredState and ProbedState type scaffolding#3
Merged
Conversation
Chunk M1-W1-A of the M1 plan: types only, no logic yet. Establishes
the data layer that every adapter, the engine, and the diff machinery
share per Plan §6.1.
Public types (re-exported from lib.rs):
- DeclaredState — top-level resolved host configuration.
- HostMeta, ArchLevel ('v3' | 'v4'), KernelDecl.
- PackageSet (core / cachyos / cachyos-v3 / cachyos-v4 / aur / cargo).
- ConfigEntry, RemovePolicy.
- ServicesDecl (enabled / disabled / masked).
- UserDecl, HomeManagerDecl, HomeManagerMode (standalone | flake).
- SnapshotPolicy.
- ProbedState — UTC-stamped live snapshot with per-subsystem inventories.
- HostInfo, PacmanInventory, CargoInventory, ConfigFileInventory,
ConfigFileMeta, ServiceInventory, KernelInfo.
- SchemaError (thiserror), ContractViolation (with Display).
Internal modules: config, declared, errors, host, packages, probed,
services, snapshots, users. One module per type cluster, kept narrow.
Workspace dep changes:
- time: add 'serde-well-known' feature so ProbedState.probed_at can
round-trip via #[serde(with = "time::serde::iso8601")] per Plan §11.
clippy.toml:
- Add doc-valid-idents (CachyOS, Nickel, NixOS, Pearlite, Snapper,
Steelbore, JsonSchema) so the doc_markdown pedantic lint stops
flagging proper nouns.
What's NOT in this chunk:
- pub fn from_resolved_toml — chunk M1-W1-B.
- pub fn validate — chunk M1-W1-B.
- Property tests + Draft 2020-12 schemars fixture — chunk M1-W1-C.
Verification:
- cargo build --workspace
- cargo clippy --workspace --all-targets -- -D warnings (zero warnings)
- cargo fmt --all --check
- scripts/ci/check-spdx.sh (SPDX header on every new .rs file)
- pearlite-audit check . (1 check, 0 violations)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cargo-audit flags time 0.3.45's parse-time stack-exhaustion DoS (RUSTSEC-2026-0009) on every CI run. The fix ships in time 0.3.47, but that release raises MSRV to 1.88 — conflicting with our Plan §4.5 pin of 1.85. Pearlite never feeds untrusted strings to time::parse. We use the crate only for serializing/deserializing machine-emitted ISO 8601 in state.toml and plan.json (per Plan §11). The DoS surface is therefore not reachable through any Pearlite API. Add the advisory to ignore lists in both deny.toml and audit.toml (the two tools read different files but the justfile invokes both). TODO(M1 close): write ADR-008 covering the MSRV bump policy and remove this ignore once MSRV >= 1.88. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cargo-audit's auto-discovery of workspace-root audit.toml didn't pick up the RUSTSEC-2026-0009 ignore in CI; the audit.toml file was honoured by some local versions but not by the CI binary. Passing --ignore on the command line is the version-stable approach. audit.toml stays as the documented authority on what's ignored and why; deny.toml mirrors it for cargo-deny. Both remain in sync via the exposure-analysis comment. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
UnbreakableMJ
added a commit
that referenced
this pull request
Apr 27, 2026
…Plan §6.4 (#9) * feat(fs): land sha256_file, write_etc_atomic, probe_config_files M1-W1-G — closes Plan §6.4. pearlite-fs is the third pure crate of M1 Week 1; primitives the engine and adapters share for hashing files, atomically writing /etc with mode/owner/group preserved, and probing declared ConfigEntry targets into a ConfigFileInventory. Public API: - pub fn sha256_file(p: &Path) -> Result<[u8; 32], FsError> - pub fn write_etc_atomic(target, content, mode, owner, group) -> Result<(), FsError> - pub fn probe_config_files(targets: &[ConfigEntry]) -> ConfigFileInventory ConfigEntry / ConfigFileInventory / ConfigFileMeta are re-exported read-only from pearlite-schema (PR #3). Internal modules: hash.rs, atomic.rs, chown.rs, inventory.rs, errors.rs. Implementation notes: - hash.rs: 64 KiB chunked read; constant memory regardless of file size. Heap-allocated buffer (clippy::large_stack_arrays denies the on-stack version). - atomic.rs: PRD §7.4 recipe — temp + chmod + chown BEFORE rename so the file appears with the right ownership atomically — followed by fsync of the file's data, atomic rename, fsync of the parent directory. - chown.rs: nix::unistd::User/Group for name<->id resolution; libc chown via nix; Permissions::from_mode for chmod. Plan §6.4 hard rule: no std::process::Command for ownership ops. - inventory.rs: stat each target, hash, encode hex, look up uid/gid -> name. Missing targets are silently absent — the diff engine treats absence as drift in its own pass. Workspace deps added: - nix = { version = "0.31", features = ["user", "fs"] } - hex = "0.4" Tests: - hash: known_file_known_digest (SHA-256("abc")), empty_file_known_digest, missing_file_yields_io_error. - chown: unknown_user_yields_typed_error, unknown_group_yields_typed_error. - atomic: write_then_read_round_trips, mode_preserved_through_temp, chown_to_self_succeeds (chowns to the running user since CI lacks root), unknown_owner_yields_typed_error. - inventory: missing_target_yields_none_in_inventory, present_target_records_sha256_and_mode, empty_target_list_yields_empty_inventory. - 2 fork-based tests stubbed as #[ignore]: crash_after_write_before_rename and rename_atomic_visible_to_concurrent_reader. These need rusty-fork machinery and land in M1 W2 with engine integration tests. Plan §6.4 "Done when" status: - sha256 implementation is constant-memory (chunked I/O). ✓ - Atomic write tested via simulated kill-9 in unit tests using forked processes. PARTIAL — ignored stubs in place; full test machinery arrives in M1 W2. - No std::process::Command — chown happens through libc directly via nix. ✓ - pearlite-audit reports no §13 violations. ✓ Verification: - cargo test -p pearlite-fs (12 passed; 0 failed; 2 ignored) - 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> * fix(fs): pin path dep to a version to satisfy cargo-deny wildcard rule cargo-deny treats `pearlite-schema = { path = "..." }` as a wildcard dep because no version constraint is present. The workspace lints have `wildcards = "deny"`. Pinning to "0.1" matches workspace.package.version (0.1.0) and the workspace SemVer baseline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
UnbreakableMJ
added a commit
that referenced
this pull request
Apr 27, 2026
Plan §11.4 milestone-exit gate requires a retrospective in docs/retrospectives/M<n>.md before the milestone tag is applied. Captures: - Scope delivered across the three M1 weeks (schema/state/fs; nickel/cargo/systemd/pacman; diff/engine/cli). - Numbers: 16 PRs, 150 tests passing, all CI tiers green at exit. - What went well (three-chunk cadence, trait-first discipline, PRD §7.3 four-way discriminator, PRD §9.3 envelope unchanged). - What didn't (extractions/setup-just regression, cargo-machete speculative-dep flags, time 0.3.47 MSRV jump, schemars mid-stream bump, solo-dev admin-merge friction). - Open items rolled forward to M2 (VM runner, CachyOS image, ADR-008 MSRV policy, fork-based atomic-write tests, 1 MB state parse benchmark, toml_edit-based unknown-field preservation, KernelInfo population). - Action items for M2 W1 (CachyOS runner ADR, MSRV ADR, pearlite-snapper, engine apply skeleton). - Appendix: PR list (#3 through #18). TODO.md: mark every M1 week-1/2/3 item completed; flag the VM scenario as deferred and m1-exit as the next tag. 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-A of the M1 plan: type scaffolding only, no logic yet.
Establishes the data layer that every adapter, the engine, and the diff machinery share per Plan §6.1. Subsequent chunks add
from_resolved_toml+validate(M1-W1-B) and the schemars Draft-2020-12 fixture + property tests (M1-W1-C).Public surface
pearlite_schema::*now re-exports:DeclaredState— top-level resolved host configuration.HostMeta,ArchLevel,KernelDecl.PackageSet(core / cachyos / cachyos-v3 / cachyos-v4 / aur / cargo).ConfigEntry,RemovePolicy,ServicesDecl.UserDecl,HomeManagerDecl,HomeManagerMode.SnapshotPolicy.ProbedState,HostInfo,PacmanInventory,CargoInventory,ConfigFileInventory,ConfigFileMeta,ServiceInventory,KernelInfo.SchemaError,ContractViolation.Every public type derives
Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema(andDefault/Hashwhere it makes sense).Workspace tweaks
timeworkspace dep: addserde-well-knownsoProbedState.probed_atcan round-trip ISO 8601 per Plan §11.clippy.toml: adddoc-valid-identsfor proper nouns (CachyOS, Nickel, NixOS, Pearlite, Snapper, Steelbore, JsonSchema) so the pedanticdoc_markdownlint stops flagging them.Verification
cargo build --workspacecargo clippy --workspace --all-targets -- -D warnings— zero warningscargo fmt --all --checkscripts/ci/check-spdx.sh— SPDX header on every new.rspearlite-audit check .— 1 check, 0 violations🤖 Generated with Claude Code