19 KiB
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:
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:8currently 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–13includes index/update/write as well as parsing;mixed/project.rs:25links 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–308derives 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 selectingcarried::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:453consumes 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
- 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.
- 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.
- 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.
- 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.
- 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.
- 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:
- 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. - 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 before Traversal, with
invalid_sourceandSource8 index/output byte limit exceeded.at main0..1 (corrected by the independent acceptance review). 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. - 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.
- 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.
- 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.