11 KiB
Mixed CLI read-only check: implementation map
Design only against public-read branch bab6f96a (parent reports accepted gates).
No source edits, compiler or remote actions. Paths/lines refer to this source.
Recommended user outcome
Add explicit arcadia check-mixed ROOT [--json], requiring the public checker's
schema8 and explicit UUIDs. Preserve check/lint/stabilize/rename-alias behavior.
Use the existing report envelope with operation check-mixed, status checked or
invalid, no changes, written=false, migration=null and identities_to_stabilize=0.
The operation validates captured original bytes; it does not stabilize, export,
format, index or save. Help must state this distinction and reject --write.
The command choice and discovery architecture are separate decisions. Merely adding a command enum variant does not make the existing loader support typed syntax. A real parser-consistent discovery seam is the prerequisite below.
Current blockers and reusable code
- oa-cli/src/lib.rs:213 prepare loads source_files::load_project, then ordinary lint/analyze/compile (234–310). Operation::Check still returns Prepared holding CompiledSourceProject and a disk snapshot; it is not a read-only mixed route.
- main.rs:76 dispatches all accepted arguments through prepare and optional write. Add a separate report-returning mixed-check branch before that branch. Do not forge Prepared or an empty SourceIndex. An explicit Arguments command enum can separate read-check from existing editable Operation, or use a distinct check_mixed flag/variant handled before prepare; avoid illegal write combinations.
- source_files.rs:329 load_project creates a DiskProject then discover_missing. Discovery's DFS reads bounded bytes and tracks originals/expectations (389–494), but495 calls ordinary inspect_includes and529 runs ordinary compile_bundle.
- source_project.rs:207–268 inspect_includes validates paths and calls ordinary language::parse_unit, so it rejects valid short typed declarations. Filtering that diagnostic cannot reveal the missing include graph safely.
- mixed.rs:390–422 already lexes and invokes the shared Parser::unit with actual mixed State and explicit IDs, producing ordinary.includes plus typed syntax. This exact unit grammar can inspect includes without resolving global references. Extract/reuse this narrow unit operation instead of a regex, guessed brace scanner, second quote decoder or calling the complete checker on partial bundles.
- source_files.rs:104–216 root_entry/check_logical/disk_path/read_bounded already enforce root containment, canonical logical names, reserved journal namespace, link/regular-file restrictions and bounded reads.240 check_snapshot and293 check_physical_aliases preserve aggregate and physical-alias checks. Reuse these functions in one core discovery engine; do not replicate their security logic in CLI.
Minimal safe ownership shape
Introduce a named read-only discovery entry in source_files, with a small private closed selector OrdinaryEditable / MixedRead for shared discovery dispatch. Existing public discover_missing/load_project select OrdinaryEditable and retain all current completion/lint behavior. MixedRead selects mixed include inspection and skips ordinary compile/stabilization; it still performs all disk/path/journal checks. No generic caller-provided parser/loader callbacks or new public framework.
Prefer an opaque owning MixedSourceSnapshot exposing only bundle() and
load_diagnostics(), or a narrowly scoped load_mixed_source result owning those
values. Do not advertise it as a save snapshot or provide conversion into editable
CompiledSourceProject. Internally reusing DiskProject's captured bytes is possible,
but returning DiskProject publicly would unnecessarily expose save/discovery
operations and expectations. Keep original raw-byte expectations private if reused.
This design adds a small wrapper, not a clone of every buffer for an extra model.
CLI owns the snapshot, borrows its complete reachable SourceBundle while calling language::check_mixed_bundle, maps warnings/errors into the existing Report, then drops CheckedMixed before dropping source. Report owns mapped diagnostic strings; no borrowed checker escapes and no self-referential source/result struct is needed. Report has no Project field: complete model evidence comes from independent core and CLI-library tests of those same literals, not an invented JSON assertion.
Discovery parser work and limits: resolve before implementing
Discovery is an extra syntax pass; final borrowed checker remains the sole complete semantic admission. Its existing budgets must be fresh for that full pass; do not subtract discovery work from final counts or represent a successful partial scan as an admitted model. Re-reading/reparsing captured strings is explicit bounded work, not a claim that the checker itself reparses or that disk is reread between phases.
The mixed unit helper needs a genuine Budget and ModelCounts. A naïve new budget per file silently multiplies allowed discovery numeric/token/reference work by the file count; a single shared budget with all retained-text totals left charged after units are dropped misrepresents live storage. Therefore contract must choose a small explicit discovery-phase work policy and lifetime:
- Recommended: one shared discovery work budget across all captured file parses, cumulative bytes/tokens/references/numeric/syntax-event work, with per-unit syntax owners dropped after copying only include locations. Reuse Parser::unit and its resource checks. Name and check temporary per-unit metadata/text ownership before growth if selecting producer mode; reconcile/release it on unit destruction. This is a real small producer-lifetime seam to specify, not existing free behavior.
- Simpler alternative: per-unit bounded inspection with a documented aggregate upper bound of max_files times its cap. It is finite but can be far larger than the selected checker work profile; do not choose it silently or describe it as inheriting one whole-project numeric budget. Parent may reject that cost policy.
- Do not retain all parsed witnesses then clone/reparse the complete bundle merely to avoid accounting a dropped unit. That grows live state and complicates source lifetimes for no consumer benefit. Do not stream-load into a borrowed BTreeMap while previously parsed units retain references into it; ordinary Rust borrowing disallows this without additional ownership infrastructure, which is unnecessary.
The implementation contract should pin the selected discovery bound/diagnostic order and helper's drop/refund rules before source edits. This is the main remaining bounded design question, not an argument for full editable-source adoption.
Filesystem and diagnostic contract
Preserve existing root/file checks, UTF-8 rejection, aggregate byte/file/edge/depth limits, case/physical alias refusals and pending-journal detection. Root depth0; include paths resolved by existing resolve_include; count repeated authored edges. Final complete check still rejects cycles/semantic failures using its exact original coordinates. Discovery diagnostics prevent invoking checker with missing/invalid buffers. Return exit3 for disk/path/pending-journal failures under existing CLI mapping; exit1 for source/semantic rejection,0 for valid with advisory warnings, 2 for invalid arguments. Pin mixed loader's diagnostic precedence in tests rather than assuming it equals in-memory checker's earlier/later limit branch.
Do not suppress pending_save simply because operation is read-only: existing CLI refuses it, and the captured files may represent interrupted save state. No journal recovery is implicit. Neither old nor new read path promises atomic multi-file snapshot/CAS against concurrent external replacement (source_files.rs:177 comment). Promise unchanged bytes by this command and validation of captured bytes, not that the disk still equals that snapshot after a concurrent editor writes.
Compared/rejected options
- Automatically replace ordinary check with mixed: breaks existing omitted-ID and migration behavior. Schema8 alone does not distinguish ordinary drafts from the stricter explicit-ID mixed contract. Avoid heuristic detection/fallback on errors.
check --mixed: coherent alternative explicit spelling, but adds an option whose invalid combinations must be excluded for mutations. Dedicated check-mixed makes the limited outcome easier to explain; select one spelling, not both initially.- Explicit all-files CLI manifest/bundle: can reuse checker immediately, but forces users to enumerate includes and introduces another input/argument format. It is useful only if a real pipeline already supplies bundles; not the recommended ordinary filesystem workflow and not a substitute for safe include loading.
- Full compile_bundle adoption: requires typed stabilization/index/update/formatter and application consumers; none is necessary for this read-only outcome.
Finite acceptance and scoped files
Core changes: source_files shared discovery selector + read-only snapshot; language mixed unit inspection helper and narrow reexport for core-only use; source_project include-location conversion reuse where appropriate. CLI lib arguments/report path + main dispatch, docs/source-cli.md/help, focused tests. No changes to updater/save/formatter or public CheckedMixed authority.
Test actual executable on independently authored OA/SA three-file fixtures with cross-file typed/scoped references and ordinary nesting. Exact complete JSON warning/error reports, raw original byte hashes/directory listing unchanged after each command, including CRLF/Unicode and comments mentioning include/aliases. Core tests compare independently authored full models for those same bytes.
Exercise missing/cyclic include, invalid path/root escape, case/physical aliases, nonregular/invalid-UTF8 file, pending journal and exact aggregate/depth/edge limits. Only claim platform-specific link/FIFO tests where cfg permits; Linux remains user-deferred. Exercise missing IDs in all four classes and typed unresolved/wrong kind errors. Verify --write rejection and all existing ordinary CLI commands remain unchanged. No fake success for an empty include graph or wrong command dispatch. Add unit-hook counter/lifetime evidence for the chosen discovery work policy and repeat relevant existing filesystem/stack regressions after shared-loop factoring.
Parent decisions needed: command spelling; explicit discovery work/storage policy above; whether reuse of private DiskProject storage under opaque wrapper is worth its duplicated captured bytes versus a smaller read-only buffer owner. No source or compiler work is authorized by this proposal.