Rust Phase 4 native project reader/writer slice

Status: accepted native product slice.

Decision and boundary

ProjectDocument owns the exact UTF-8 bytes of one .kicad_pro JSON object and an insertion-ordered JSON value. Exact writes preserve the source unchanged. Canonical writes use KiCad's two-space indentation and trailing newline while preserving authored key order and JSON number spelling. This avoids cross-language float shortening such as rewriting 0.049999999999999996 to 0.05.

ProjectView promotes the settings currently used by native compilation and PCB workflows: text variables, schematic variants, net classes/assignments/patterns/colors, differential-pair dimensions, tuning defaults, and arbitrary read-only dotted paths. The existing schematic bundle subpart settings now read through this shared model rather than a second private JSON DTO.

The owned writer supports transactional text-variable set/remove, variant add/remove/rename, and typed JSON dotted-path replacement. Every successful mutation is bounded, serialized, and reparsed before commit; stable no-ops retain the original exact source. Unknown project fields and their insertion order survive mutations.

Resource, dependency, and safety behavior

Input admission reads at most max_source_bytes + 1. Exact writes reject over-limit owned source before publishing bytes. Canonical serialization uses a bounded writer and never retains output beyond max_output_bytes. A non-allocating structural preflight bounds JSON value nodes and container depth before the generic Serde DOM is materialized. The caller's depth ceiling governs through 512 nested containers, the library's explicit hard safety cap; Serde's lower default recursion ceiling is disabled only after that iterative preflight. Typed projection independently bounds text variables, variants, net classes, assignments, patterns, colors, differential-pair presets, and aggregate copied string bytes. Outer netclass assignments and their nested class references have independent ceilings.

The implementation uses the workspace's existing serde and serde_json dependencies. The enabled preserve_order and arbitrary_precision features add the standard indexmap/hashbrown support needed for observable KiCad/Python canonical parity; unbounded_depth lets the explicitly bounded preflight own the recursion policy. The structural preflight is not a JSON parser: Serde remains authoritative for syntax and materialization. The crate continues to forbid unsafe code.

Invalid UTF-8, malformed JSON, non-object roots, invalid paths, mutation conflicts, I/O errors, and resource exhaustion use a structured project error. Candidate mutations are isolated from the owned document until the complete output reparses successfully.

Evidence and retained work

Rack case L1_034 requires all 217 project files and 3,048,297 bytes from the content-addressed package corpus. For every file, native Rust proves an exact source write, stable canonical second write, complete canonical hash equality with Python, and equality of every promoted typed field. The gate fails if the archive/extraction is incomplete and batches process launch arguments to remain portable on Windows.

A separate mutation gate compares native output byte-for-byte with Python after text-variable, variant, and dotted-path changes while retaining unknown JSON. Focused Rust tests cover independent limits, ordered serialization, transactional conflicts/no-ops, malformed input, and I/O.

Project-folder creation and orchestration remain downstream workflow concerns. Schematic mutation and the project-bundle-to-netlist/compiled-graph obligations are covered by the accepted Phase 4 exit under separate governed surfaces. Native plotter and Cruncher behavior are governed by later accepted phases and do not widen this project reader/writer boundary.