Define public borrowed mixed-source validation contract

This commit is contained in:
2026-09-24 21:10:56 -07:00
parent e79645a0ec
commit bcddaba399
4 changed files with 481 additions and 0 deletions
@@ -0,0 +1,104 @@
# Public read-only mixed source8 validation
Baseline `7b06b8ea1b7ef902ee0b3d0ec2971a9a7a1f1acd`, local-only while Gitea
publication is blocked. This bounded outcome advances the user's textual linting
and programmatic model requirements without claiming application editing parity.
The [implementation map](analysis-public-read-design-20260925/IMPLEMENTATION-MAP.md)
and [independent critique](analysis-public-read-design-20260925/CRITIQUE.md) compare
diagnostics-only, borrowed, owned and full editable-compilation alternatives.
The [acceptance review](analysis-public-read-design-20260925/ACCEPTANCE-REVIEW.md)
pins independent fixture, model, warning and boundary-diagnostic oracles.
## Selected interface and authority
Expose only `language::check_mixed_bundle(&SourceBundle, ProjectLimits)` returning
`Result<language::CheckedMixed<'_>, Vec<SourceDiagnostic>>`. The opaque, non-Clone
result offers `project() -> &Project` and `warnings() -> &[SourceDiagnostic]`.
Define it beside the existing resolved owner and re-export the type/function
through private modules. Keep its fields and constructor private. It directly
retains the actual ResolvedMixedProject; no model substitution or second parse.
The result borrows the actual complete input bundle for its lifetime. It grants
validation and immutable queries, not an editable source index, stabilized output,
save authority, ownership extraction or history installation. Preserve the real
syntax/expression/occurrence/storage owners while the witness lives. Do not add
unused public methods, artificial runtime reads or broad lint suppression merely
to accommodate retained internal fields.
Accept only schema8 with explicit UUIDs for project, every ordinary and typed
declaration, and typed parameters. Preserve canonical logical paths, include
traversal and detached-file rejection. Return complete admitted OA/SA models and
actual located warnings in existing order; errors return no partial successful
result. Preserve exact original bytes, comments and line endings. No missing-ID
generation, migration, filesystem discovery, formatter or write operation occurs.
Use clear public diagnostics for the schema/explicit-ID requirements rather than
describing the supported API as private test machinery.
## Policy and production dependency boundary
Construct fresh borrowed C=0 authority from the actual bundle and caller policy.
Derive internal Limits once by the existing source8 formula: caller source-byte
and step-reference limits, remaining ParseLimits fields UNLIMITED. Internal
entries therefore remain capped at262144 independently of caller model records;
the latter still governs actual final-model admission. Keep the existing hard
token, frame, numeric, byte and reference ceilings. Do not expose an arbitrary
profile or raw Slots constructor. Preserve Some(Borrowed) and select both text
and entry producers, then complete resolution/admission with the same policy.
Promote only the required parse/lower/resolve dependency cone. Parser mixed state
and shared helpers must compile in production coherently while existing ordinary
callers still select None. Keep private edit/index/emission machinery, fixture
constructors, retained snapshots and Pending-backed authority under cfg(test).
Use item-level gating from the reviewed map. Necessary retained allocation or
admission-evidence fields may have individually documented non-test dead-code
allowances; do not suppress whole modules/structs or weaken -D warnings.
This remains the existing logical storage policy, not an all-heap/RSS guarantee.
Independently bounded traversal, delimiter/Pratt/duplicate-name scratch and
established model/validator/numeric internals retain their documented scopes.
Preserve generic depth64, typed depth128, DEPTH_128_VALIDATED=false and the
unresolved full-pipeline small-stack limitation. Linux remains user-deferred.
## Observable acceptance
- A real non-test integration consumer uses the exported API on substantial OA
and SA multi-file fixtures with independently authored complete model oracles,
located warnings, nested ownership, cross-file/scoped references and Unicode/CRLF.
Reuse valid authored fixtures/oracles without deriving expected models from
the candidate parser or writer. Check all original bytes on success/refusal.
- Negative cases cover missing UUIDs in each required class, unsupported versions,
invalid paths/includes/cycles/detached files, malformed and duplicate/unresolved
or wrong-kind references. Assert actual diagnostic paths/spans and meaningful
error categories. Keep long ordinary analysis-constructor rejection explicit.
- Prove the real production constructor selects borrowed zero carry, both producer
modes and every derived limit through narrow internal observation; preserve
existing discriminating producer cap tests. Public tests exercise caller source,
file, edge/depth and final-model/step restrictions. The minimal78-byte fixture
reaches input admission before its C0 text44/43 boundary, so do not mislabel a
source-byte rejection as a producer-boundary test or reuse pending O152/151 caps.
Preserve existing diagnostic mapping and phase precedence: the early byte check
reports `invalid_source` with `Source8 index/output byte limit exceeded.`, not
Traversal's later `source_limit`. Use the reviewed exact spans/categories for
file, edge and depth boundaries rather than assuming a uniform limiter layer.
- Compile-fail documentation demonstrates that the result cannot outlive or permit
mutation of its source. Include a concise real usage example and public API
documentation in the established language documentation; distinguish this
bounded read contract from full source8 editing and application support.
- Run core library, the new non-test integration target, relevant existing
ordinary integration/stack controls, doctests, strict core all-target Clippy and
formatting disposition. Production Parser layout now includes mixed state:
previously test-only success is insufficient to qualify ordinary non-test
callers. Investigate concrete stack/runtime changes; no larger stack or relaxed
budgets. Reuse other valid evidence only when component inputs are unchanged.
Existing parse/compile, source-file loader, CLI, desktop, server, formatting,
update, save and history contracts remain separate. Current CLI discovery parses
ordinary syntax and unconditionally constructs editable compilation even for
Check; a new Rust API alone cannot enable mixed CLI checking. Reject a fake index,
regex include scanner or nominal command that fails before this checker. A later
CLI/discovery adapter or full editing adoption requires its own concrete outcome.
Parent owns compiler execution, synthesis, acceptance and publication. Bounded
implementation and independent source/oracle review may proceed on a distinct
local branch; no shared ownership or publication success is implied by this
contract while Gitea writes remain unavailable.
@@ -0,0 +1,67 @@
# Public mixed read: independent acceptance review
Read-only review of selected `harness/analysis-public-read-contract-20260925.md`, its IMPLEMENTATION-MAP/CRITIQUE, and immutable implementation baseline `7b06b8ea1b7ef902ee0b3d0ec2971a9a7a1f1acd`. No source edit, compiler, executable or network operation. This is a proposed evaluator contract, not public-API acceptance.
## Disposition and concrete diagnostic correction
The selected `language::check_mixed_bundle` / opaque non-Clone `CheckedMixed<'_>` is a coherent bounded read capability. Retaining the actual resolved owner and borrowing the complete bundle preserves existing authority without inventing an editable index or output ownership. No fundamental design blocker found. Production wiring and non-test behavior still require actual verification.
One source-backed correction to the implementation map's suggested78/77 oracle is necessary. `mixed.rs:345–358` checks `budget.index_bytes(total_source_bytes)` **before** `Traversal::new`. `budget.rs:310–313` emits `Source8 index/output byte limit exceeded.`; `mixed.rs:78` maps its start through ordinary_error to code `invalid_source`. Thus E at caller77 returns `main.arcadia`, span0..1, line1 column1, Error severity, no object/field/related locations, exactly that message. It does not reach Traversal's later `source_limit` branch. Parent explicitly selected preserving this existing mapping, not a new error taxonomy. Distinguish parser one-character transport from full traversal/resolver spans.
## Existing semantics that must actually cross the production seam
- `mixed.rs:239–260` checks typed declaration identity and each calculation parameter identity after parsing; missing parameter ID reports the owning declaration start, not an invented exact absent-ID span. `language.rs:1291+` refuses omitted project/ordinary IDs at the parser's current token. Test each class separately with all earlier required IDs present.
- `mixed.rs:283–288` derives source8 Limits from only caller source bytes and step references, with remaining ParseLimits fields UNLIMITED. Therefore internal entries are262144, bytes=min(caller,64MiB), tokens=min(bytes,1048576), references=min(caller,500000), frames256 and numeric work50000000. Caller max_records remains a separate model-admission control, not syntax-entry capacity. Preserve all other original ProjectLimits fields for traversal/model admission.
- `carried::parse_entry_producers` invokes parse_impl with **Some(guard),true,true**. The new constructor must select an actual borrowed guard with zero carry, not None, a fake pending output or a separately supplied policy. Zero carry excludes caller-owned source bytes from carried storage; input-byte admission and text/entry producer accounting remain active.
- `Traversal::new` validates canonical paths, file counts and case collisions; next()/finish() enforce include existence/cycles/depth and reject detached supplied files. Root depth is0; includes advance depth by1. Duplicate edges count even if a file is visited only once. `finish()` attributes unreachable-file refusal to the detached path with default span. Do not silently filter the supplied map.
- `project.rs:596–665` maps actual validation issues in their existing order, reserves mapped diagnostics, rejects any Error issue without returning a successful owner, and retains complete model/warnings/syntax/storage. Warning columns count Unicode scalars, while spans count UTF-8 bytes. Existing fallback to whole owning declaration remains intentional when a validation field has no authored field span.
## Non-test complete-model and warning fixtures
Use a real `oa-core/tests/public_mixed_read.rs`-style integration target importing only public API/model types. Cargo integration tests link the normal non-cfg(test) library; no `#[path]` access to private modules, public fixture constructor, writer-generated expected model or parser-produced expected Project. A small integration support module may transcribe already independently authored literals and whole model constructors.
1. **Rich OA and SA three-file models.** Reuse the literal content and independent model construction of `mixed/project/tests.rs::rich_bundle` / `rich_model`, not its `accepted()` parser helper. This includes ordinary Work and diagram/node references to typed property, property subject back to ordinary Work, calculations f/g across files, nested calls with reordered x/y arguments, and scoped parameter UUIDs reused across different owners (including a UUID equal to the global project UUID). Expected expressions must be constructed by semantic parameter IDs, not textual argument order. Compare the entire Project, all maps/body fields/IDs, and all warnings, not just definitions or named objects. Keep explicit IDs in every class.
2. **Nested ordinary ownership.** Add a small authored nested counterpart, or transplant a proven nested ordinary fixture without reconstructing expectations from its parse: OA entity→actor and SA system→component with fixed IDs. Expected parent is explicit in the independent model while source uses nesting. Compare the complete Project. Keep this separate if nesting would alter the rich Work warning set; do not accidentally remove a leaf warning by adding a Work child.
3. **Unicode/CRLF attribution.** Apply a literal Unicode comment and CRLF variant with expected model unchanged, then independently count bytes/scalars. Existing small source `arcadia 8;\r\nproject p "港🌊" id "00000000-0000-0000-0000-000000000001" { oa_activity w "Work" id "00000000-0000-0000-0000-000000000002" {} }\r\n` has Work span76..141, line2,column60. This is a useful separate exact warning fixture rather than guessing offsets after a broad rich rewrite.
For unallocated named Work, pin **both** warnings in order: `unallocated_leaf`, fieldSome("allocation"), message `Allocate leaf 'Work' to a performer.`; then `work_without_behavior_context`, fieldNone, message `Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.` Both Warning severity, object UUID suffix2, empty related locations, same actual whole Work declaration span/path/line/column. This avoids the prior false empty-warning and one-warning assumptions. Any added historical trace must be deliberately authored with independently checked endpoints; do not assume a reused example contains traces. Historical tests can remain separate if required rather than make the primary rich fixture unwieldy.
Before every success or error call, retain a complete clone of the literal SourceBundle (or an independently saved raw byte map), and compare it afterward. Assert exact entry string, key set and every UTF-8 string including comments/newlines; no generated IDs, migrated header or stabilization. For successful result, inspect project/warnings while result is alive; drop it explicitly before testing later source mutability. A later ability to clone the public Project does not grant verified save authority.
## Finite public policy oracles
E is exactly `arcadia 8;\nproject p "Project" id "00000000-0000-0000-0000-000000000001" {\n\n}\n` in `main.arcadia`:78UTF-8 bytes, whole Project with fixed UUID1/nameProject/aliasp and all maps empty, no warnings. With other policy roomy,78 admits source and the known existing zero-carry resolver/storage peaks fit;77 fails the initial byte check with the exact diagnostic above. Its zero-carry text peak44/43 is **not reachable** through public source-byte policy because the78-byte input fails first. Owned-output152/151 and warning161/160 caps are also not public borrowed-zero examples. Do not expose a profile knob merely to reproduce them.
H three-file include chain is independently authored:
```text
main.arcadia = "arcadia 8;\nproject p \"Project\" id \"00000000-0000-0000-0000-000000000001\" {\ninclude \"part.arcadia\";\n}\n"
part.arcadia = "include \"last.arcadia\";\r\n"
last.arcadia = "//é\r\n"
```
Lengths101/25/6 total132; same complete empty Project as E, no warnings. Use roomy model/storage limits while varying only one public field:
| Boundary | Exact intended observation |
| --- | --- |
| Source132/131 |132 permits complete input;131 fails initial budget byte check, main0..1,line1col1, invalid_source/index-output-byte message. |
| Files3/2 |3 fits;2 fails Traversal at main0..0,line1col1, project_limit, `Source project exceeds 2 files.` |
| Include depth2/1 |2 fits root0→part1→last2;1 fails at part's include span0..23,line1col1, project_limit, `Include depth exceeds 1.` |
| Include edges2/1 |2 fits;1 fails earlier mixed parser include counter in part, span0..1,line1col1, project_limit, `Include graph exceeds 1 edges.` It is not the later full-span Traversal edge diagnostic. |
| Detached file | With roomy limits, add syntactically valid unreachable file: unreachable_file on that file, default0..0,line1col1, `Supplied source file is not reachable from the entry file.` |
Invalid paths/case collisions/missing files/cycles should similarly use tiny independent fixtures so upstream file/byte failures cannot mask the intended branch. For a cycle, all files/IDs and syntax must otherwise be valid; assert referring include location and actual cycle category. Distinguish source grammar prechecks from final semantic model limits: small max_records may fail while counting declarations before resolution. A final-model-specific test needs a known nested model record/ordered-step contribution that is not exhausted by declaration prechecks. Reuse existing independently counted diagram/node or ordered-flow cases; pin the full expected model and count definition first. Do not call a declaration refusal final-model admission evidence. Reference-bearing typed fixtures need enough model step allowance to isolate the intended earliest reference branch, with an independently counted authored reference total; an empty E/H fixture cannot prove positive reference enforcement.
Internal bridge observation should invoke the **same constructor used by the exported API**, observe borrowed-zero guard + both producer metadata options and every derived limit, then resolve with that same policy. Deliberately small but sufficient caller max_records distinguishes262144 internal entry cap from model records. Keep existing discriminatory producer tests unchanged. Observation is wiring evidence, not an external public producer-cap failure or all-heap guarantee.
## Negative and compile-time authority tests
Remove exactly one project ID, one ordinary declaration ID, one typed declaration ID and one parameter ID from otherwise valid literals in separate cases. Require expected public-facing ID requirement message and source location; do not rely only on Err. Preserve parent-selected existing code/span transport. Add schema3/7/9 refusal with valid remaining syntax, unresolved typed global ref, duplicate global alias/UUID, duplicate scoped parameter, wrong-kind ref, malformed syntax and long ordinary analysis-constructor refusal. Strongly distinguish valid scoped-ID reuse from invalid duplicates within one owner. Success controls for each base fixture prevent malformed test setup from manufacturing negative results. Compare original bytes for every refusal.
Compile-fail documentation must force a **later use** of the result, otherwise nonlexical lifetimes may legitimately end its source borrow before mutation. Cover moving a CheckedMixed out of a scope containing its source, and mutating bundle.files while calling checked.project()/warnings() afterward. Use only real public imports and a valid fixture helper mirrored by a positive compiling usage example; compile_fail alone would pass for a misspelled API or unrelated error. Also verify no public fields/constructor/into_project/index or Clone implementation is accidentally exposed; no mutable accessor. Avoid additional compile-fail infrastructure if ordinary rustdoc examples suffice.
## Gate scope
Run actual non-test integration consumer plus core library, relevant ordinary source integrations and required stack controls, doctests, strict all-target Clippy and format disposition. Production Parser layout and mixed hook cfg changes mean previously successful cfg(test) parsing cannot qualify ordinary non-test callers. Preserve failed runs and investigate concrete changes without increasing stack or budgets. Reuse old runtime evidence only for demonstrably unchanged component inputs.
This contract promises a Rust complete-bundle API, not CLI/discovery/desktop/server support. Existing CLI still discovers and compiles ordinary editable source before returning Check; no CLI feature should be claimed from this API. Preserve depth64/typed depth128 policies, DEPTH_128_VALIDATED=false, prior full-pipeline stack limitation and user-deferred Linux work. No broader all-heap instrumentation or new public error taxonomy is needed for this slice.
@@ -0,0 +1,50 @@
# Independent public mixed-read critique
Source-only proposal against `0c07f53ab517626f536f79dbaa2885a587bf1db7`; source advanced during this review to `7b06b8ea1b7ef902ee0b3d0ec2971a9a7a1f1acd` with only the separately reviewed r3 migration test/note correction. Production findings apply to both. Read the prior versioned ordinary-source8 design maps, actual callers and private admission seams. This critique was written before reading the new IMPLEMENTATION-MAP; the implementer's brief coordination message disclosed its borrowed-result candidate. No builds, source edits or publication. Current ordinary compatibility acceptance remains separate.
## Recommendation and user outcome
Select an explicit **read-only check of a supplied, complete, explicit-ID source8 bundle**, using the strong producer parser and complete resolver. Return an opaque borrowed result exposing `&Project` and located warnings, backed by the actual resolved witness. This is the smallest coherent public library capability for an external author, integration or AI tool to determine whether typed calculations/analysis plus ordinary architecture describe a valid model. It advances the textual linting and programmatic-model needs in PROJECT_GOAL.md:64–71,83,96. It is not yet application import, disk-project checking or graphical/text editing parity.
That limited user value must be stated honestly: a new Rust API plus a real external integration test is a usable developer-facing outcome, but it does not improve the existing desktop or CLI automatically. If the selected checkpoint instead promises a shell user can check a mixed project directory, include the actual CLI/discovery adapter now. Do not add a nominal CLI branch that fails before reaching the checker. Full compile/index adoption and repeated owned rename are unnecessary prerequisites for a read-only library result.
## Consequential choices
| Candidate | Benefit | Concrete cost or constraint |
| --- | --- | --- |
| Diagnostics-only check, dropping the witness before return | Smallest output contract; appropriate if the sole consumer needs pass/fail/warnings | Throws away the already admitted model; later model queries must parse again. Owning warning extraction needs an explicit drop/move boundary. It does not solve disk discovery. |
| Opaque borrowed resolved result — recommended | Reuses the existing owner and complete model, source lifetime, warning inventory and ledger without another copy or storage transfer | Keeps syntax/expression/occurrence metadata live. Expose no mutable bundle/model, index/updater or generic constructor. The borrow must prevent changing/dropping input while the result is live. Avoid exposing internal resolver types as the permanent public interface. |
| Owned read result containing moved Project/warnings | Useful for long-lived model-only query/import consumers; permits releasing syntax promptly | Requires a deliberate extraction/storage contract. It is still not editable compiled authority and cannot be called AcceptedRename: no emitted output was verified. No need to clone a bundle or charge output O unless this result actually owns one. Defer until a consumer needs ownership. |
| Replace public parse/compile dispatch immediately | Broadest apparent reach | `compile_bundle` promises stabilized returned bytes, migration and a matching SourceIndex. Typed identity/index/writer/update coverage is incomplete; silently using an empty index or refusing valid ordinary omitted-ID input would violate those promises. Reject this candidate for the present slice. |
The borrowed candidate does not need to index occurrences merely to validate: `mixed/project.rs:29..53` already owns the admitted Project and warnings; indexing at60+ is a separate operation. Keeping a private wrapper around that owner also avoids creating an adoption capability accidentally. A user can always clone an exposed Project through existing public model APIs; this does not authenticate that clone for source saving. Existing downstream validators must remain in place.
## Required boundaries grounded in current code
**Initial authority and limits.** `mixed/carried.rs:43` currently has only a real pending-backed production constructor; the borrowed-zero constructor is fixture-only at191+. `parse_entry_producers:137` selects both producer modes, whereas `mixed.rs:279..305` uses the weaker legacy phase route. Add a deliberate initial borrowed-input constructor deriving policy from supplied ProjectLimits through the existing maximum-profile derivation. Do not expose arbitrary Limits/Slots, manufacture PendingRename or call the weaker route simply because its signature is convenient. C=0 means no previous verifier-owned allocation carry is claimed; it does not assert that the caller's source bundle uses no memory. It must not be reused to erase a pending owner's existing carry during verification.
Preserve exact caller file/include/source/model/reference constraints. The private profile additionally caps auxiliary tokens/entries/frames/numeric work; explicitly select and document these as this entry point's policy, including that auxiliary entries are not the caller's model-record count (`mixed.rs:283..289,317..338`). Existing logical-domain exclusions remain: this is not total heap/RSS accounting or every allocation before growth. Do not promise that every ordinarily valid unbounded model passes this bounded checker. No resource-profile or depth qualification follows merely from making the API public.
**Language and identity.** The initial contract should say source8 only, already explicit project/declaration/parameter IDs, no migration or stabilization. `language.rs:1291+` and `mixed.rs:239..259` presently enforce those IDs. This is a bounded checker scope, not a redefinition of ordinary compile's missing-ID behavior. Preserve whole ordinary/typed global names, scoped parameters, model validation and original file/span attribution. Do not infer syntax kind from whether the resolved Project happens to contain definitions: dispatch needs to be an explicit API decision, not a lossy model-based guess. Define treatment of supplied unreachable files according to the actual traversal/source-byte policy; do not advertise every supplied file as semantically checked without confirming traversal coverage.
**Production compilation.** `language.rs:6..9` currently gates analysis_source, and Parser state/hooks are separately gated. Promoting only the module cannot establish a real read path. Restrict the dependency promotion to parsing/resolution and necessary shared types; keep fixture authority and test fault injection gated. Do not publicly expose legacy typed-only update APIs just because their module is in the dependency graph. A non-test oa-core integration consumer is mandatory evidence.
**Existing caller separation.** `source_project.rs:111,287` defines the much stronger CompiledSourceProject contract. `source_files.rs:495,529` uses ordinary `inspect_includes` and later ordinary compile. CLI `lib.rs:213..305` loads that snapshot, runs ordinary lint and unconditionally compiles even for Check; its Prepared owns a CompiledSourceProject at175. Thus neither a new core checker nor substituting lint alone enables actual CLI mixed checks. A future CLI adapter must preserve syntax-based include discovery, path/edge/depth/byte limits, original-coordinate diagnostics and no-write behavior, while avoiding the legacy compile/stabilization branch. Regex include scanning or a fake Prepared is not an acceptable shortcut.
**No write/adoption inference.** The public formatter still omits definitions (`language.rs:522..607`), while generic updates use old/new formatting and whole-output recompilation (`source_project.rs:640..877`). Disk save revalidates the complete compiled tuple (`source_files.rs:849+`); desktop apply/history and server preparation similarly depend on it (`oa-desktop/src/document.rs:299..395`, `oa-server/src/store.rs:117..173`). Keep these guards and their present typed refusals. No conversion from the new result to these types is needed or justified. Reader success cannot authorize a lossy save, update, import, or history commit.
## Minimal strong acceptance
1. A non-test public integration consumer actually calls the new entry point on substantial complete mixed OA and SA bundles; independently authored whole Projects and complete located warnings match, including historical traces, nested ordinary owners, scoped parameters and cross-file references. Test genuine short typed forms and long analysis constructors under the explicitly selected mixed grammar.
2. Exact original bundle bytes remain unchanged across success/refusal, including Unicode, mixed LF/CRLF, comments, includes and unused supplied-file policy. No stabilization, migration, output formatter, source index or disk operation is invoked. Retained result lifetimes and private fields enforce immutable source binding; no public tuple constructor.
3. Exercise unresolved/duplicate global and scoped names, malformed syntax, invalid paths/includes/cycles, included-file diagnostics, omitted IDs under the declared scope, unsupported versions, caller limits and selected producer cap/cap-minus-one controls. Match errors/warnings to actual files/ranges; do not substitute success-only model equality for diagnostics tests.
4. Ordinary public parse/compile/migration/update/save behavior and explicit typed refusals remain unchanged through their existing APIs. Preserve ordinary depth64, typed expression depth128 and the independent JSON DEPTH_128_VALIDATED=false gate. Reuse prior evidence only for byte-identical components; run actual promoted production builds and relevant stack controls because cfg/layout changes can alter frames even with equivalent semantics.
5. If the outcome is claimed as CLI functionality, add real multifile CLI success, warning and invalid/refusal cases, exact exit/report semantics and unchanged disk bytes through the actual discovery path. Otherwise explicitly leave CLI/desktop/server adoption open and supply a small public API usage example; do not label library-only acceptance as application support.
No fundamental obstacle requires full editing adoption before this bounded read capability. The unresolved prerequisites are deliberate initial authority/policy, the explicit-ID language scope, production dependency promotion and the chosen real consumer. Those decisions should be fixed in the contract before implementation; they cannot be inferred from the private rename verifier's success.
## Subsequent proposal comparison
After writing the independent critique above, read IMPLEMENTATION-MAP.md. Its opaque borrowed resolver wrapper and strong initial C=0 constructor align with the recommendation; no new blocker was found. One wording should become explicit in the selected contract: “missing authored IDs follow existing parser semantics” means **refusal** for project, ordinary/typed declaration and parameter IDs, not UUID generation or compile stabilization. Public API documentation should say this directly.
The detached-file question has a concrete existing answer: `source_project/traversal.rs:185..200` rejects any supplied file not visited from the entry, and `mixed.rs:503` invokes that finish check. Preserve this refusal. Aggregate supplied source bytes/path validation precedes traversal; successful complete-bundle admission therefore cannot silently ignore a detached file. No new disconnected-file interpretation is needed.
@@ -0,0 +1,260 @@
# Public mixed read: bounded implementation map
Proposal only, source `0c07f53ab517626f536f79dbaa2885a587bf1db7` inspected read-only.
No production changes, compiler invocation or acceptance claim. Parent's ordinary
compatibility gates remain authoritative. Paths below are repository-relative to
that private source commit. This proposal needs independent critique and selection.
## Smallest coherent next outcome
Provide an explicitly separate public **complete-bundle, read-only mixed checker**.
It serves host applications/automation that already own source documents and can
validate typed calculations alongside ordinary architecture without formatting or
building an incomplete edit index. Do not silently replace ordinary `parse`,
`compile_bundle`, or CLI `prepare(Check)` with this path.
Suggested surface in a small `source_project`-adjacent public module:
```rust
pub fn check_mixed_bundle<'s>(
source: &'s SourceBundle,
limits: ProjectLimits,
) -> Result<CheckedMixed<'s>, Vec<SourceDiagnostic>>;
pub struct CheckedMixed<'s> { /* private resolved witness */ }
impl CheckedMixed<'_> {
pub fn project(&self) -> &Project;
pub fn warnings(&self) -> &[SourceDiagnostic];
}
```
The wrapper contains `ResolvedMixedProject<'s>` directly; no cloned Project,
self-reference, second desired lifetime, trait framework, or duplicated policy.
It is not Clone and supplies no mutable model, into_project, index, formatter,
save or update conversion. Borrowed immutable source cannot change/drop while the
result lives. The result owns its parsed/model/diagnostic auxiliaries and drops
them normally; source bundle remains caller-owned. Holding a full resolved witness
costs more retained auxiliary memory than moving only the model; it is the smallest
honest authority with existing cleanup and ledger semantics. Later extraction is
an independent lifecycle design, not required for read-only checking.
A caller can explicitly clone a public Project and misuse an unrelated formatter;
Rust cannot prevent that through an immutable accessor. Documentation must state
that this result grants validation only and does not qualify legacy source save.
## Actual code seams and invariants
- `language.rs:8` currently gates the whole analysis_source module under cfg(test).
Parser's mixed field and hooks are separately gated at1218–1753. Activating only
the root module is insufficient. Inventory and enable the required parser hooks
coherently, retaining None for every existing ordinary caller (483/919).
- `language/analysis_source.rs:3–13` includes index/update/write as well as parsing;
`mixed/project.rs:25` links ordinary_index and carried.rs:5 links PendingRename.
Do not expose or ship all edit machinery merely to silence unused-code warnings.
Audit the minimal production dependency cone; cfg-gate edit-only items or narrow
module wiring, preserving their existing test build. This is actual required
integration work, not an assumption that private tests already compile publicly.
- `mixed.rs:279–308` derives source8 Limits from ProjectLimits then invokes legacy
phase parsing. That route does NOT select both producer checks. Prefer a narrow
internally constructed borrowed C=0 CarriedInput selecting
`carried::parse_entry_producers` (carried.rs:153–168), then project::resolve.
The Borrowed backing already binds bundle/policy (carried.rs:21–26); its existing
constructors are fixture-only. A production constructor must derive policy from
the same bundle/ProjectLimits call, not accept arbitrary Slots/profile authority.
- Exact ProjectLimits survive; source8 internal Limits are derived once using the
existing source8 profile, including caller byte/reference restrictions. No policy
resets between parse and resolve. C=0 excludes externally owned source strings;
U/M and retained text producer checks stay active, plus resolver accounting once.
This explicitly stronger new route may refuse earlier than legacy phase checks;
it must not alter existing ordinary/text-only/phase entry-point acceptance.
- `mixed/project.rs:453` consumes syntax and seeds its ledger from guard slots once;
complete construction/lowering follows;596–665 performs complete validation,
mapped diagnostics and warnings. No typed-only Index, ordinary-index phase,
RenamePlan, PendingRename, equality-to-desired, or AcceptedRename is needed for
fresh read-only source admission. Those are output-edit lifecycle authorities.
- Preserve canonical complete-bundle validation, original byte spans, Unicode
columns, include ownership, full validation/admission, and warnings in order.
Return existing Failure.diagnostics on rejection; never convert errors to warnings
or return a model alongside a failed admission. On success warnings are non-errors.
No migration is implied: mixed entry requires8; ordinary3–7 callers retain their
separate migration path. Explicit UUIDs are required for the project, every
ordinary/typed declaration and every typed parameter: missing IDs are errors,
never generated or stabilized (language.rs:1295; mixed.rs:252). Detached supplied
files are rejected by Traversal::finish (traversal.rs:185–200; mixed.rs:503).
## Why existing CLI check is not a free consumer
`oa-cli/src/lib.rs:213–310` loads DiskProject, lints ordinary raw text, performs
ordinary declaration analysis and compile_bundle, even for Operation::Check.
Prepared (175ff) retains a save-capable compiled result. Reusing that pipeline
would reject typed input before the new checker, or falsely manufacture an index.
`source_files.rs:329` calls discover_missing;495 calls inspect_includes,529 compiles
ordinary reachable source. `source_project.rs:207–245` inspect_includes invokes
ordinary language::parse_unit. Valid mixed files therefore cannot currently use
that loader unchanged. Preserve path containment, include budgets, encoding,
physical aliases, missing files and interrupted-save handling; do not bypass these
by recursively scanning include strings or silently filtering parse failures.
If an actual executable consumer is required in the same milestone, choose a
separate read-only `check-mixed` operation with no Prepared/save authority, plus
an explicitly selected mixed include-inspection/discovery adapter using the shared
parser. This is larger than the core bundle checker and requires its own finite
file-system contract. Do not claim the existing loader already solves discovery.
An explicit manifest of all files could avoid parser discovery, but adds a new
user workflow/format for little value; reject it absent a real caller need.
Full compile_bundle adoption is substantially larger: `source_project.rs:111,287`
returns stable source/index authority used by update/save. It needs typed identity
stabilization, complete SourceIndex/update semantics, complete formatter coverage,
loader integration, and downstream caller review. Read-only validation does not
need these dependencies and should not hold that useful slice hostage to them.
## Finite acceptance and exclusions
1. External, non-cfg(test) consumer checks exact rich OA and SA multi-file literals;
assert independently authored complete Project and every mapped warning. Same
source bytes/maps remain identical, no UUID insertion/migration/formatter path.
2. Include Unicode/CRLF diagnostics, forward references/scoped parameter collisions,
typed wrong-kind/unresolved/error cases, malformed syntax, missing/cyclic includes,
noncanonical paths and detached-file policy exactly as existing mixed contract.
3. Ordinary8-only complete models agree with public ordinary semantics for explicit
IDs; legacy public3–7 migration and ordinary8 six-test roundtrips remain unchanged.
Both long analysis constructors and malformed typed syntax retain intended errors.
4. Exercise inherited source/file/edge/depth/final-model restrictions and selected
strong producer exact caps using C=0 expectations, not pending O-based152/151
numbers. Logical auxiliary entries/text are not an allheap/RSS promise; existing
model/serde/raw validator exclusions and numeric policy remain explicit.
5. Borrow/lifetime compile checks prevent escaping result after source dies; scoped
success/failure destruction controls and existing small-stack suite unchanged.
Audit stack prologues if public wiring changes frames. DEPTH_128_VALIDATED stays
false; user-deferred Linux and unresolved full-pipeline limitations are not waived.
6. If CLI included: actual non-test executable, real three-file mixed fixture,
exact report diagnostics/exit codes, unchanged directory and file bytes, and
explicit refusal of write/rename/stabilize flags. No accepted save artifact.
Decision required: core read-only bundle API first (recommended minimal useful
slice) versus adding disk discovery/CLI consumer immediately. Select explicit new
strong C=0 producer policy and production cfg cone before implementation. No public
editing, repeated-owned adoption, output formatting or universal heap instrumentation
is a prerequisite for the recommended core slice.
## Item-level production wiring refinement
This is a proposed bounded wiring plan, not permission for blanket dead_code
suppression or wider public visibility. Line references remain source0c07.
| Item | Production read requirement / proposed treatment |
| --- | --- |
| language::analysis_source root and Parser mixed hooks | Compile privately in production; preserve ordinary None paths. Mixed selection remains constructor-controlled. |
| analysis_source budget, cursor, declarations, lex, lower, syntax, mixed | Required shared parse/lower dependency cone; keep private. |
| analysis_source index, update, write; model_oracle | Gate entire modules cfg(test) for this milestone. Existing private edit tests still compile their complete chain. |
| analysis_source::parse standalone expression wrapper (analysis_source.rs:107) | Test-only; real mixed parser uses syntax::binding through declarations. Gate wrapper, not shared Binding/Expression types. |
| lower::Unit, lower::lower (lower.rs:273), Tables::ordinary | Legacy typed-only adapter; gate these items cfg(test). Keep Tables, insert, typed, lower_bodies and actual lowering representation in production. |
| mixed::parse / parse_with_profile | Existing phase-only fixtures/tests: cfg(test) wrappers; keep parse_impl shared. Factor the unchanged source8 policy derivation into a small private helper reused by new borrowed constructor and old fixture wrapper. |
| carried SnapshotPolicy, CarriedInput, CarriedGuard | Required; only borrowed source authority is production in this slice. Policies transfer into existing Budget and ParsedMixedSyntax once. |
| carried PendingRename import, SnapshotBacking::Pending, GuardBacking::Pending, from_pending and corresponding match arms | cfg(test): private emitter/acceptance remains a tested internal dependency, not public read's production dependency. |
| carried RetainedSnapshot type, both Retained variants and corresponding match arms | cfg(test): only fixture construction owns this snapshot. Keep existing fixture methods/modules gated. |
| carried Borrowed variant, into_parts, guard slots/check, parse_entry_producers | Production: actual constructor derives policy from ProjectLimits and borrows actual complete bundle; slots returns zero. Do not flatten Some(Borrowed) into None. |
| carried parse_carried / parse_text_producers | Test-only historical phase/text-only entry paths. No extra production entry-point that bypasses selected strong mode. |
| mixed/project ordinary_index module | cfg(test), retaining complete rename/emission/acceptance tests unchanged. |
| IndexedMixedProject, ResolvedMixedProject::index, its impl/accessors; IndexInput and impl; Failure::index | cfg(test). Failure::typed/ordinary and mapped diagnostics remain production. |
| ResolvedMixedProject project/warnings | Required accessors used by public read wrapper. |
| ResolvedMixedProject syntax, expressions, occurrences, storage | Keep all real ownership and ledger retention in production: they back accounted auxiliaries even though read consumers do not inspect them. Do not drop early just to remove warnings or invent reads. |
| ResolvedMixedProject static_limits/static_usage | Immutable admission evidence already computed by resolve; choose retaining it with precise field-level explanation rather than public methods just for lint. Admission itself is always executed. |
Some shared lower representation fields are produced for retained expression/
occurrence evidence and read only by cfg(test) index. Implementation must inspect
actual production lint output item-by-item rather than turn off lint for lower.rs.
Prefer cfg(test) on truly test-only methods; for ownership/evidence fields that
must survive, a **field-level** cfg_attr(not(test), allow(dead_code), with a nearby
reason identifying the retained allocation/evidence) is justified. Do not apply
this to entire structs/modules or change the represented storage inventory. A
private retained-owner substruct with underscore ownership field is an alternative,
but would require touching existing destructures/test access for no runtime value;
prefer narrowly documented existing fields. No fake runtime reads/assertions or
public accessors solely to placate lint. Exact surviving field list is compiler-
verified during implementation, not a promise source inspection caught every lint.
### Opaque wrapper placement and privacy
Place public CheckedMixed in `mixed/project.rs`, beside the only constructor that
can obtain ResolvedMixedProject, with private field `resolved`. Its constructor
accepts source+ProjectLimits, calls the selected strong private parse route and
resolve, and maps Failure.diagnostics. Public project/warnings methods delegate.
`CheckedMixed` is public as a type but its underlying module remains private;
re-export only this type and the checker function through analysis_source then
language (`pub use ...`). Offer the canonical user-facing path
`language::check_mixed_bundle` / `language::CheckedMixed`; this avoids making
analysis_source/mixed/project modules public or widening ResolvedMixedProject.
Reexports through private module ancestors are legal when the final exported type
itself is pub; no public signature mentions a private ledger/syntax/guard type.
Do not simultaneously publish duplicate source_project aliases without caller need.
This chooses concrete placement over the original tentative adjacent-module wording.
Production API is read-only and borrow-bound, with explicit IDs for every project,
ordinary declaration, typed declaration and typed parameter. Finite negative tests
must independently remove each class's UUID, not merely one project UUID. A supplied
unreachable file is an error even when syntactically valid; no ignored detached
payloads. Non-test integration tests exercise the exported path and strong producer
checks; unit cfg(test) tests alone cannot establish public wiring correctness.
## Public policy and finite mode evidence (final bounded question)
Preserve exactly `Limits::source8(ParseLimits { max_source_bytes:
limits.model.max_source_bytes, max_step_references:
limits.model.max_step_references, ..ParseLimits::UNLIMITED })`.
Budget.rs:16–26 therefore derives bytes=min(caller bytes,64MiB),
tokens=min(bytes,1,048,576), entries=262,144 (NOT caller max_records),
references=min(caller references,500,000), frames256 and numeric work50,000,000.
The separate original ProjectLimits.model still enforces actual final graph/record
admission. No new public numeric profile or arbitrary carried baseline knob.
A proportionate evidence set is:
1. **Actual production constructor selection (private observation, real route).**
Construct from the same public-input bundle and ProjectLimits, invoke exactly
the production strong parse helper used by check_mixed_bundle, and observe
`carried=Some(Borrowed)` with slots0, producer_text Some((T,0)),
producer_entries Some(metadata). Inspect every derived limit including entries
=262144 while caller max_records is deliberately small but sufficient for the
fixture. Compare complete model/warnings, Usage and numeric work with the old
zero-carry reference on the same explicit-ID OA/SA fixture. No mutation/reset,
second policy argument, fabricated owner or public instrumentation accessor.
The external integration test calls the actual exported checker; the private
observation is narrowly labeled wiring evidence, not an independently observed
external producer refusal.
2. **Public input-byte boundary.** Minimal explicit-ID E source is78 bytes. Public
caller max_source_bytes78 accepts that input if later full admission fits;
77 refuses input, with expected source_limit attribution. E's C0 grammar UUID
overlap is only44 (=alias1+name7+UUID36), scanner36 and retained text8. Thus a
hypothetical44/43 producer boundary is unreachable through this public policy:
both reject the78-byte input earlier. Pending output's152/151 includes carried
owned-output bytes and is categorically inapplicable here. Likewise the escaped
fixture's existing C0 producer peak must be compared with its source length;
do not transplant owned-C491/490 as public checker evidence.
3. **Public include policy.** Use the already authored explicit-ID three-file chain
for exact max_files3/2, include edges2/1 and its actual include-depth definition
(count entry root per Traversal, not an invented number). Pin path/span/error
order from existing implementation. Reuse full bundle expected model/warnings;
original complete bytes unchanged. This tests public policy, not entry262144.
4. **Final-record/graph and reference policy.** A small independently authored
valid typed+ordinary model exercises exact final-record and graph counts versus
one less; a real reference-bearing typed fixture exercises declared reference
limit. Counts must distinguish authored source-reference work from final model
ordered-step admission, both constrained through the same caller setting.
Do not assume setting max_records13 sets syntax entry cap13: it does not.
5. **Selected-mode mechanisms are already separately discriminated.** Existing
carried/entry_producer.rs zero-C11/12 and prefix owner/value refusal controls,
text_producer.rs zero-C inventory/phase parity, checked arithmetic and scanner
ordering remain unchanged. Add only the actual production-constructor bridge
above, not duplicate all those profiles through an unsupported public knob.
Keep existing explicit frame/depth controls and add external refusal coverage
at supported policy limits without implying depth128 platform qualification.
The public byte policy masks the independently derived producer boundary for the
minimal E and existing small examples; this is a concrete ordering fact, not a
proof that every possible valid multi-file input has auxiliary text <= source.
Repeated owned key copies can make other inventories larger. Finding a genuinely
producer-dominant *valid* public fixture could later strengthen external evidence,
but is not a reason to fabricate an input profile or block the bounded constructor
selection + existing mechanism + external policy tests. If such a fixture is used,
handderive its peak, verify earlier source/UUID/scanner/catalog phases fit and
assert exact refusal site before claiming independent producer-cap isolation.