Files
ARCADIA-Studio/harness/analysis-mixed-project-contract-20260923.md
T

91 lines
17 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.
# Private mixed Project contract
**Accepted design, 2026-09-23; implementation remains gated by syntax acceptance.** This document specifies required behavior and proposed acceptance oracles; it reports no pending gate or fixture as passed. Implementation facts refer to `a578ac98a06772a60bec00ae287e65650680d119`, paths under `crates/oa-core/src/`. The [independent review](reviews/mixed-project-contract-20260923.md) informed the retained-site and diagnostic-allocation requirements below.
This slice supplies an admitted private Project with retained original-source provenance, a dependency toward the [project goal](../PROJECT_GOAL.md). It extends the [mixed-parser ownership contract](../harness/analysis-mixed-parser-proposal-20260923.md#minimal-private-interfaces-and-ownership) and [review corrections](../harness/analysis-mixed-parser-review-20260923.md#corrections-and-acceptance-conditions), while preserving the [original-span namespace/resource/acceptance invariants](../harness/analysis-original-span-proposal-20260922.md#namespace-resource-and-acceptance-invariants) and [reviewed boundary separation](../harness/analysis-original-span-review-20260923.md#concrete-next-implementation-boundaries). It does not activate public source8 or deliver mixed editing.
## 1. Result, authority and order
Consume actual `ParsedMixedSyntax`. A successful opaque result owns the complete Project, mapped warnings, successful static admission limits/usage and retained syntax/lowering provenance. It retains the actual borrowed SourceBundle, exact entry, complete canonical paths and original bytes, canonical numeric-file-key table, exact ProjectLimits and the original Budget. No separately supplied Project, aliases, Lowered, tokens, file remap, replacement Budget or widened limits may establish authority. No mutable Project or edit constructor escapes this read-only boundary.
Required sequence: complete headers/signatures → ordinary construction once → typed body lowering once → checked disjoint moves into complete Project → retained model limits → one static/architectural admission route → accepted result. No rescanning, Exact conversion, ordinary decoder duplication, source emission/reparse, UUID invention, or migration/admission of a partial skeleton. Missing explicit IDs remain refused by syntax. Failure returns no usable partial witness.
Seed the entry Project first. Thereafter process canonical file order and original declaration-start order within each file, merging ordinary/typed header occurrences. Detect alias collision before ID collision when both apply, preserving both source owners. Validate complete catalog uniqueness before Project map insertion. Ordinary construction and typed lowering follow this order restricted to their respective declarations; normal validator order stays unchanged. Syntax/include traversal order is unchanged. This is a phase policy, not a promise of earliest physical error across phases.
Catalog categories include project, ordinary objects, neutral definitions and typed layer roots. Parameter identities/names remain scoped to their calculation; equal parameter UUIDs in different calculations or equal to a global UUID are permitted. Ordinary alias projection derives solely from this complete catalog. Preserve declaration occurrence→file mapping through ordinary conversion errors; UUID lookup cannot distinguish rejected duplicates.
## 2. Shared construction and retained-site handoff
Extract the existing `language.rs:1667 resolve_declarations` construction path, including deferred node attachment. Preserve **both Project and legacy_elements scratch** for the existing source3 tail. Existing source3–7 wrappers retain migrations, required fields, defaults, Scenario MixedLegacy fallback, lexical-parent conflict behavior and diagnostics. Public parser/version acceptance remains unchanged. Use borrowed declaration/unit views over retained per-file ASTs, rather than cloned flattened ASTs or a second ordinary decoder. Existing typed-only lower callers retain their wrapper behavior, including diagnostic precedence.
`lower.rs:113 Lowered` separates model maps from occurrence/expression vectors. Move its typed maps disjointly into Project once; do not retain another typed model. Move these three things into the sealed result **together and unchanged**:
- the one global `Lowered.expressions` arena shared across all files/declarations;
- the paired occurrence vector, whose `expression` indices address that arena, and each expression's parent-index/Child edge;
- the canonical numeric-file-key table referenced by both vectors and retained files.
Do not independently sort/compact vectors, join separately numbered per-file arenas or renumber files. Every owner, field, scoped target and original whole-file span retains its meaning. Preserve actual tokens, typed AST, ordinary ordered positions and lexical ownership beside the sites. Numeric arena positions remain private; they are not persistent IDs.
This retained handoff has not performed Index's physical-span, call-site or independent syntax-edge verification. A later typed Index must consume the sealed accepted result, derive names/scoped parameters from complete Project once, and verify original syntax/sites without lexing, lowering or admitting the Project again. Neither the existing numeric-map Index nor its updater is changed in this slice.
## 3. Resource contract
Keep cumulative parsed bytes/tokens/syntax entries/Exact work unchanged. Ordinary active references add once to the existing reference counter. Preserve caller graph/record/step/file/include limits; complete nested model records remain checked at final model admission. Static analysis work remains its existing separate meter.
A private auxiliary ledger tracks checked-add live totals `(entry_slots, occurrence_slots, owned_text_bytes)`. Before allocating or copying an in-scope representation, check the **aggregate of all live reservations** through the inherited Budget: `lowering_entries(total)`, `lowering_occurrences(total)` and `index_bytes(total)`. Overflow or any nonlatching check failure aborts. An allocation move transfers its reservation; copies coexist and both count. Release only when the represented ownership ends. Borrowed text costs zero copied bytes, but its container slots still count. Logical slots do not claim exact allocator capacity or heap/RSS metering.
Entry slots cover syntax/storage metadata, catalog/map/vector/work entries and expression sites. Occurrence slots cover lowerer occurrence records, using the existing entries-plus-reference ceiling. Each slot belongs to one domain. Owned text covers all separately owned in-scope String bytes, including copied keys, paths and retained diagnostic text. Original source bytes are borrowed and already cumulatively admitted; they are not recounted as copied auxiliary text. The auxiliary byte ceiling is distinct from cumulative source bytes.
| Representation / lifetime | Required reservation |
| --- | --- |
| Retained syntax, tokens, positions, file references and lexical ownership | Fixed all-file baseline: retain/reconstruct mixed metadata subtotal, plus parsed syntax-entry storage. Avoid counting tokens/positions twice. Existing dropped value-end-map subtotal may remain a conservative overestimate. Sum owned strings by a bounded walk of parsed structures, not a source rescan; counting scratch reserves before growth. Baseline stays live in accepted result. |
| Complete catalog, ordinary alias projection, diagnostic location registry | Count every distinct map/set/container entry; reserve owned key copies before cloning. Borrow names where existing APIs permit. Projection drops after ordinary construction; catalog drops after lowering once retained locations no longer depend on it. |
| Ordinary construction coordination | Borrowed ordered occurrence/file views and deferred-node vector reserve before growth. Nodes move into diagrams; coordination storage then drops. The active-reference hook below runs before field cloning. |
| Typed lowering | Reserve lower occurrence/expression sites and Resolver work/temporary structures before growth across all files. Site vectors and their reservations remain in the accepted result; temporary work reservations end after use. Model maps move once. |
| Diagnostic mapping and retention | Source mappings, output/retained diagnostic containers, related-location entries, transferred retained strings and newly copied strings share the still-live syntax/sites/locations reservations. Reserve before copy/insertion/retention. Compute line/Unicode column from borrowed file prefixes, or reserve any new cache explicitly. |
This is **auxiliary** accounting, not preallocation admission of every ordinary serde/model allocation. Existing architectural validation, its indexes and its produced raw `Vec<Issue>`/issue-owned strings are explicitly outside this preallocation ledger, as are the architecture/wire allocations excluded by the static meter. An optional cap checked after raw Issue production is only post-production checking. A raw Issue vector must not remain uncharged in the accepted witness: map/move its contents only after acquiring retained reservations, or drop it. Transferring a String avoids a copy but still requires its new retained reservation; copying requires an additional reservation before the copy. If mapped/retained storage cannot fit, return a resource failure, not a success with truncated warnings.
Retained caller max_records/max_graph_objects are enforced on complete Project before success. Deferred nested model checks are not described as preallocation checks. These exclusions must remain visible in code documentation and evidence; neither this ledger nor static events constitutes whole-resolution CPU/allocation metering.
## 4. Active ordinary reference reservation
Add a narrow private hook at the start of each ordinary declaration's construction iteration, **before** the existing `fields.iter().map(Ast::resolve).collect()` and any resulting JSON/model vectors. Borrow witnessed arrays selected by DeclKind and these exact existing body/tag paths:
| Declaration | Active ordered array |
| --- | --- |
| Process, FunctionalChain, Scenario element | top-level `steps` |
| Occurrence element | `body` → externally tagged `Branch` object → `occurrences` |
| ContextualLink relation | `source.references`, then `target.references` |
These selectors mirror `ParseLimits::check_model` and `ElementBody::steps`. In declaration/item order, charge each occurrence once through the retained `Budget.reference`, including repeated targets and UUID items as well as @references. Charge neither unique-target count nor separate per-file allowances. Locate refusal at the first unaffordable item in its original file; use stored extent or an explicit item-start/field fallback.
Absent/default-empty arrays cost zero. For malformed shapes, conservatively reserve recognizable active arrays and let existing conversion diagnose invalid shapes/items; do not add a decoder just for accounting. A private resource failure can precede malformed-item conversion. Existing public wrappers retain ordinary behavior through a no-op hook.
Exclude scalar parent/owner, endpoint occurrence, purpose fields, unrelated arrays named references, already charged typed fields and **all historical Trace baseline snapshots**. Baseline/model storage still participates in final model limits. The counter therefore equals parsed typed references plus active ordinary steps/contextual-chain items. Final check_model validates the complete model without charging that counter again.
## 5. One admission route, complete warnings and locations
After checked merge and complete model limits, use a narrow shared internal validator route, shared by mixed resolution and the existing `validation::validate` wrapper. Run `admit_structure_checked` once. On success, extract owned static limits/usage, drop the Project-borrowing environment, then run `validate_structure` once and preserve its complete raw warning/error result for mapping. On static failure preserve existing short-circuit behavior. Do not additionally call `analysis::admit`, which would repeat static/architecture work. Existing standalone admit/validate public behavior must remain compatible; no cached/self-borrowing environment enters the new witness.
Static limits use the existing declared profile and must be recorded with usage; they cannot silently replace or reset the syntax/model policies. A shared internal result carries successful static metadata and complete issues to mapping. Architectural Issue production has the exclusion in §3; subsequent mapped/retained allocation does not.
Retain every warning on successful acceptance. Any error refuses the witness. Locations cover ordinary and typed globals plus calculation-scoped parameters/fields, using actual canonical file ownership and full-file offsets. Preserve typed failure file keys and ordinary occurrence mapping. An unmapped semantic field falls back to its owning declaration explicitly. Never choose a file by coincident local byte offset, globalize ParameterId or fabricate a precise field span.
## 6. Discriminating acceptance oracles
All are requirements for authored fixtures and execution, not reported results. Expected models, spans, warning content and thresholds must be independently stated; formatting/lowering the tested input cannot generate its own oracle.
1. **Complete bidirectional model:** tiny OA and SA bundles with ordinary node→later typed property and typed subject→later ordinary work element. Compare entire independently built Project, including ordinary defaults, diagrams and definitions. Reordered presentation retains intended semantics and original source ownership.
2. **Catalog and legacy boundaries:** interleaved ordinary/typed duplicate aliases and IDs, including simultaneous collision and entry-project collision, prove alias-first errors with both locations before insertion. Scoped parameter UUID/global reuse succeeds. Separately preserve source3 scratch migration, current required-field errors, lexical-parent conflict and omitted Scenario viewpoint behavior through unchanged public source3–7 wrappers.
3. **Full retained-site oracle:** repeated calls with reversed argument-label order, two calculations sharing parameter UUID/name, nested child expressions and coincident local spans in distinct files. Hand-enumerate every `(file, owner, field, parent-edge/path, role, qualified target, original span)`. Resolve private arena positions to these authored structural identities; check every occurrence.expression targets the intended entry. Do not invoke Index or lower again. Include both directions of ordinary/typed references in whole-Project comparison. Model equality alone cannot satisfy this test.
4. **Inherited and aggregate limits:** hand-count active ordinary lists/chains plus typed references; exact cap succeeds, cap-minus-one rejects before field cloning with the correct item/file. Repeat ordered occurrences and positive parent/baseline exclusions. Two simultaneously live catalog/projection producers each fit alone but exceed aggregate storage together. Different retained nested-record caps distinguish final admission on identical syntax without replacement limits. Parse/Exact counters remain unchanged; reference delta equals the independent active count.
5. **Admission/diagnostic producer:** one exact warning-only complete model preserves that warning; one static failure and one architecture failure return no witness. A tight mapped-diagnostic storage cap fails while retained syntax/sites remain live, including copied path/text or transferred-string reservation. Instrument the narrow internal route or inspect its control flow to prove one static admission and one architecture pass on success; equal outputs alone do not prove this. Do not call raw Issue production preallocation-bounded.
6. **Provenance and cleanup:** complete entry/file/path ownership, Unicode/CRLF coordinates, unchanged global arena/file keys, no externally replaceable authorities and no partially usable failed result. Run relevant existing typed-only lower/export, public parser/traversal and small-stack success/rejection/drop controls after shared-code changes. No source8/editing success is inferred from these controls.
## 7. Deferred dependency and scope boundary
Next typed-Index work must account for names, owner/physical/path/call maps and syntax-edge verification tables against the retained ledger, use original SourceBundle/tokens through a private read-only view, and refuse/hide that view from the old numeric-map updater. It must derive names once from complete Project plus scoped parameters, rather than reusing both complete Project and former Lowered model maps. Ordinary occurrences are still not provided by a typed Index.
Public7→8 dispatch/migrations/admission, missing-ID stabilization, ordinary+typed composition against original bytes, output recompile/admission and intended-Project equality, history/checked-save, root Scenario/editor reconciliation and full graphical/textual parity remain coordinated later work. This checkpoint does not claim those capabilities or a complete occurrence index. Parent owns final scope acceptance, decision/publication and gate disposition.