Files
ARCADIA-Studio/harness/analysis-source8-20260913/analysis-source-save-guard-independent-review-20260913.md
T

47 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Independent review: S0 source-save containment
Reviewed exact Git blobs at `112e0d910bd2b2c9a191f29536fecb0fbc0177b0`, parent `847b1caa7e75bb82f6ce5193c45bcfc45973ad1a`. The author's changing worktree/S1 source was not used. No compilation, tests, application or tracked edits were performed. Findings below are source conclusions, not executed results.
**No actionable production correctness defect found within S0's guard-only scope.** The change prevents unreopenable or lossy generated source from reaching filesystem staging. It does not complete source8 or make this branch ready for ordinary source-save acceptance. One bounded public-entrypoint test coverage gap remains worth closing alongside S1; it does not invalidate the inspected guard implementation.
## Verified guard ordering and scope
At `persistence.rs:935–957`, public `save_checked` passes the existing DOCUMENT profile and unchanged depth-admission flag through `save_checked_profile` to the new production helper, supplying the real `language::format`. Thus the test seam contains the actual production guard, not a separate test-only reconstruction.
At `969–1000`, the helper validates the requested Project, selects source using the existing case-insensitive `.arcadia` extension rule, generates bytes, appends a newline only when missing, and passes those exact final bytes to public `decode_with_report_limited`. The effective byte limit is `profile.bytes` (DOCUMENT64MiB; smaller test profiles retain their exact limit). Other ParseLimits stay unrestricted because save_checked has no caller-supplied graph/record policy; S0 does not invent one. It then requires the decoded **entire Project** to equal the requested Project and refuses any migration report. The equality includes the new definitions catalog; no UUID mask, selected-field comparison or formatter-derived model expectation is used.
All those operations precede parent resolution and `NamedTempFile::new_in` at1006, writes/sync at1007–1008, conflict inspection at1009 and persist at1017. Decoder/validation failures return their existing typed error; lossy-output and unexpected-migration cases return distinct Invalid errors. There is no public decoder→save recursion introduced by this path. A generated source larger than the byte cap is rejected before decoding/IO, although the existing formatter still constructs its String before that cap check: this is not a new bounded-output allocator or a proof of arbitrary formatter stack/memory safety.
The `depth_128` parameter continues to govern the JSON encoder. Source admission deliberately uses the public reader's production setting rather than the private candidate-depth override, matching the requested public save/reopen invariant. `json_codec::DEPTH_128_VALIDATED` remains false; no deeper-profile acceptance is implied. Existing source parser nesting constraints and formatter behavior remain separate from this small guard.
## Sentinel evaluator assessment
The four new tests in `persistence/source_save_tests.rs` call `save_checked_profile_with_source`, injecting formatter output into the **same guard used by production**. They do not call the outer public `save_checked` function or exercise its real formatter output. This distinction should remain explicit in any test-result summary.
The evaluator is meaningful: each case tests an existing sentinel destination, a new destination and a destination under a missing parent; it requires the intended non-IO error, unchanged root membership, exact sentinel bytes and absence of new output. The missing-parent case is especially useful: moving NamedTempFile creation ahead of decoding would yield Io and fail the expected-error assertion even if temporary-file Drop would otherwise hide creation in the existing-parent cases. Inspection confirms no other filesystem effect precedes the guard.
- Malformed output (`arcadia`) checks public parser refusal, rather than manufacturing an arbitrary error.
- The lossy-output case adds a valid unused QuantityKind definition while injected source preserves familiar ID/name/elements and omits the definition. Full equality must reject it with the specific loss error. This catches an implementation that compares only the old graph fields. The equality check intentionally precedes migration refusal, so a schema7 decoded candidate can expose catalog loss distinctly.
- The equal-project legacy case reaches the separate migration refusal. Ordinary source7 decoding yields the current-schema8 Project with a migration report; equal meaning does not excuse emitting a legacy source representation.
- The newline test puts the unterminated UTF-8 source exactly at the byte cap, forcing the appended newline to exceed it. A preterminated version at the new exact cap reaches migration refusal instead, catching either checking before append or appending twice.
**Coverage note, not a demonstrated production bug:** none of these new sentinel checks protects the outer public call wiring against a later bypass of the shared helper. The existing public regressions at `review_regressions.rs:454,472,490` are useful, but all iterate `.arcadia` before JSON and currently unwrap the first source save. At S0 they are expected to stop at that first save rather than reaching their JSON/conflict cases. Consequently neither passing these four new tests nor the still-failing public regression establishes current JSON/conflict execution coverage. The smallest later check is one public save_checked end-to-end invariant/sentinel case using the real formatter, plus separate existing JSON/conflict cases so a source failure cannot skip them. After S1, retain the fault-injection tests and run the ordinary public save/update/no-op/conflict/deletion regression with source and JSON both observed. Do not weaken the public regression to accept permanent source failure.
## JSON, conflicts and integration limitation
The only production diff moves existing save logic into the private shared helper and adds the source-only check. JSON still calls the same encoder with the same profile/depth arguments. Existing newline, temporary write/sync, exact-byte conflict matching, missing-file Conflict and persist code are unchanged. Source admission now takes precedence over inspecting a stale destination; that is necessary for containment and preserves the external bytes, but it means S0 cannot demonstrate source Conflict for an unreopenable formatter output. This is source-based invariance, not a test pass.
At this exact commit, `language::format` writes schema8 while the language parser still accepts only3–7 (`language.rs:1410`). Therefore ordinary `.arcadia` saves remain unavailable, now safely returning an error before a destination is created/replaced. The previously executed847 regression failed after writing unreadable source; S0 is expected to fail its first unwrap instead. That changed failure position is a **prediction until executed**, and is containment, not repaired source8 round-trip capability. S1 must supply the complete current source representation/parser before normal source saving can be accepted. No JSON128 or broader analysis acceptance follows from this review.
## Exact input identities
| Git blob path | SHA256 |
| --- | --- |
| `crates/oa-core/src/persistence.rs` | `d7b33495e429a0bff65782be4f5295f8fedbd4f72ec394250919bb80ccf79d9e` |
| `crates/oa-core/src/persistence/source_save_tests.rs` | `a8eb528ac8f96c97292124409908476c25504303b11bd30b4c0358412e88edda` |
| `crates/oa-core/src/persistence/json_codec.rs` | `2aa2c52eea794acb85744440192df9ef5193160ad6ebd1febaa824e144aadd90` |
| `crates/oa-core/src/language.rs` | `b65098321ecf79e4dc1182a7fab7bafeec0a238bb56153e81782f04fab838558` |
| `crates/oa-core/tests/review_regressions.rs` | `f154e5c88d35e31512e2b07733a1327ea3e2fcfd1ef8eb23066a4e9573f387c1` |
The commit changes only persistence.rs and the new four-test source file. No S1 bytes were reviewed or endorsed. Execution and parent integration remain pending.