13 KiB
Mixed typed-index seam: independent source/design critic
2026-09-24. Source-only review of clean private source aaec78fcc9b273d1485497126392afdd1fb2e54e; source paths below are relative to crates/oa-core/src/. Read root mixed-Project contract section 7, original-span proposal and accepted review, and parent's draft harness/analysis-mixed-index-contract-20260924.md. The reviewer performed no compiler, source/build/Gitea mutation, or runtime check. Parent subsequently verified and resolved the findings and adopted the revised contract. Initial findings and final disposition are preserved below. Prerelease #37 is unaffected.
Assessment and two precise draft refinements
The consumed wrapper plus shared private verifier is the smallest sound next slice. It preserves original complete-bundle authority without claiming an ordinary occurrence index or enabling editing. Separate mixed verifier duplication offers no demonstrated benefit over narrow borrowed-input extraction. Parent's draft is implementable after settling these two points explicitly:
- Set accounting dimensions in the contract, not during implementation. Existing mixed-Project contract section 3 assigns map/vector/work entries and expression sites to entry slots; occurrence slots cover lowerer occurrence records. Consequently every new index-map entry, including an occurrence-map entry, charges
entry_slots; retainedlower::Occurrencerecords keep their existingoccurrence_slots. Owned copied names charge text bytes separately. Parent draft lines 115–119 currently leave this choice open. Applying the oldIndex::insertlocallowering_occurrences(map.len())check would not satisfy that inherited aggregate policy (analysis_source/index.rs:643). - Separate runtime damage checks from completeness by sealed origin. Parent draft lines 138–144 list missing/extra occurrences among corruption controls. The existing verifier does not independently enumerate all expected syntax references: its own module header states that occurrence completeness trusts the lowerer (
index.rs:1–5).check_syntax_edgesonly traverses expression structure (:478–632). Deleting an isolated parameter-declaration occurrence or a non-expression type reference can leave all runtime checks satisfied. Do not promise arbitrary omission rejection without adding a separate reviewed reference-syntax verifier. For this minimal slice, retain sealed parse/lower completeness, use hand-enumerated full occurrence output as the independent oracle, and restrict mandatory runtime omission rejection to checks actually present (missing expression, missing call label for an existing argument child). This is not a reason to widen scope or expose fabrication APIs.
Concrete ownership/API candidate
A private constructor consumes ResolvedMixedProject<'s> and returns IndexedMixedProject<'s> { resolved, data }. resolved owns the unchanged accepted Project, syntax, warnings, admission metadata, raw sites and Ledger (mixed/project.rs:16–32, :535–543). data owns names and the typed expression/occurrence maps. Read-only methods return views borrowing the wrapper; a view may expose canonical file/path/source lookup and qualified keys, but no mutable fields, raw-site constructor or old Index conversion. No extra lifetime is needed for self-references because data owns its copied name Strings and keys rather than borrowing fields of resolved.
During construction, narrowly split borrows of retained immutable syntax/project/sites, mutable Budget (for the existing depth check), and mutable Ledger. These borrows exist only inside the builder and end before returning the owner. The retained SourceBundle borrow outlives the wrapper and prevents safe mutation while it is live; identical lifetimes alone are not provenance, so neither source nor file table is a separately supplied parameter. Keep the constructor in the witness's module boundary or expose only a constructor-authenticated private view to the shared verifier.
Do not reconstruct BTreeMap<usize, String> from mixed files. ParsedMixedSyntax.files already fixes canonical ordering, original source strings and tokens (mixed.rs:36–58, :370–376). A borrowed file lookup can return (original_source, token_slice) for either existing typed-only inputs or the mixed file table. Typed declaration iteration should yield (file_key, &Declaration) over retained vectors; site inputs are borrowed expression/occurrence slices. Avoid allocating cloned Unit declarations, relexing, rebuilding Lowered model maps, rerunning admission or generating replacement source.
The old nominal Index<'s> remains the only input to Plan and Composition (update.rs:15–27, :238–245). A shared private IndexData/verification implementation does not justify a generic update trait, Deref<Index>, AsRef<Index>, or a public adapter. Keep the old sources: &BTreeMap<usize, String> authority in its wrapper (index.rs:122–128) and preserve typed-only checks/semantics. Mixed-only aggregate reservations must not be accidentally imposed on legacy tests via an unreviewed policy change.
Reuse boundary and scope
Reuse structural key/path semantics (index.rs:15–120), owner/physical-token checks (:246–283, :635–675), expression-parent/path construction (:288–359), qualified roles and call-label checks (:360–472), and original-syntax expression-edge verification (:478–632). Extract these algorithms with small private input/storage adapters, rather than introduce a public generic indexing framework.
Replace the current name-construction assumption only for mixed input. It currently enumerates ordinary identities and then separate Lowered definitions/elements (index.rs:209–241); mixed resolution already moved those models into the complete Project (mixed/project.rs:439–459). Iterate every complete Project global once, then scoped parameters once. Global categories include Project, definitions, elements, relations, diagrams and placements, artifacts, and traces (model.rs:1763–1783). A streaming borrowed walk avoids Project::identities()'s temporary Vec; otherwise reserve that Vec before calling it. Names include ordinary globals for resolution/namespace purposes, but indexed declarations/references remain typed-owned. Ordinary references to typed targets are preserved in syntax/model but not supplied by this view.
A separate mixed-index implementation would duplicate roughly the whole path/call/syntax-edge verifier and its damage tests; later fixes could diverge. Reconsider only if extraction exposes an actual incompatible invariant, rather than because mixed fields require private access. The distinct wrappers already express the genuine provenance difference.
Aggregate storage inventory and release points
Continue Ledger::reserve's checked totals and inherited Budget (mixed/project/accounting.rs:43–70), including retained syntax, warnings, expression sites and occurrences. Never start from zero. One consumed owner prevents repeated retained indexes using one accounting baseline.
For a successful simple implementation let G be complete global-plus-scoped name count, H typed headers, O retained lower occurrences, E expression sites, P nonroot expression sites, and C calls. The added retained logical storage is entry slots = G + (H + O) + E + P; occurrence slots = 0; owned text = sum of the copied names' UTF-8 lengths. The existing baseline already contains E raw-site entry slots and O raw-occurrence slots. P counts each allocated shared Path Step once; Arc clones do not allocate another Step but their map/vector records still count. This formula assumes exactly the current three maps and one Step per nonroot expression; any added representation must be listed separately.
Scratch must additionally reserve before growth:
- typed owner map H; physical-token set H + O;
- arena-position/path/depth vector E; call-site map C;
- borrowed reverse-name maps G; syntax-site lookup E; iterative syntax work slots W;
- any allocated file/declaration view table, error mapping retained in success, or identity-visitor scratch.
Borrowed reverse-name strings cost no new text bytes. Work-pop releases its logical slot; this remains a logical live-element policy, not retained Vec capacity/RSS metering. Scalars such as visited are not a separately allocated entry per visited expression.
Explicitly drop/release owners and physical-set storage after occurrence validation, then paths/calls after argument-child validation and before syntax-edge work where possible. Otherwise all remain live and must be summed at that peak. Retained Path Steps remain charged even after dropping the temporary path vector, because expression/occurrence maps share them. Failure drops the entire consumed owner; no partial reusable success or stale reservation lease escapes. Preserve iterative Path destruction (index.rs:68–79). Path::edges allocates a Vec (:82–90) and currently has test-only call sites; do not turn it into a new unmetered production materialization API.
Discriminating acceptance cases
- Independently enumerate complete names and all typed key/location/spelling tuples in OA and SA mixed files with Unicode/CRLF, ordinary prefixes and coincident offsets across canonical paths. Compare every tuple and original whole bundle, Project, warnings and admission metadata; do not generate expectations by the builder/lowerer. Include a parameter UUID equal to a global and reused in two calculations.
- Reordered repeated calls must retain distinct parent paths, correct callee-scoped labels and original argument order. Corrupt a label to an existing other callee with the same parameter UUID/spelling so token/catalog checks cannot mask the intended call-site rejection (
index.rs:426–469). - Runtime negatives: missing/extra expression; forward/cyclic parent; cross-file/owner/field location; internally consistent but syntax-wrong child edge; duplicate physical token or qualified occurrence; non-Word/partial/UTF-8-split token; missing call label with an argument child. Record which exact check rejects. For arbitrary reference omission, the full authored oracle must fail; do not label this builder rejection unless new syntax completeness logic exists.
- Hand-count a retained baseline plus the formula above and peak scratch: exact cap succeeds, cap minus one fails at the intended reservation before growth. Separate copied-name-byte pressure and overlapping temporary maps. A case where old baseline and new maps each fit alone but their sum does not catches ledger reset; retain both raw and indexed representations throughout. No parse byte/token/entry/reference or Exact-work recharge is permitted; inspect control flow as well as counters. The existing
Budget::framemay update deepest-frame usage while checking depth (budget.rs:118–121); this is not another parse. - Type/API inspection must establish no old-updater-compatible conversion and no replaceable bundle/table/profile inputs; wrong-session composition is a later compositor negative, not an invented runtime API in this read-only slice. Test original provenance through views and success/late-failure cleanup on the existing small-stack harness. Re-run legacy Index/update/export oracles after extraction.
Dependencies deliberately still open
This slice neither authenticates a complete ordinary occurrence set nor authorizes rename emission. Subsequent work must derive both edit producers and complete rename intent from the same witness/target; define the precise supported ordinary diff/compound-edit normalization; aggregate and validate original-coordinate edits; emit once; recompile/admit the actual complete result; compare the whole independently intended Project; and preserve atomic history/save behavior. Full parity still requires typed subtree and parameter edits, repaired deletion/reidentity, file movement/ownership, missing-ID stabilization/reindexing, migration/public dispatch, and editor/root reconciliation. These remain delivery requirements, not reasons to expand this next read-only dependency.
Revised parent draft disposition
Re-read the updated contract's live-storage and evaluator clauses after parent revision. Both requested refinements are now explicit: new map/vector/set/Path storage uses entry slots while retained lower occurrences keep occurrence slots, and runtime expression/token/call checks are separated from complete occurrence emission guaranteed by sealed lowering and independently enumerated output tests. Those changes resolve the two design ambiguities identified above. No remaining blocking source/design finding for this bounded implementation proposal. The concrete retained-storage formula and failure cases in this report are implementation guidance, not measured results or a final accepted decision. Parent retains adoption and gate authority.