feat: NTFS full-volume capture, offline parity validation, and tooling#535
Merged
Conversation
Design for capturing a complete NTFS metafile set (MFT + $Secure/$UsnJrnl/ $Bitmap/$LogFile/$Boot/$UpCase/...) into a verifiable, transferable bundle: two-phase live+VSS-frozen model (no read-only remount), a `capture` subcommand as the home, a rust-script VSS/zip wrapper, and the `sysinfo` host-probe that gates best-effort by OS class (client/server). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add `uffs-mft sysinfo`: a cross-platform host + storage provenance probe that runs before a capture and records the machine it ran on. - Host: OS name/build/arch, client/server InstallationType (the VSS/shadow discriminator), elevation, VSS availability, CPU/RAM, timezone, tool version. - NTFS drives (Windows): media type, geometry, $MFT stats, and per-drive capture-capability flags (iocp_capturable, vss_eligible). - Volumes (all OS): every mounted filesystem with type / format / size / used%, collapsing shared-container volumes (e.g. synthetic APFS mounts). - Derives the best-effort capture mode (full-server / best-effort-client / live-only / unsupported); also useful as a benchmark provenance stamp. Text report + `--json` (Windows). Windows OS identity via a small read-only RegGetValueW reader (new Win32_System_Registry feature); other probes reuse existing safe wrappers. Split into capture_mode/report/windows/unix submodules. Verified: host + cargo-xwin Windows clippy clean, 8 unit tests, macOS run. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First frozen-metafile capture target. Adds a self-describing capture format (`MetafileHeader`: magic/version/kind/drive/serial/timestamp/size) and a `uffs-mft metafile --drive C --kind boot -o C_boot.bin` command that reads $Boot (the 8 KiB volume boot region at offset 0) via the broker-safe read_handle_at primitive and writes header + payload. - New `platform::metafile` module (mirrors `platform::upcase`): MetafileKind, MetafileHeader (to_bytes/from_bytes), read_metafile (Windows) + non-Windows stub, save/load_metafile_to/from_file. - `metafile` subcommand + MetafileKind CLI value-enum; dispatch on Windows, "requires Windows" elsewhere. - 6 cross-platform header round-trip/validation tests. Foundation for the remaining targets ($Bitmap, $Secure:$SDS, $AttrDef, ...), which reuse the same header/format via data-run reads. Verified: host + cargo-xwin Windows clippy clean, tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…1.2) Add the keystone data-run stream primitive and the first target that uses it. - read_frs_record: read a raw MFT record (any FRS) with USA fixup. - read_data_stream: resolve a metafile's non-resident $DATA attribute (unnamed, or a named stream like $SDS) and assemble its data runs. - read_runs: read data runs into a data_size-truncated buffer; sparse runs stay zeroed. Reuses the broker-safe read_handle_at. - $Bitmap (FRS 6): the volume cluster-allocation bitmap, captured via the unnamed non-resident $DATA stream. `metafile --kind bitmap`. The named-stream support lands $Secure:$SDS next with no new I/O code. Verified: host + cargo-xwin Windows clippy (pedantic/nursery) clean, tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Capture the NTFS security-descriptor store via the named-stream path added in P1.2 (no new I/O code): $Secure (FRS 9), named $DATA stream $SDS. This is the ACL / owner / DACL-SACL store that the $MFT namespace only references by SecurityId. `metafile --kind secure`. Verified: host + cargo-xwin Windows clippy (pedantic/nursery) clean, tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… (P1.4) Complete the frozen-metafile target set: - $AttrDef (FRS 4) and $MFTMirr (FRS 1): unnamed non-resident $DATA streams, via the P1.2 stream reader. - $Volume (FRS 3) and $BadClus (FRS 8): their useful data lives in the MFT record itself ($VOLUME_NAME/$VOLUME_INFORMATION; the $Bad run list), so the fixed-up record is captured via read_frs_record. `metafile --kind attr-def|mft-mirr|volume|bad-clus`. With $Boot/$Bitmap/$Secure and the existing `save --upcase`, the complete metafile set is now capturable. Verified: host + cargo-xwin Windows clippy (pedantic/nursery) clean, tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add `uffs-mft capture --drive C --out <dir>`: capture every NTFS metafile for a
drive into one hashed, manifested bundle under <out>/drive_<x>/.
- Writes each metafile ($Boot/$Bitmap/$Secure/$AttrDef/$MFTMirr/$Volume/
$BadClus) via the P1.1-P1.4 metafile module, best-effort (a metafile that
can't be read is skipped and noted, not fatal).
- manifest.json: schema, drive, captured_at, tool version, volume facts
(serial/ntfs-version/cluster+record size), and per-artifact
{file, kind, frs, bytes, sha256}.
- SHA256SUMS for transfer verification.
sha2 + hex added to the Windows dep set for the manifest hashing. Verified:
cargo-xwin Windows clippy + host pedantic clean, tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Capture the NTFS metadata transaction log: $LogFile (FRS 2), an unnamed non-resident $DATA stream, via the P1.2 stream reader (no new I/O code). Wired into `metafile --kind log-file` and the `capture` bundle. This is the crash-consistency / replay artifact. The remaining temporal artifact, $UsnJrnl:$J, needs $Extend directory-index traversal to resolve its FRS and lands separately. Verified: host + cargo-xwin Windows clippy (pedantic/nursery) clean, tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…(P2) Capture the NTFS change journal, the one metafile not at a fixed FRS: - resolve_extend_child: walk the $Extend (FRS 11) directory index to resolve a child's FRS by name — parses $INDEX_ROOT entries, and falls back to the $INDEX_ALLOCATION INDX blocks (with USA fixup via apply_usa_fixup) when the index has spilled. - read_usn_journal: resolve $UsnJrnl's FRS, then read its named non-resident $DATA stream $J (sparse; the active tail) via the P1.2 stream reader. - MetafileKind::UsnJrnl uses a reserved sentinel kind code (0xF5) since its FRS is dynamic; wired into `metafile --kind usn-jrnl` and the `capture` bundle. - scan_index_entries is a pure parser with a cross-platform unit test (synthetic $INDEX_ROOT buffer → resolves a child FRS). Completes the NTFS metafile capture set: $MFT + $Boot/$Bitmap/$Secure/$AttrDef/ $MFTMirr/$Volume/$BadClus/$LogFile/$UsnJrnl (+ existing $UpCase). Verified: host pedantic + cargo-xwin Windows clippy clean, tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`uffs-mft capture` now writes the primary artifact — the LZ4-compressed $MFT (<x>_mft.compressed.bin, via MftReader::save_raw_to_file) — alongside the metafiles, so the bundle is self-sufficient (MFT + all metafiles + manifest + SHA256SUMS). Refactored the per-artifact loop into collect_artifacts/capture_mft to keep cmd_capture simple. Verified: host pedantic + cargo-xwin Windows clippy clean, tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update the full-capture plan status/phasing to reflect what landed: sysinfo, all metafile save targets ($Boot..$UsnJrnl via $Extend traversal), and the capture orchestrator/manifest incl. the compressed $MFT. Remaining: P3 (VSS --volume-path), P5 (rust-script VSS/zip wrapper), P6 (full offline load). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ecode (P6) The offline reconstitute/validate side of the capture flow — cross-platform, so it runs on the transfer target (macOS/Linux) as well as Windows. - platform::metafile::parse_boot: decode volume geometry (bytes/sector, sectors/cluster, cluster + MFT record size, total sectors, MFT LCN, serial) from a captured $Boot payload via NtfsBootSector. - platform::metafile::summarize: human summary of a captured metafile (header + kind-specific detail). - `uffs-mft metafile-info --input <file>`: load a captured metafile and print the summary. Dispatched on all platforms. - Real cross-platform unit test: synthetic 512-byte boot sector → decoded geometry (runs on macOS). Verified end-to-end on macOS against a synthetic $Boot metafile. This closes the loop: capture on Windows → transfer → `metafile-info` on the Mac reproduces the same decode. Verified: host pedantic + cargo-xwin Windows clippy clean, tests green, macOS e2e run. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… (P6) - parse_bitmap: decode cluster-allocation stats (total/used/free clusters) from a captured $Bitmap payload by popcounting the allocation bitmap; folded into summarize / `metafile-info` for offline free-space validation (vs fsutil). - Extract the pure offline decoders (BootGeometry/parse_boot, BitmapStats/ parse_bitmap, summarize) into a new cross-platform `platform::metafile_decode` module — the $Bitmap addition pushed metafile.rs over the file-size limit, so this is the real fix (metafile.rs 826 → 661 LOC), no exception. Cross-platform + unit-tested; verified end-to-end on macOS. host pedantic + cargo-xwin Windows clippy clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
parse_usn decodes a captured $UsnJrnl:$J payload — skips the leading sparse region, walks USN_RECORD_V2/V3 records (8-byte-aligned), and returns a record count, USN range, and a small sample (usn / reason / filename). Folded into summarize / `metafile-info` so the offline machine can inspect the change flow (the temporal dimension) without the live volume. Cross-platform + unit-tested (synthetic USN_RECORD_V2); verified end-to-end on macOS. host pedantic + cargo-xwin Windows clippy clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update the capture plan: metafile-info + parse_boot/parse_bitmap/parse_usn offline decoders (cross-platform, Mac-verified) are in. Remaining: $Secure/ $Volume/$AttrDef parsers, verify_parity wiring, and the Windows-only P3 (VSS --volume-path) / P5 (rust-script VSS+zip wrapper). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`[super::volume::read_handle_at]` (volume is #[cfg(windows)], absent in the macOS doc build) and `[MetafileHeader]` failed `cargo doc -D warnings` at pre-push. Both were illustrative — demote to plain code spans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Raw volume ReadFile requires sector-aligned length, but read_runs clamped the final read to the attribute's exact data_size — which for $Bitmap on a 64 KB- cluster volume is not sector-aligned (bitmap size = ceil(clusters/8)), so the read failed with ERROR_INVALID_PARAMETER (os error 87). Now allocate the whole cluster-aligned span the runs cover, read each run in full (aligned), and truncate to data_size afterwards. $LogFile/$AttrDef worked before only because their sizes happened to be aligned. Found by the first real Windows e2e run (C: 64 KB clusters). Needs a re-run to confirm $Bitmap now captures. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
$Secure:$SDS and $UsnJrnl:$J live in extension records referenced by $ATTRIBUTE_LIST when the base MFT record overflows, so the base-only reader missed them. Add a pure, testable attribute_list_data_frs() parser and fall back to reading the extension record(s) for the named non-resident $DATA stream, merging runs in VCN order. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… data FSCTL_GET_NTFS_VOLUME_DATA only returns the NTFS version in the NTFS_EXTENDED_VOLUME_DATA block that trails NTFS_VOLUME_DATA_BUFFER, so ntfs_major_version/ntfs_minor_version were hardcoded to 0 and every capture reported "ntfs:0.0". Query with a combined base+extended buffer and read MajorVersion/MinorVersion when NTFS filled the extended block (graceful 0.0 fallback on systems that don't). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AttributeRef::name() was a stub that decoded the UTF-16 name and then unconditionally returned None (it could not safely return a borrowed &[u16] from unaligned record bytes). Every named-stream match therefore failed, so $Secure:$SDS and $UsnJrnl:$J were never captured — the real root cause behind the earlier $ATTRIBUTE_LIST work, which relied on this broken primitive. Return an owned Vec<u16> instead and lock it with a decode test. Also make read_runs' buffer allocation fallible: a sparse stream like $UsnJrnl:$J can span a huge logical size, so surface a clean error rather than aborting on an infallible vec![0; capacity]. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
$UsnJrnl:$J is a sparse file whose logical size can reach ~150 GB while the live journal is a tiny allocated tail (the purged prefix is a deallocated hole). The full-span reader hit the "too large to materialize" guard and skipped it. Add read_runs_sparse_compact, which reads only the non-sparse runs concatenated — each USN record is self-describing, so dropping the leading hole loses no data. Share run-resolution (base record + $ATTRIBUTE_LIST extensions) via resolve_data_runs and the fallible allocation via alloc_zeroed. Split the live-volume readers out of metafile.rs into a new platform/metafile_read.rs (metafile.rs kept the on-disk format + save/ load); the growing reader set pushed metafile.rs to the 800-line gate. read_metafile is re-exported so callers are unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A USN is the record's byte offset in the journal, so it is always positive and 8-byte aligned. The sparse-compact $J capture can leave stale/padding bytes after the live tail that pass the RecordLength + MajorVersion checks, producing a bogus record (observed last_usn 281479271678612, misaligned). Reject records whose USN is non-positive or not 8-aligned, and bound RecordLength / require MinorVersion==0. Locked with a misaligned-tail test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Capture every eligible NTFS volume in one run, each into its own out/drive_<x>/ subfolder. --drive is now optional (required only for the single-drive path); per-drive failures are reported and skipped so the run continues, and the command errors at the end if any drive failed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pack each captured drive directory into a single <dir>.tar.zst (extractable with `tar --zstd -xf`), optionally split into --split-gib GiB parts (<dir>.tar.zst.NNN) for transfer. No new dependency: add a small, unit-tested ustar writer + byte splitter (src/archive.rs) and compress with the existing zstd. Works per-drive, so it composes with --all-drives (each volume archived independently, bounding memory). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Compare two MFT CSV exports (from `load`) — Rust-on-Windows vs Rust-on-macOS, or Rust vs a C++ golden — to tie down the capture flow. Rows are projected onto key columns matched by header name (so column order may differ), canonicalized as a multiset, and diffed; the command exits non-zero on any divergence so it drops into scripts. Adds a pure, unit-tested parity module (RFC 4180 field splitter, name-based column projection, multiset diff) and the cross-platform `verify --left A.csv --right B.csv [--columns ...]` command. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
End-to-end runbook for the capture flow: capture (single / --all-drives / --zip --split-gib), transfer + SHA256SUMS verification, offline metafile-info inspection, and the three-way parity check (CPP golden vs Rust-on-Windows vs Rust-on-Mac) via load → verify. Plus a CLI cheat-sheet and the sparse-$UsnJrnl / CSV-quoting caveats. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
capture wrote c_mft.compressed.bin, which verify_parity.rs --regenerate (and test_runs.ps1) don't recognize — they look for <DRIVE>_mft.bin / .iocp. Rename the captured MFT to <DRIVE>_mft.bin so new captures drop straight into the existing offline parity harness alongside old ones. Still zstd-compressed on disk; load_raw_mft auto-detects the flag and decompresses, so the .bin name is correct. Runbook updated to match. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nsport After each live per-drive compare, --bundle captures the MFT + metafiles (uffs-mft capture) into the same drive dir as the cpp/rust outputs, then zips it (PowerShell Compress-Archive) into drive_<x>.zip. The zip carries the MFT, all metafiles, the manifest, and both tools' outputs, ready for offline --regenerate on the Mac. uffs-mft is auto-detected (~/bin or target/release) or set via --mft-bin. Bundling implies --keep. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PowerShell Compress-Archive silently fails past ~2 GB, so on large drives (e.g. S:, whose <DRIVE>_mft.bin exceeds 2 GB) the bundle reported success but wrote no zip. Replace the capture + Compress-Archive two-step with a single `uffs-mft capture --zip` (ustar + zstd, no size limit), which packs the whole drive dir into drive_<x>.tar.zst, and verify the archive actually exists (never trust the exit code alone). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ustar writer hardcoded mtime=0, so extracting a bundle produced 1970-dated files — and the offline parity flow derives its --tz-offset from the baseline file's mtime, which that broke. push_entry now takes an mtime; capture reads each file's modified time so extraction restores it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Offline mode now discovers drive_<x>.tar.zst transport bundles in the base dir, temp-expands each (tar --zstd -xf, or plain tar -xf which libarchive auto-detects) into a drive_<x>/ dir, validates it via the normal --regenerate path, and removes the expansion afterward — so a dropped-in bundle is validated without keeping an uncompressed copy. Skips expansion when a drive_<x>/ dir already exists. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…drive load) Offline --regenerate ran `uffs --mft-file <drive>` via autospawn, but a daemon left resident from an earlier drive/session served its stale in-memory index instead — so a --drive M run hit a daemon still holding C: (327ms "scan", no verify_rust_m.txt, then FATAL not-found). --no-cache only skips the cache file, not the hot daemon. Kill the daemon + purge caches (cross-platform) before invoking uffs, mirroring the live mode's cold-start, so only the requested drive's MFT is loaded. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
classify_live_skew tolerated a directory's Size and Descendant-count aggregates as live-read skew but not Size-on-Disk (col 4), which is equally a subtree aggregate. So when Norton populated the S:\$AV_NLL quarantine vault between the cold Rust scan and the warm C++ scan, the vault dir's size/size-on-disk/descendants all moved; Size + Descendants were tolerated but Size-on-Disk tripped a false "real diff", leaving 2 entries that flipped S: from SUPERSET MATCH to MISMATCH. Tolerate Size-on-Disk for directories too. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Offline --regenerate under-counted the root's size-on-disk by the reserved-cluster bytes (total_reserved × cluster_size) because the sequential .bin MFT format doesn't persist reserved_allocated_bytes (only the IOCP format does), so it loads as 0. On drive M this was the single 52 MB (13,327-cluster) root diff. Capture now writes the value from volume data into reserved_allocated.txt, which verify_parity.rs --regenerate already consumes via --reserved-allocated so the root matches the live/C++ baseline. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
--status shelled out to `ps` twice (process list + parent-name lookup) via Command::output() with no timeout, so a process with a huge argument vector (or one stuck in uninterruptible I/O) made `ps` block and hung the whole command. Route both through a shared capture_with_timeout helper (2s watchdog on a helper thread; kills the child and reports "process scan timed out — skipped" on timeout). Require the real `--mcp` flag when matching sessions so build tooling (cargo/sccache compiling uffs-mcp) is not listed. Split the MCP-stdio scan into a process_scan submodule to stay under the file-size gate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`just use-local` rebuilt the workspace and installed to ~/bin but left a running daemon/MCP holding the old binaries — a stale in-memory index could shadow the fresh build. Add a step 0 that runs `uffs --daemon kill` + `pkill -x uffsd uffsmcp` before building. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The offline root Size-on-Disk was short by the reserved-cluster bytes (total_reserved × cluster_size) because the sequential .bin format never carried the value (only IOCP did) and the --reserved-allocated flag is a no-op stub. Bump RawMftHeader to v3 with a reserved_allocated_bytes field, thread it through SaveRawOptions + the streaming writer, populate it in capture from volume data, and restore it on load — including load_raw_to_index_direct, the loader the daemon actually uses for --mft-file. Validated on the Mac: G: offline flips MISMATCH → SUPERSET MATCH (root only-in-baseline 1 → 0). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`extract-mft --input C_mft.bin --output C.mft` decompresses a captured UFFS-MFT .bin and writes the byte-exact $MFT records — the format analyzeMFT / MFT2CSV / ntfstool consume. Cross-platform, so a .bin moved to a Mac yields a tool-ready $MFT with no re-capture. Validated: G_mft.bin → 20224 records × 1024 B, every record carrying the FILE magic. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New advisory RUSTSEC-2026-0204: invalid-pointer dereference in crossbeam-epoch's fmt::Pointer impl for Atomic/Shared (transitive via crossbeam-deque). Bump to the patched 0.9.20. Delta audited safe-to-deploy: a soundness fix that removes the unsafe deref (prints the raw address instead), a benign sanitizer-cfg build.rs, no new runtime deps or ambient capabilities, publisher taiki-e (crossbeam maintainer). Vet-Reviewed-Diff: crossbeam-epoch@0.9.18->0.9.20 Co-Authored-By: Claude Opus 4.8 <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.
Builds the NTFS full-volume capture + offline reconstitution/validation system in
uffs-mft, plus supporting CLI/tooling and a security dep bump. Validated end-to-end on real Windows captures replayed offline on macOS.Capture (
uffs-mft capture)$MFT(compressed) + all 10 NTFS metafiles ($Boot, $Bitmap, $Secure:$SDS, $AttrDef, $MFTMirr, $Volume, $BadClus, $LogFile, $UsnJrnl:$J), hashedmanifest.json+SHA256SUMS.--all-drives,--zip/--split-gib(ustar+zstd, no PowerShell 2 GB limit).$UsnJrnl:$Jsparse-compact reader;$ATTRIBUTE_LISTfollowing for named$DATAstreams; sector-aligned data-run reads; NTFS version from extended volume data..binheader carriesreserved_allocated_bytes, restored on load so an offline parse reproduces the live root size-on-disk.<DRIVE>_mft.binnaming for drop-in compatibility with the existingverify_parityflow.Offline tooling (cross-platform)
metafile-info— decode a captured metafile ($Boot geometry, $Bitmap free space, $UsnJrnl summary).verify— canonical MFT-CSV parity (column-name matched, multiset diff).extract-mft— pull the byte-exact raw$MFTout of a.binfor analyzeMFT / MFT2CSV / ntfstool.sysinfo— capture-host probe.verify_parity.rs (parity harness)
--live --bundle(capture + zip transport), offline auto-expand ofdrive_<x>.tar.zst, daemon reset before--regenerate, dir size-on-disk skew tolerance, real mtimes in archives.Fixes
uffs --statuscan no longer hang (boundedpscalls);just use-localstops daemon/MCP first.Validation
Offline replay on macOS: D/E/G/M/S clean SUPERSET/SORTED MATCH against the C++ golden (root reserved diff fixed by the v3 header); C/F are the inherently-churning drives. Docs:
docs/architecture/mft-full-capture.md,docs/user-manual/mft-capture-verify-runbook.md.🤖 Generated with Claude Code