diff --git a/AGENTS.md b/AGENTS.md index 3fb27854..21b73d05 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -144,9 +144,10 @@ publication evidence compass-graph + fixtures/qualification Read `docs/design/language-architecture.md` and `docs/reference/universal-semantic-evidence.md` before changing universal -adapters, evidence facts, resolution rules, or language cutovers. Do not add a -universal adapter profile without direct evidence emission, validation, -resolution, and qualification in the same change. +evidence producers or pipelines, evidence facts, resolution rules, or +language cutovers. Do not add a universal evidence pipeline without direct +evidence emission, validation, resolution, and qualification in the same +change. ## Rust conventions diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a270825..7dfbd61b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,22 @@ ## Unreleased -- Hard-cut Ruby extraction to the version-1 `compass.ruby.candidate` universal +- Refactor universal language metadata around `UniversalEvidenceProducer` and + `UniversalEvidencePipeline`. `UniversalCandidate`/`UniversalComplete` are + now the clearer lifecycle states `Qualifying`/`Qualified`; the serialized + evidence envelope moves from `adapter` to `pipeline`, replaces `profile` + with `qualification`, and replaces the producer string with `emitter`. + Universal evidence schema is now `/2` and extraction semantics is `/3`, so + pre-refactor caches and evidence artifacts are intentionally rebuilt. + Qualification manifests, candidate exports, and TypeScript scorecards now + use version-2 schemas and call their language identity `producer`. + +- Hard-cut Ruby extraction to the version-1 `compass.ruby` universal evidence publisher and replace the Rails source detector with the evidence-backed `rails-ruby` universal pack. Reopened constants coalesce by exact graph identity, instance/singleton method spaces stay distinct, dynamic dispatch/load/eval forms fail closed, and Ruby remains explicitly - `UniversalCandidate` pending the independent precision/recall audit. + `Qualifying` pending the independent precision/recall audit. - Preserve anonymous PHP functions and arrow functions as typed callable `closure` nodes, and publish exact PHP trait composition as `mixes_in` @@ -36,7 +46,7 @@ records, and amortize bounded snapshot garbage collection. Exact preflights fall back to full publication whenever topology or secondary indexes change. -- Hard-cut Kotlin onto a version-1 universal candidate adapter with packages, +- Hard-cut Kotlin onto a version-1 qualifying universal pipeline with packages, declarations, extension functions, annotations, generic/nullable types, and named/default argument resolution; convert Spring Kotlin to the universal framework pack and require exact compiler evidence for Java/Kotlin calls. @@ -88,7 +98,7 @@ the historical realization. Materialization also verifies the requested profile before reusing a preferred realization. -- Hard-cut PHP production extraction to the version-1 universal candidate with +- Hard-cut PHP production extraction to the version-1 universal evidence pipeline with modern declarations, namespaces, grouped and aliased imports, traits, attributes, enum cases, promoted properties, calls, construction, typed receivers, and source-backed ownership. Resolve PHP type and callable names @@ -98,7 +108,7 @@ Laravel routes and Drupal hooks to universal source evidence while retaining Drupal routing configuration and Blade template extraction. -- Hard-cut C# production extraction to the version-1 universal candidate with +- Hard-cut C# production extraction to the version-1 universal evidence pipeline with bounded AST-backed declarations, namespaces, imports and aliases, overload signatures, typed members and parameters, construction and call evidence, base types, ownership, and explicit overrides. Remove the replaced C# raw diff --git a/MIGRATION.md b/MIGRATION.md index 4076308a..6c36f40c 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -5,15 +5,27 @@ sidecars. Its output root now preserves the familiar flat artifact shape so file-based workflows can transition while Compass's snapshot and store layout remains visible and clearly owned. -## Ruby universal-candidate rebuild +## Ruby universal evidence rebuild The current release publishes Ruby through the version-1 universal evidence -candidate (`compass.ruby.candidate`) and the evidence-backed `rails-ruby` -framework pack. Ruby graph output is therefore regenerated on the first build -after upgrading; do not reuse a Ruby cache produced by an older Compass -publisher. Ruby is still a candidate rather than a complete-quality claim, -so retain review of ambiguous/dynamic Ruby relationships and do not treat -unresolved dynamic dispatch as a missing deterministic fact. +pipeline (`compass.ruby`) and the evidence-backed `rails-ruby` framework pack. +Ruby graph output is therefore regenerated on the first build after upgrading; +do not reuse a Ruby cache produced by an older Compass publisher. Ruby is still +`Qualifying` rather than a complete-quality claim, so retain review of +ambiguous/dynamic Ruby relationships and do not treat unresolved dynamic +dispatch as a missing deterministic fact. + +## Universal evidence schema reset + +The universal evidence envelope is now `compass.languages.evidence/2` and the +extraction semantics identity is `compass.languages.extraction/3`. The envelope +field is `pipeline` (with `qualification` and `emitter` metadata), replacing +the provisional `adapter`/`profile`/`producer` shape. Compass intentionally +does not translate or reuse pre-refactor universal evidence; run a forced +update to regenerate one coherent artifact set. Qualification manifests, +candidate exports, and TypeScript scorecards likewise require their `/2` +schemas and use `producer` for the language evidence identity; regenerate +those audit inputs rather than trying to load the old field names. ## Install Compass @@ -220,7 +232,7 @@ queries use JSON by default; use `--engine store` to select a retained sidecar. Downgrades must validate the output with the target binary. Do not reuse a newer physical SQLite/redb file merely because its filename matches. Rebuild -when the target binary reports an unsupported major or adapter. +when the target binary reports an unsupported major or storage provider. ## Update node-trail direction handling diff --git a/advisor-plans/013-typescript-javascript-graph-quality.md b/advisor-plans/013-typescript-javascript-graph-quality.md index 60accb66..cf202dc0 100644 --- a/advisor-plans/013-typescript-javascript-graph-quality.md +++ b/advisor-plans/013-typescript-javascript-graph-quality.md @@ -10,7 +10,7 @@ > > **Drift check (run before each phase)**: > `git diff --stat a8a6a80aa7547f89cae97f127b7bd44512bcfaee..HEAD -- vendor/compass-tree-sitter-language-pack crates/compass-files crates/compass-languages crates/compass-resolve crates/compass-graph crates/compass-program crates/compass-core crates/compass-cli tests/qualification benchmarks/performance scripts docs PERFORMANCE.md COMPATIBILITY.md MIGRATION.md CHANGELOG.md Cargo.toml Cargo.lock package.json package-lock.json` -> Reconcile renamed files, adapter versions, evidence schemas, and already +> Reconcile renamed files, producer versions, evidence schemas, and already > shipped behavior before continuing. Do not mechanically apply stale line > numbers. @@ -30,7 +30,7 @@ The first implementation slice is intentionally below the phase-5 production cutover. It establishes independent measurement and removes one known -precision hazard while the larger universal-adapter program remains gated: +precision hazard while the larger universal-evidence program remains gated: - Phase 0 now has a pinned TypeScript 5.9.3 compiler-API source oracle with bounded Node/Python bridging, deterministic UTF-8 byte ranges, malformed-file @@ -56,7 +56,7 @@ precision hazard while the larger universal-adapter program remains gated: pass. Existing qualification warnings about intentionally omitted partial fixture records remain unchanged and are not TypeScript/JavaScript failures. -These changes do not register TypeScript/JavaScript universal adapters, do not +These changes do not register TypeScript/JavaScript universal evidence pipelines, do not invoke Node or TypeScript during normal Compass builds, and do not claim that the full standards-correct module model or leadership gates are complete. @@ -89,18 +89,18 @@ closed: `customConditions`; excluded or ambiguous admitted files remain unresolved. Package condition selection consumes the selected project's custom condition order rather than applying a repository-wide guess. -- A qualification-only direct universal-evidence emitter now exists for the +- A qualification-only direct universal-evidence producer now exists for the ECMAScript family. It emits source-grounded declarations, lexical scopes, value/type namespaces, imports/reexports, calls/constructors, base-type candidates, member access, decorators, JSX references, dynamic literal - imports, and CommonJS exports. Adapter identity preserves parser dialect + imports, and CommonJS exports. Pipeline identity preserves parser dialect (`ts`, `tsx`, `js`, `jsx`, `mts`, `cts`, and related extensions) separately from semantic language. - Regression coverage now includes same-directory config inheritance and cycles, relative named-import repointing, package `imports`, `exports` conditions, `typesVersions`, module suffixes/root directories, include / exclude ownership, `typeRoots`, custom conditions, import-kind metadata, JSX - occurrences, CommonJS exports, and direct candidate evidence. Targeted + occurrences, CommonJS exports, and direct universal evidence. Targeted language/resolver tests and resolver-wide tests pass; the full workspace baseline and independent oracle/fixture gates remain required before any phase exit. @@ -109,9 +109,9 @@ This checkpoint is still below the Phase 2 universal-evidence and Phase 5 production-hard-cut gates. `include`/`exclude` ownership, complete Node16/ NodeNext/Bundler condition semantics, complete `typeRoots` package lookup, compiler-trace differential qualification, broad syntax/capability coverage, -and direct TypeScript/JavaScript production adapter registration remain -planned work. The candidate emitter is deliberately not in -`UNIVERSAL_ADAPTERS`. +and direct TypeScript/JavaScript production pipeline registration remain +planned work. The universal evidence emitter is deliberately not in +`UNIVERSAL_EVIDENCE_PIPELINES`. ### Execution checkpoint (2026-08-06, identity slice) @@ -119,7 +119,7 @@ The following Phase 1/2 contract slice is now source-grounded and validated: - Universal evidence has an optional `SymbolNamespace` field on declarations and bindings (`value`, `type`, `namespace`, `value_and_type`). Existing - adapters continue to omit it, and their stable IDs remain byte-for-byte + established producers continue to omit it, and their stable IDs remain byte-for-byte compatible because the identity component is included only when explicitly emitted. - Import and re-export bindings can carry `type_only`; validation rejects the @@ -127,10 +127,10 @@ The following Phase 1/2 contract slice is now source-grounded and validated: `import type`, `export type`, namespace imports, normal dual-space imports, CommonJS bindings, and local re-exports are covered by direct candidate evidence tests. -- Candidate adapter versions advanced to 2 for the new semantic identity +- Qualifying evidence pipeline versions advanced to 2 for the new semantic identity contract. The shared evidence schema major remains `/1`; fields are optional - and omitted by legacy producers. The candidate path remains qualification - only and is not registered in `UNIVERSAL_ADAPTERS`. + and omitted by legacy producers. The evidence pipeline remains qualification + only and is not registered in `UNIVERSAL_EVIDENCE_PIPELINES`. - Contract documentation now describes symbol-space identity and the validation rule. Focused language tests cover serialization/validation, legacy omission, deterministic IDs, and TS/JS identity assertions. @@ -237,7 +237,7 @@ records source-proven call-shape evidence without widening the production cut: unresolved. - The direct candidate suite still passes five tests and now covers namespace JSX members, `super` dispatch, fixed-arity negatives, and typed overload - selection. The candidate adapter remains unregistered; compiler differential + selection. The qualifying evidence pipeline remains unregistered; compiler differential qualification, broad syntax coverage, and release scorecard gates are still required. - Tree-sitter's dedicated `this` node is now treated as a nominal receiver, @@ -253,7 +253,7 @@ records source-proven call-shape evidence without widening the production cut: ### Execution checkpoint (2026-08-06, unresolved-evidence and corpus slice) -The candidate path now preserves source constructs even when it cannot prove a +The evidence pipeline now preserves source constructs even when it cannot prove a target, without weakening the graph resolver: - Dynamic/ambiguous calls, nominally unknown members, unresolved type @@ -298,8 +298,8 @@ target, without weakening the graph resolver: This closes a high-value evidence-loss and false-external-edge slice, but does not close the mandatory precision/Wilson gates, per-capability accepted-sample minimums, compiler target adjudication, Graphify/SCIP equivalent-scope audit, -framework matrix, performance gate, or production hard cut. The candidate -adapter remains unregistered in `UNIVERSAL_ADAPTERS`. +framework matrix, performance gate, or production hard cut. The qualifying +pipeline remains unregistered in `UNIVERSAL_EVIDENCE_PIPELINES`. ### Execution checkpoint (2026-08-06, target-adjudication and JavaScript-shape slice) @@ -327,7 +327,7 @@ accepted-sample and precision gates are frozen: - Dynamic/parenthesized/computed callees remain targetless when proof is not available; JavaScript `class_heritage` and TypeScript `import = require()` now retain exact module/base evidence. The candidate still is not registered - in `UNIVERSAL_ADAPTERS`. + in `UNIVERSAL_EVIDENCE_PIPELINES`. - Initial target adjudication is intentionally a failing diagnostic, not a release gate. On Axios, the candidate hit 2,923/3,167 same-file source targets (92.30% local recall) with 17 wrong-target cases and 564 local @@ -346,7 +346,7 @@ scope, performance, and production hard-cut gates remain open. ### Execution checkpoint (2026-08-06, scope/flow/type/call quality slice) The qualification-only emitter now covers several high-impact target gaps -without widening the production adapter registry: +without widening the production evidence-pipeline registry: - Lexical block, switch, catch, and loop scopes are explicit; `var` bindings hoist to the nearest function/module scope while lexical bindings remain @@ -390,7 +390,7 @@ adjudication measurements, not release precision: the candidate still has unresolved project/flow/framework members and a non-zero local false-positive count. Graphify, SCIP, Wilson intervals, accepted-sample minimums, framework matrix, performance, and the production hard cut remain open gates. The -candidate emitter is still deliberately absent from `UNIVERSAL_ADAPTERS`. +universal evidence emitter is still deliberately absent from `UNIVERSAL_EVIDENCE_PIPELINES`. ### Verification checkpoint (2026-08-06, repository gates) @@ -418,7 +418,7 @@ Plan 013 scorecard is still open: the current same-file target diagnostics remain below the precision/recall thresholds, accepted-sample/Wilson evidence is not frozen, and Graphify/SCIP equivalent-scope, framework, performance, compiler-tier, and production hard-cut gates have not been run. Keep the -candidate adapter out of `UNIVERSAL_ADAPTERS` until those gates pass. +qualifying evidence pipeline out of `UNIVERSAL_EVIDENCE_PIPELINES` until those gates pass. ### Execution checkpoint (2026-08-06, typed-call and boundedness follow-up) @@ -477,7 +477,7 @@ The next bounded resolution slice is now implemented and regression-tested: files and preventing a large-Zod stack overflow. - The direct candidate suite has 18 passing tests, including a constrained generic call regression. The target harness remains developer-only and - ignored; no candidate adapter was added to `UNIVERSAL_ADAPTERS`. + ignored; no qualifying evidence pipeline was added to `UNIVERSAL_EVIDENCE_PIPELINES`. Updated pinned checker adjudication (oracle SHA unchanged) remains zero wrong local targets: @@ -499,7 +499,7 @@ performance, and the production hard cut remain open. ### Execution checkpoint (2026-08-06, JavaScript prototype and wrapped-object slice) The next bounded JavaScript/TypeScript resolution slice is now implemented and -measured without widening the production adapter registry: +measured without widening the production evidence-pipeline registry: - Same-file function constructors now participate in nominal instance-member resolution only when source evidence proves constructor intent: a `new Ctor` @@ -542,12 +542,12 @@ accepted-sample/manual adjudication before a precision claim. These remain diagnostic same-file results, not a release or leadership claim. Precision and recall thresholds, Wilson bounds, framework/compiler tiers, Graphify/SCIP equivalent-scope comparison, performance, and the production hard cut remain -open, and the candidate emitter stays absent from `UNIVERSAL_ADAPTERS`. +open, and the universal evidence emitter stays absent from `UNIVERSAL_EVIDENCE_PIPELINES`. ### Execution checkpoint (2026-08-06, interface inheritance and optional-callable slice) The next source-proven TypeScript slice is implemented and measured without -registering the candidate as a production universal adapter: +registering the candidate as a production universal evidence pipeline: - Interface declarations now participate in the bounded `extends` hierarchy index used by member lookup. A member inherited through one local interface @@ -591,7 +591,7 @@ The next actionable phases are deliberately still open: measure each stratum on Zod, Axios, and a third framework-heavy corpus. 2. **Accepted precision gate:** hand-label a stratified sample of local, external, unresolved, dynamic, and ambiguous calls/members; compute Wilson - intervals and set release thresholds before enabling any adapter profile. + intervals and set release thresholds before enabling any evidence pipeline. 3. **Framework/compiler tiers:** qualify React/JSX, Node, Express, Jest, Next/Vite, decorators, `const enum`, namespaces, and modern TS 5.x syntax; compare tree-sitter-only, optional compiler-oracle, and Graphify/SCIP @@ -603,8 +603,8 @@ The next actionable phases are deliberately still open: ### Execution checkpoint (2026-08-06, callable/generic-flow and inline-anchor slices) -The candidate-only implementation now covers several additional source-proven -paths while remaining deliberately absent from `UNIVERSAL_ADAPTERS`: +The qualification-only implementation now covers several additional source-proven +paths while remaining deliberately absent from `UNIVERSAL_EVIDENCE_PIPELINES`: - Callable property signatures such as `getter: () => T` preserve their direct return type, including generic constraints, so a chain like @@ -652,7 +652,7 @@ production artifacts were byte-identical; the qualification summary reported 28 edge kinds, 45 node kinds, 57 languages, 27 flows, 980 coverage records, and the stable graph digest `sha256:f13848fefa81b79c70ed9b50081c1cab8024f1ce84030064c8eb2d154ba4c160`. -The focused 43-test candidate suite, package Clippy gate, formatting check, +The focused 43-test universal-evidence suite, package Clippy gate, formatting check, product-boundary check, and 147-test benchmark suite pass. The broader `compass-languages --tests` run still exposes the pre-existing registry expectation mismatch (`Rust` reports 15 capabilities while that test expects @@ -666,7 +666,7 @@ The next actionable phases remain open: measure each stratum on Zod, Axios, and a third framework-heavy corpus. 2. **Accepted precision gate:** hand-label a stratified sample of local, external, unresolved, dynamic, and ambiguous calls/members; compute Wilson - intervals and set release thresholds before enabling any adapter profile. + intervals and set release thresholds before enabling any evidence pipeline. 3. **Framework/compiler tiers:** qualify React/JSX, Node, Express, Jest, Next/Vite, decorators, `const enum`, namespaces, and modern TS 5.x syntax; compare tree-sitter-only, optional compiler-oracle, and Graphify/SCIP @@ -689,7 +689,7 @@ checker as Compass runtime behavior: capability, and is bounded to 128 MiB. Normal native tests and builds do not write it. - `benchmarks/performance/compass/typescript_scorecard.py` validates the - reviewed `compass.typescript-target-scorecard/1` contract. Every record must + reviewed `compass.typescript-target-scorecard/2` contract. Every record must have an explicit `accepted`/`source_oracle` pool, a manually entered `judgmentSource: "manual"`, and a judgment (with a review reason for every non-correct label); missing labels, unsafe paths, duplicate or unsorted IDs, @@ -708,8 +708,8 @@ checker as Compass runtime behavior: This closes the measurement contract but not the quality gate. The four-corpus release sample, hand-labeled accepted records, Graphify/SCIP equivalent-scope -results, framework strata, and production hard cut remain open. The candidate -adapter remains absent from `UNIVERSAL_ADAPTERS`. +results, framework strata, and production hard cut remain open. The qualifying +pipeline remains absent from `UNIVERSAL_EVIDENCE_PIPELINES`. ### Execution checkpoint (2026-08-06, universal module-index and re-export slice) @@ -740,8 +740,8 @@ universal index does not yet consume the full project resolver for `paths`, `rootDirs`, package `exports`/`imports`, `typesVersions`, or all Node16/ NodeNext/Bundler conditions; declaration merging, generic instantiation, mapped/indexed shapes, framework tiers, compiler differential recall, and the -accepted precision/leadership scorecard remain open. The candidate adapter is -still not registered in `UNIVERSAL_ADAPTERS`. +accepted precision/leadership scorecard remain open. The qualifying evidence pipeline is +still not registered in `UNIVERSAL_EVIDENCE_PIPELINES`. ### Execution checkpoint (2026-08-06, project-resolver handoff slice) @@ -767,13 +767,13 @@ paths both pass the same project-edge handoff. This closes the first universal/project-module integration gap, not the Plan 013 release gate. Declaration merging, generic instantiation, mapped/indexed shapes, framework/compiler strata, hand-labeled precision and recall, and the -production hard cut remain open. The candidate adapter remains absent from -`UNIVERSAL_ADAPTERS` until those gates and the exact-release qualification +production hard cut remain open. The qualifying evidence pipeline remains absent from +`UNIVERSAL_EVIDENCE_PIPELINES` until those gates and the exact-release qualification complete. ### Execution checkpoint (2026-08-06, imported declaration-merging slice) -The TypeScript candidate path now preserves the source binding when a value is +The TypeScript evidence pipeline now preserves the source binding when a value is annotated with an imported nominal type (`value: ImportedType`). The receiver proof no longer collapses to a bare qualified spelling, so member calls and accesses carry the imported binding identity and a qualified target such as @@ -792,13 +792,13 @@ binding identity and member qualification. This advances declaration merging and imported nominal receiver quality but is not a full TypeScript type system. Generic instantiation beyond bounded source shapes, mapped/indexed conditional types, framework tiers, compiler -differential recall, and the production hard cut remain open. The candidate -adapter remains absent from `UNIVERSAL_ADAPTERS` until the mandatory quality +differential recall, and the production hard cut remain open. The qualifying +pipeline remains absent from `UNIVERSAL_EVIDENCE_PIPELINES` until the mandatory quality and exact-release qualification gates complete. ### Execution checkpoint (2026-08-06, bounded generic member-chain slice) -The candidate adapter now publishes a bounded declaration-shape fragment in +The qualifying evidence pipeline now publishes a bounded declaration-shape fragment in the existing `DeclarationFact.signature` field: generic class/interface parameter order and direct property nominal types. Imported generic receiver arguments are canonicalized to their proven module-qualified identities and @@ -821,8 +821,8 @@ TypeScript candidate suite pass. This is a bounded generic propagation slice, not compiler-equivalent TypeScript. Nested mapped/conditional/indexed types, overload-aware generic substitution, framework tiers, compiler differential recall, and the -production hard cut remain open. The candidate adapter remains absent from -`UNIVERSAL_ADAPTERS` until the mandatory quality and exact-release +production hard cut remain open. The qualifying evidence pipeline remains absent from +`UNIVERSAL_EVIDENCE_PIPELINES` until the mandatory quality and exact-release qualification gates complete. ### Execution checkpoint (2026-08-06, recursive nested generic member-chain slice) @@ -842,8 +842,8 @@ universal resolver suite is now 140 tests and the TypeScript candidate suite remains 44 tests. Unsupported structural, mapped, conditional, indexed, overload-dependent, or over-limit shapes still fail closed; framework tiers, compiler differential recall, project-corpus qualification, and the production -hard cut remain open. The candidate adapter remains absent from -`UNIVERSAL_ADAPTERS` until the mandatory quality and exact-release +hard cut remain open. The qualifying evidence pipeline remains absent from +`UNIVERSAL_EVIDENCE_PIPELINES` until the mandatory quality and exact-release qualification gates complete. ### Execution checkpoint (2026-08-06, generic alias and indexed-member slice) @@ -869,7 +869,7 @@ bounded by the existing member/export candidate limits and type-shape byte and depth limits; mapped/conditional/indexed access beyond a direct index value, overload-dependent substitutions, framework tiers, compiler differential recall, project-corpus qualification, and the production hard cut remain -open. The candidate adapter remains absent from `UNIVERSAL_ADAPTERS` until the +open. The qualifying evidence pipeline remains absent from `UNIVERSAL_EVIDENCE_PIPELINES` until the mandatory quality and exact-release qualification gates complete. ### Execution checkpoint (2026-08-06, bounded local reassignment slice) @@ -893,8 +893,8 @@ branch/unknown/compound negative cases, and a resolver-level exact-target assertion. The TypeScript/JavaScript candidate suite is now 52 tests and the universal resolver suite is now 147 tests. Alias escape, `eval`, proxy/dynamic property mutation, interprocedural flow, and compiler differential recall remain -open Phase 4 work; the candidate adapter remains absent from -`UNIVERSAL_ADAPTERS` until the mandatory quality and exact-release gates pass. +open Phase 4 work; the qualifying evidence pipeline remains absent from +`UNIVERSAL_EVIDENCE_PIPELINES` until the mandatory quality and exact-release gates pass. ### Execution checkpoint (2026-08-06, homomorphic mapped-alias slice) @@ -921,7 +921,7 @@ This remains a bounded evidence slice, not compiler-equivalent mapped-type assignability: modifier-rich mapped types, conditional/infer transforms, indexed access beyond direct value shapes, alias escape, dynamic mutation, framework tiers, compiler differential recall, and the production hard cut -remain open. The candidate adapter stays out of `UNIVERSAL_ADAPTERS` until the +remain open. The qualifying evidence pipeline stays out of `UNIVERSAL_EVIDENCE_PIPELINES` until the mandatory precision, determinism, qualification, and exact-release gates pass. ### Execution checkpoint (2026-08-06, bounded sequence-index slice) @@ -948,7 +948,7 @@ regression; the focused candidate suite is now 62 tests and the universal resolver suite is now 149 tests. Arbitrary structural indexed access, conditional/mapped modifier semantics, alias escape, dynamic mutation, framework tiers, compiler differential recall, and the production hard cut -remain open. The candidate adapter remains absent from UNIVERSAL_ADAPTERS. +remain open. The qualifying evidence pipeline remains absent from UNIVERSAL_EVIDENCE_PIPELINES. ### Execution checkpoint (2026-08-06, bounded generic callable-return slice) @@ -971,8 +971,8 @@ uniquely determine its type parameters: The slice adds five direct candidate regressions and one resolver regression; the focused candidate suite is now 67 tests and the universal resolver suite -is now 150 tests. The production candidate adapter remains absent from -`UNIVERSAL_ADAPTERS`; accepted precision/Wilson gates, cross-file callable +is now 150 tests. The production qualifying evidence pipeline remains absent from +`UNIVERSAL_EVIDENCE_PIPELINES`; accepted precision/Wilson gates, cross-file callable return evidence, framework/compiler tiers, equivalent Graphify/SCIP scope, and the production hard cut remain open. @@ -999,7 +999,7 @@ focused candidate suite is now 70 tests and the universal resolver suite is now 150 tests. Imported callable-return evidence, richer conditional/mapped modifier semantics, accepted precision/Wilson gates, framework/compiler tiers, equivalent Graphify/SCIP scope, and the production hard cut remain open. The -candidate adapter remains absent from `UNIVERSAL_ADAPTERS`. +qualifying evidence pipeline remains absent from `UNIVERSAL_EVIDENCE_PIPELINES`. ### Execution checkpoint (2026-08-06, imported callable-return evidence slice) @@ -1026,8 +1026,8 @@ resolver suite is now 152 tests. Imported callable member-method returns, explicit generic-call arguments without inferable value arguments, richer conditional/mapped modifier semantics, accepted precision/Wilson gates, framework/compiler tiers, equivalent Graphify/SCIP scope, and the production -hard cut remain open. The candidate adapter remains absent from -`UNIVERSAL_ADAPTERS`. +hard cut remain open. The qualifying evidence pipeline remains absent from +`UNIVERSAL_EVIDENCE_PIPELINES`. ### Execution checkpoint (2026-08-06, imported callable-member and explicit-generic slice) @@ -1051,8 +1051,8 @@ resolver suite is now 154 tests. Conditional/mapped modifier semantics, framework/compiler tiers, accepted precision/Wilson gates, equivalent Graphify/SCIP scope, imported callable properties, alias escape/eval/proxy flow, overload-aware generic substitution beyond bounded source shapes, and -the production hard cut remain open. The candidate adapter remains absent -from `UNIVERSAL_ADAPTERS`. +the production hard cut remain open. The qualifying evidence pipeline remains absent +from `UNIVERSAL_EVIDENCE_PIPELINES`. ### Execution checkpoint (2026-08-06, callable-property and typed-value slice) @@ -1081,8 +1081,8 @@ byte-stable at Compass revision `0d051868` with graph digest Conditional/mapped modifier semantics, framework/compiler tiers, accepted precision/Wilson gates, equivalent Graphify/SCIP scope, alias escape/eval/ proxy flow, overload-aware generic substitution beyond bounded source shapes, -and the production hard cut remain open. The candidate adapter remains absent -from `UNIVERSAL_ADAPTERS`. +and the production hard cut remain open. The qualifying evidence pipeline remains absent +from `UNIVERSAL_EVIDENCE_PIPELINES`. ### Execution checkpoint (2026-08-06, bounded imported-overload selection slice) @@ -1103,13 +1103,13 @@ exact, bounded argument shape: limits; it emits no new external or deferred targets and preserves existing member-binding provenance for unique matches. -The focused candidate suite remains at 73 tests and the universal resolver +The focused universal-evidence suite remains at 73 tests and the universal resolver suite is now 158 tests, including one positive imported-overload fixture and one ambiguity/mismatch/unknown negative fixture. Conditional/mapped modifier semantics, richer overload-aware generic substitution, alias escape/eval/proxy flow, framework/compiler tiers, accepted precision/Wilson gates, equivalent -Graphify/SCIP scope, and the production hard cut remain open. The candidate -adapter remains absent from `UNIVERSAL_ADAPTERS`. Fixture qualification stays +Graphify/SCIP scope, and the production hard cut remain open. The qualifying +pipeline remains absent from `UNIVERSAL_EVIDENCE_PIPELINES`. Fixture qualification stays byte-stable at Compass revision `24f937360cc80d6b62b633f2fdd2a367eb6529c3`, with 57 languages, 980 coverage records, 1,565 invariants, 27 flows, and graph digest `sha256:f13848fefa81b79c70ed9b50081c1cab8024f1ce84030064c8eb2d154ba4c160`. @@ -1133,8 +1133,8 @@ receiver identity through safe straight-line aliases: The focused candidate suite is now 75 tests and the universal resolver suite remains 158 tests; the overload fixture also proves an imported call-result can -be aliased before its returned member is resolved. The candidate adapter remains -absent from `UNIVERSAL_ADAPTERS`. Conditional/mapped modifier semantics, richer +be aliased before its returned member is resolved. The qualifying evidence pipeline remains +absent from `UNIVERSAL_EVIDENCE_PIPELINES`. Conditional/mapped modifier semantics, richer interprocedural flow, framework/compiler tiers, accepted precision/Wilson gates, equivalent Graphify/SCIP scope, and the production hard cut remain open. @@ -1162,7 +1162,7 @@ TypeScript containers or unknown APIs: The focused candidate suite is now 77 tests and the universal resolver suite is 159 tests, including resolver publication checks for callable references with no indirect-call edge and candidate checks for unique/ambiguous `in` guards. -The candidate adapter remains absent from `UNIVERSAL_ADAPTERS`. Conditional and +The qualifying evidence pipeline remains absent from `UNIVERSAL_EVIDENCE_PIPELINES`. Conditional and mapped modifier semantics beyond the bounded utility/union rules, richer interprocedural flow, framework/compiler tiers, accepted precision/Wilson gates, equivalent Graphify/SCIP scope, and the production hard cut remain open. @@ -1184,8 +1184,8 @@ subset of utility receivers while preserving fail-closed behavior: source anchors, occurrence ranges, and member direction remain unchanged. The focused candidate suite is now 79 tests and the universal resolver suite -remains 159 tests. The candidate adapter remains absent from -`UNIVERSAL_ADAPTERS`. Conditional and mapped modifier semantics beyond these +remains 159 tests. The qualifying evidence pipeline remains absent from +`UNIVERSAL_EVIDENCE_PIPELINES`. Conditional and mapped modifier semantics beyond these bounded utility/union rules, richer interprocedural flow, framework/compiler tiers, accepted precision/Wilson gates, equivalent Graphify/SCIP scope, and the production hard cut remain open. @@ -1209,8 +1209,8 @@ source-proven subset of conditional and mapped receiver types: occurrence multiplicity, and published declaration identities are unchanged. The focused candidate suite is now 81 tests and the universal resolver suite -remains 160 tests. The candidate adapter remains absent from -`UNIVERSAL_ADAPTERS`. Broader distributive conditional semantics, indexed/keyof +remains 160 tests. The qualifying evidence pipeline remains absent from +`UNIVERSAL_EVIDENCE_PIPELINES`. Broader distributive conditional semantics, indexed/keyof evaluation, richer interprocedural flow, framework/compiler tiers, accepted precision/Wilson gates, equivalent Graphify/SCIP scope, and the production hard cut remain open. @@ -1238,8 +1238,8 @@ bounded project model before measuring source recall: The source oracle remains qualification-only and emits source constructs, not targets; its output is still a deterministic JSON payload rather than the final -JSONL evidence stream described by Phase 0. The candidate adapter remains -absent from `UNIVERSAL_ADAPTERS`. Project-manifest corpora, compiler-backed +JSONL evidence stream described by Phase 0. The qualifying evidence pipeline remains +absent from `UNIVERSAL_EVIDENCE_PIPELINES`. Project-manifest corpora, compiler-backed scope/declaration inventories, broader conditional/keyof semantics, framework tiers, accepted precision/Wilson gates, equivalent Graphify/SCIP scope, and the production hard cut remain open. @@ -1266,8 +1266,8 @@ property: anchors, direction, multiplicity, and deterministic ordering. The focused TypeScript candidate suite is now 83 tests and the universal -resolver suite is now 162 tests. The candidate adapter remains absent from -`UNIVERSAL_ADAPTERS`. Broader `keyof`/mapped/indexed evaluation, distributive +resolver suite is now 162 tests. The qualifying evidence pipeline remains absent from +`UNIVERSAL_EVIDENCE_PIPELINES`. Broader `keyof`/mapped/indexed evaluation, distributive conditional semantics, project-manifest corpora, compiler-backed scope and declaration inventories, framework tiers, accepted precision/Wilson gates, equivalent Graphify/SCIP scope, and the production hard cut remain open. @@ -1293,8 +1293,8 @@ additional source-proven type shapes: `keyofItem` as ordinary names. The focused TypeScript candidate suite is now 86 tests and the universal -resolver suite is now 162 tests. The candidate adapter remains absent from -`UNIVERSAL_ADAPTERS`. Full `keyof` value-space evaluation, arbitrary +resolver suite is now 162 tests. The qualifying evidence pipeline remains absent from +`UNIVERSAL_EVIDENCE_PIPELINES`. Full `keyof` value-space evaluation, arbitrary mapped/indexed projections, distributive conditional semantics, project-manifest corpora, compiler-backed scope and declaration inventories, framework tiers, accepted precision/Wilson gates, equivalent Graphify/SCIP @@ -1399,7 +1399,7 @@ precision/Wilson, framework, comparator, and production-hard-cut gates. ### Execution checkpoint (2026-08-07, bounded inline structural index receivers) -The native TypeScript/JavaScript candidate path now carries one additional +The native TypeScript/JavaScript evidence pipeline now carries one additional source-proven structural shape across dynamic member access: - Inline index signatures on parameters, variables, and properties cache their @@ -1590,7 +1590,7 @@ were previously conflated: target. Ordinary ES namespace members continue through the same exact module/export path, while direct calls through non-callable ES namespaces remain unresolved. -- The candidate adapter versions advanced to 3 for the binding identity +- The qualifying evidence pipeline versions advanced to 3 for the binding identity change. Focused coverage is now 91 candidate tests and 168 universal resolver tests, including aliased named requires, namespace member calls, callable CommonJS defaults, and dynamic/nested negative patterns. @@ -1708,7 +1708,7 @@ Spread-free ES default object literals now publish a source-backed `default` value owner and retain their exact property declarations beneath the stable `.default` qualified identity. Cross-file default imports can therefore resolve `value.member()` and `value.member` to the provider property or method -declaration. The adapter emits an explicit default reexport binding for the +declaration. The producer emits an explicit default reexport binding for the synthetic owner. Default objects containing spreads remain outside this path; their member resolution stays unresolved/external rather than assuming that a listed property survives an override. The candidate and resolver tests cover @@ -1791,7 +1791,7 @@ escape hatch: inherited spread properties remain unresolved until a dedicated member-alias contract preserves their original provider identity. - Unknown/imported/computed/conditional/mutable/ambiguous spread operands and - direct properties before an export spread remain unresolved. Candidate adapter + direct properties before an export spread remain unresolved. Qualifying evidence pipeline versions advanced to 4 to invalidate qualification-only evidence meaningfully. The focused candidate suite remains 99 tests and the universal resolver suite @@ -1835,7 +1835,7 @@ the reverse) still require the same exact source proof. The focused candidate suite remains 99 tests and the universal resolver suite is now 172 tests. New regressions cover local and imported owner markers, cross-file inherited member publication, direct-property precedence, competing -spread sources, and spreading a non-object default. Candidate adapter versions +spread sources, and spreading a non-object default. Qualifying evidence pipeline versions advanced to 5 so the qualification-only evidence cache cannot reuse the prior owner-less spread realization. @@ -1846,7 +1846,7 @@ graph digest `sha256:f13848fefa81b79c70ed9b50081c1cab8024f1ce84030064c8eb2d154ba4c160`, 82 exact/11 unresolved route resolutions, and byte-equivalent clean, warm, rebuild, restored, and alternate-checkout outputs. The unchanged digest is -expected because the candidate adapter remains unregistered in production. +expected because the qualifying evidence pipeline remains unregistered in production. The read-only Axios target differential remains 3,054/3,167 exact local targets (113 missing, 0 wrong; 391/503 member targets and 1,544/1,545 call targets, 228 accepted-candidate false positives); it remains diagnostic and @@ -1859,7 +1859,7 @@ equivalent Graphify/SCIP scope, and the production hard cut remain open. The qualification-only CommonJS path now carries the same bounded owner contract as safe ES default/export-assignment objects. For -`module.exports = { ...base, direct }`, the adapter emits the module's exact +`module.exports = { ...base, direct }`, the producer emits the module's exact default reexport, a source-range-backed `Member("*")` alias for each proven local or static default-import object source, and ordinary named reexports for direct properties after the spread. The resolver treats the module declaration @@ -1883,7 +1883,7 @@ kinds. The graph digest remains `sha256:f13848fefa81b79c70ed9b50081c1cab8024f1ce84030064c8eb2d154ba4c160`, with 82 exact and 11 unresolved route resolutions; clean, warm, rebuild, restored, alternate-checkout, and source-fixture byte comparisons all pass. -The unchanged digest is expected because this qualification-only adapter is +The unchanged digest is expected because this qualification-only producer is not registered in production. The independent Axios diagnostic is unchanged at 3,054/3,167 exact local targets (113 missing, 0 wrong; 391/503 members, 1,544/1,545 calls, and 228 accepted-candidate false positives); it is not a @@ -1922,7 +1922,7 @@ diagnostics, 28 edge kinds, and 45 node kinds. The graph digest remains `sha256:f13848fefa81b79c70ed9b50081c1cab8024f1ce84030064c8eb2d154ba4c160`, with 82 exact and 11 unresolved route resolutions; clean, warm, rebuild, restored, alternate-checkout, and source-fixture byte comparisons all pass. -The unchanged digest is expected because the qualification-only adapter is not +The unchanged digest is expected because the qualification-only producer is not registered in production. This remains correctness evidence, not a leadership claim. @@ -1945,7 +1945,7 @@ resolver coverage now includes positive source inheritance, direct override, mutating `exports`, and unknown-source negatives; the focused suites are 102 candidate tests and 176 universal resolver tests. -Exact fixture qualification remains unchanged because this adapter is still +Exact fixture qualification remains unchanged because this producer is still qualification-only and is not registered in production: 57 languages, 980 coverage records, 1,565 invariants, 27 flows, 24 negatives, 25 diagnostics, 28 edge kinds, 45 node kinds, graph digest @@ -1969,8 +1969,8 @@ Regression coverage now includes direct value exports, imported getter exports, the `__esModule` marker, dynamic keys/values, and shadowed globals, plus a cross-file `require()` consumer that resolves the exact value and getter targets while leaving an unknown export unresolved. The focused suites are -103 candidate tests and 177 universal resolver tests. This remains a -qualification-only correctness slice; the production adapter is still not +103 universal-evidence tests and 177 universal resolver tests. This remains a +qualification-only correctness slice; the production pipeline is still not registered and no release-leadership claim is implied. ### Execution checkpoint (2026-08-07, bounded CommonJS `__exportStar` barrels) @@ -1981,7 +1981,7 @@ The CommonJS bridge now recognizes the source-proven barrel form contains the bounded TypeScript/Babel export-star guard shape or its binding comes from `tslib`; the require argument must be a static string and the destination must be the unshadowed `exports` or `module.exports` object. The -adapter emits a source-range-backed `Member("*")` owner alias rather than +producer emits a source-range-backed `Member("*")` owner alias rather than inventing one binding per unknown property. The shared resolver projects both named imports/destructured `require()` @@ -1991,7 +1991,7 @@ arguments, wrong destinations, lookalike helpers, and unresolved source members remain unresolved. Candidate and resolver coverage now includes local and `tslib` helpers plus all negative forms; the focused suites are 104 candidate tests and 178 universal resolver tests. Exact fixture qualification -remains unchanged because the adapter is still qualification-only: 57 +remains unchanged because the producer is still qualification-only: 57 languages, 980 coverage records, 1,565 invariants, 27 flows, 24 negatives, 25 diagnostics, 28 edge kinds, 45 node kinds, graph digest `sha256:f13848fefa81b79c70ed9b50081c1cab8024f1ce84030064c8eb2d154ba4c160`, @@ -2021,13 +2021,13 @@ calls instead of treating the template body as an ordinary argument list: or oversized template cannot create an unbounded argument vector. Candidate and resolver coverage now stand at 105 and 179 focused tests. This -remains qualification-only; the adapter is still not registered in -`UNIVERSAL_ADAPTERS`, and compiler differential, framework, competitor, and +remains qualification-only; the producer is still not registered in +`UNIVERSAL_EVIDENCE_PIPELINES`, and compiler differential, framework, competitor, and production hard-cut gates remain open. ### Execution checkpoint (2026-08-07, bounded decorator-factory evidence) -Decorator syntax now has a first-class, source-backed candidate path instead +Decorator syntax now has a first-class, source-backed evidence pipeline instead of relying on the nested call traversal: - Direct decorators (`@Injectable`, `@Controller(...)`, and local decorator @@ -2044,8 +2044,8 @@ of relying on the nested call traversal: cross-file through the shared resolver. Overload/arity/type mismatches and ambiguous members remain unresolved rather than being selected by spelling. -Candidate and resolver coverage now stand at 107 and 180 focused tests. The -adapter remains qualification-only and unregistered; compiler differential, +Universal-evidence and resolver coverage now stand at 107 and 180 focused tests. The +pipeline remains qualification-only and unregistered; compiler differential, framework-contract, competitor, production hard-cut, and leadership gates remain open. @@ -2069,7 +2069,7 @@ were previously easy to lose during qualification: The candidate test suite is now 108 tests and the resolver suite remains 180; focused oracle correctness tests pass. This is still a qualification-only -slice: the universal adapter is unregistered, the legacy/native hard cut is +slice: the universal evidence pipeline is unregistered, the legacy/native hard cut is open, and compiler-differential, framework, competitor, release-corpus, and leadership gates remain required. @@ -2147,7 +2147,7 @@ current mapping. The current implementation also has architectural ceilings: - TypeScript, TSX, and JavaScript still use the established direct extraction path in `crates/compass-languages/src/engine.rs`; only Go, Java, Python, and - Rust have registered universal evidence adapters. + Rust have registered universal evidence pipelines. - TypeScript parsing still uses byte-preserving masks for grammar gaps such as variance, `export type *`, and import-type syntax. - project evidence now accepts bounded JSONC and records alias markers, while @@ -2209,7 +2209,7 @@ strata, require: - at least 2,000 independently accepted sampled relationships; - at least 400 accepted relationships per release-gate corpus; -- at least 100 accepted relationships for each required relation and adapter +- at least 100 accepted relationships for each required relation and producer capability; - no target cluster larger than 10% of accepted samples; - observed precision at least 99.5% and a 95% Wilson lower bound at least 99%; @@ -2249,7 +2249,7 @@ results as if they were the same product contract. | Concern | Current evidence | Owner / likely files | |---|---|---| | language and dialect registry | TS/TSX/JS share generic extraction | `crates/compass-languages/src/registry.rs`, `engine.rs` | -| universal adapter registry | only Go/Java/Python/Rust | `crates/compass-languages/src/adapters.rs` | +| universal evidence registry | C#, Go, Java, JavaScript, Kotlin, PHP, Python, Ruby, Rust, TypeScript | `crates/compass-languages/src/evidence_pipeline.rs` | | direct typed evidence | language switch has no TS/JS branch | `crates/compass-languages/src/evidence/build.rs`, `model.rs`, `validate.rs` | | project config markers | strict JSON and partial alias collection | `crates/compass-languages/src/project_evidence.rs`, `json_config.rs` | | module/package resolution | relative extensions, index, exports subset | `crates/compass-resolve/src/lib.rs` | @@ -2274,7 +2274,7 @@ live code before implementation. `crates/compass-languages/src/evidence/build.rs` has no TS/JS universal branch: ```rust -match profile.language { +match pipeline.producer.language { "python" => state.extract_python(root)?, "go" => state.extract_go(root)?, "java" => state.extract_java(root)?, @@ -2304,11 +2304,11 @@ its ambiguity behavior. ```rust .filter_map(|extraction| extraction.semantic_evidence.as_ref()) -.filter(|batch| batch.adapter.language == "java") +.filter(|batch| batch.pipeline.language == "java") ``` Phase 6 should generalize this exact-anchor contract only after TS/JS are -hard-cut universal adapters; it should not add a second TS/JS graph path. +hard-cut universal evidence pipelines; it should not add a second TS/JS graph path. ## Reference semantics @@ -2550,7 +2550,7 @@ the test-only universal path without duplicating config semantics. `crates/compass-languages/src/evidence/build.rs` currently dispatches direct universal extraction only for Python, Go, Java, and Rust. The TypeScript/JS extractor must emit typed evidence directly from the parser tree; translating -legacy raw graph records is not an acceptable adapter. +legacy raw graph records is not an acceptable producer. ### Actions @@ -2582,8 +2582,8 @@ legacy raw graph records is not an acceptable adapter. 5. Emit parser-recovery diagnostics with exact affected ranges and evidence completeness. Never publish a construct outside a source-backed range just because the root parser recovered. -6. Expose the adapter only to focused tests/qualification at this phase. Do not - add it to `UNIVERSAL_ADAPTERS` yet. +6. Expose the producer only to focused tests/qualification at this phase. Do not + add it to `UNIVERSAL_EVIDENCE_PIPELINES` yet. ### Verification @@ -2600,7 +2600,7 @@ legacy raw graph records is not an acceptable adapter. ### Exit gate -Every capability claimed in the future adapter profiles has direct validated +Every capability claimed in the future evidence pipelines has direct validated evidence and a positive, ambiguity, negative, and boundary test. Do not register a capability that is merely inferred later from a legacy graph edge. @@ -2733,8 +2733,8 @@ or conflicting graphs and conceal semantic drift. ### Actions -1. Add separate `compass.typescript` and `compass.javascript` adapter profiles - with exact capability lists and new adapter versions. Map TSX/MTS/CTS and +1. Add separate `compass.typescript` and `compass.javascript` producer pipelines + with exact capability lists and new producer versions. Map TSX/MTS/CTS and JSX/MJS/CJS through their semantic family while preserving dialect evidence. 2. Switch production extraction to direct parser-tree universal evidence in one commit. Do not publish legacy raw and universal facts in the same build. @@ -2961,8 +2961,8 @@ corpus minimum, exact-SHA check, or equivalent-configuration review fails. ## Done criteria -- [ ] TypeScript and JavaScript have direct parser-tree universal adapters; no - production adapter translates legacy raw graph records. +- [ ] TypeScript and JavaScript have direct parser-tree universal evidence pipelines; no + production publisher translates legacy raw graph records. - [ ] Parser dialect and semantic language family are distinct and exact JS/TS interop never enables generic cross-language matching. - [ ] The typed project model covers the supported TypeScript config, workspace, @@ -3002,7 +3002,7 @@ Stop and report rather than weakening the design if: first-candidate selection, or an unbounded barrel/project/package traversal; - a normal build would require Node.js, a compiler, SCIP, credentials, network, runtime grammar downloads, or Graphify; -- the universal adapter cannot pass mandatory gates before production cutover; +- the universal evidence pipeline cannot pass mandatory gates before production cutover; - the hard cut requires dual production publication or an automatic legacy fallback; - an existing framework route/template contract regresses without an approved @@ -3017,7 +3017,7 @@ Stop and report rather than weakening the design if: ## Maintenance notes -- Adapter, project-model, evidence, cache, and qualification versions are +- Producer, project-model, evidence, cache, and qualification versions are contracts. Bump the narrowest version when meaning changes and reject unknown majors explicitly. - Keep historical corpus realizations and published scorecards immutable. Add a diff --git a/advisor-plans/019-ruby-universal-candidate.md b/advisor-plans/019-ruby-universal-qualification.md similarity index 94% rename from advisor-plans/019-ruby-universal-candidate.md rename to advisor-plans/019-ruby-universal-qualification.md index bfd127b9..0fe2ac16 100644 --- a/advisor-plans/019-ruby-universal-candidate.md +++ b/advisor-plans/019-ruby-universal-qualification.md @@ -1,4 +1,4 @@ -# Plan 019: Hard-cut Ruby to a qualified universal candidate +# Plan 019: Hard-cut Ruby to a qualifying universal evidence pipeline > **Executor instructions**: Deliver this as a sequence of reviewable PRs. Read > this plan completely, then read `AGENTS.md`, @@ -8,12 +8,12 @@ > `docs/reference/universal-semantic-evidence.md` before changing source. Run > every verification gate and confirm its expected result before starting the > next phase. Ruby must not have production dual-running: keep the established -> path active while the candidate is qualification-only, then switch the +> path active while the pipeline is qualification-only, then switch the > registry, Ruby publisher, resolver, and Rails source pack atomically. > > **Drift check (run before every phase)**: > `git diff --stat b53c3ea2..HEAD -- crates/compass-files crates/compass-languages crates/compass-resolve crates/compass-model crates/compass-graph crates/compass-core fixtures/code-graph tests/qualification scripts benchmarks/performance docs PERFORMANCE.md COMPATIBILITY.md MIGRATION.md CHANGELOG.md advisor-plans` -> Reconcile any changed adapter versions, evidence fields, Ruby/Rails paths, +> Reconcile any changed producer versions, evidence fields, Ruby/Rails paths, > qualification thresholds, or cache contracts before proceeding. If live code > contradicts the “Current state” section, stop and update this plan first. @@ -31,7 +31,7 @@ ### Execution status (2026-08-17) The implementation has completed the semantic hard cut through Phase 5. Ruby -is now published through `compass.ruby.candidate` and the `rails-ruby` +is now published through `compass.ruby` and the `rails-ruby` universal pack, with the old Ruby publisher/resolver removed from production. Phase 0 source-oracle and pinned-commit evidence is complete. Phase 6 now has a corrected file-only incremental publication path plus copy-on-write snapshot @@ -53,7 +53,7 @@ parser-recovery diagnostics, and coverage instead of reclassifying cached files as extracted; the strict fixture restore gate passes byte-for-byte. Phase 7 audit gates now pass: 89,981 accepted relationships, 100% observed precision, 98.5567% source-oracle recall, zero ambiguity, and zero critical -violations. Ruby remains intentionally `UniversalCandidate`; promotion is a +violations. Ruby remains intentionally `Qualifying`; promotion is a separate decision and has not been made here. ## Why this matters @@ -73,7 +73,7 @@ anchors, limits, ambiguity, provenance, incremental caching, and framework facts are enforced consistently. The intended endpoint of this plan is a hard-cut version-1 Ruby -`UniversalCandidate`, not `UniversalComplete`. Candidate promotion remains +`Qualifying`, not `Qualified`. Promotion remains blocked on the independent 2,000-relationship quality audit and all precision, recall, and zero-tolerance gates in `docs/reference/universal-semantic-evidence.md`. @@ -100,9 +100,9 @@ recall, and zero-tolerance gates in and otherwise resolves capitalized receivers or a missing `receiver_type`. It has no lexical constant path, require/load evidence, reopen handling, or separate singleton/instance member space. -- `crates/compass-languages/src/adapters.rs:270+` has no Ruby adapter profile. +- `crates/compass-languages/src/evidence_pipeline.rs:270+` has no Ruby evidence pipeline. The closest dedicated emitter is `evidence/php.rs`; the closest small - candidate and hard-cut tests are the Kotlin emitter/conformance/resolver + qualifying and hard-cut tests are the Kotlin emitter/conformance/resolver suites. - `crates/compass-resolve/src/evidence/languages/policy.rs:9-30` has no Ruby policy variant. Unknown languages use only generic resolution. @@ -129,8 +129,8 @@ recall, and zero-tolerance gates in ### Post-implementation state The pre-change inventory above is retained as the rationale for the cut. The -delivered tree now has a bounded `evidence/ruby.rs` emitter, a Ruby adapter -profile (`compass.ruby.candidate`, version 1), Ruby method-space-aware +delivered tree now has a bounded `evidence/ruby.rs` emitter, a Ruby producer +and pipeline (`compass.ruby`, version 1, `Qualifying`), Ruby method-space-aware resolution, exact contained `require_relative` decisions, and a single `rails-ruby` universal framework pack. Ruby extraction has no production `RawCall` publisher, and the replaced Ruby member resolver and Rails line/regex @@ -200,7 +200,7 @@ limit, and corpus evidence. The initial target set is: Do not advertise decorators, static type references, reexports, tests, macros, or complete hierarchy dispatch merely because a fixture contains a related syntax form. Literal `attr_reader`/`attr_writer`/`attr_accessor` and -`define_method(:literal)` can land as declarations during the candidate phase, +`define_method(:literal)` can land as declarations during the qualifying phase, but `Macros` becomes an advertised capability only after its own audit stratum passes. @@ -231,7 +231,7 @@ pinned Prism-based oracle; do not reuse Tree-sitter as its own oracle and do not lower the recall gate. Graphify-only facts, if sampled, belong only in the -`graphify_hypothesis` pool defined by `compass.quality-audit`. They are not +`graphify_hypothesis` pool defined by `compass.quality-audit/2`. They are not truth and must never become runtime, fixture, fallback, or CI dependencies. ## Commands executors will need @@ -298,7 +298,7 @@ performance modes without silently changing the production graph. - treating Gemfile gem names as require paths or local declarations; - broadening `compass.graph/1` endpoint validation to admit false Ruby edges; - adding a Ruby runtime dependency to the released Compass binary; -- promoting Ruby to `UniversalComplete` before the complete audit gates pass. +- promoting Ruby to `Qualified` before the complete audit gates pass. ## Git and delivery workflow @@ -321,10 +321,10 @@ performance modes without silently changing the production graph. | 1 | Ruby identity and graph representation contract | No | | 2 | Qualification-only bounded evidence emitter | No | | 3 | Qualification-only project and resolver semantics | No | -| 4 | Universal Rails pack ready behind candidate evidence | No | -| 5 | Atomic hard cut to Ruby `UniversalCandidate` | Yes, once | -| 6 | Measured cold/warm/incremental optimization | Candidate remains active | -| 7 | Complete quality audit and release qualification | Candidate; promotion is separate | +| 4 | Universal Rails pack ready behind qualifying evidence | No | +| 5 | Atomic hard cut to Ruby `Qualifying` | Yes, once | +| 6 | Measured cold/warm/incremental optimization | Qualifying remains active | +| 7 | Complete quality audit and release qualification | Qualifying; promotion is separate | ## Phase 0: Freeze truth, established behavior, and performance @@ -361,7 +361,7 @@ phase creates reproducible evidence before any production Ruby behavior moves. twice and require byte-identical canonical output. 5. Create a small curated baseline of established correct, missing, ambiguous, and incorrect graph facts. Do not translate missing established facts into - candidate requirements unless the source oracle proves them. + qualification requirements unless the source oracle proves them. **Acceptance criteria**: @@ -372,7 +372,7 @@ phase creates reproducible evidence before any production Ruby behavior moves. - the oracle fails closed on malformed/partial input and produces identical bytes on two runs; - build time is excluded from Compass timings and corpus trees remain clean; -- no product source, adapter registration, or production Ruby graph changes in +- no product source, producer registration, or production Ruby graph changes in this phase. **Verify**: the new oracle unit suite and baseline command both exit 0; rerun @@ -429,7 +429,7 @@ canonical bytes in forward and reversed input order. ## Phase 2: Build the qualification-only Ruby evidence emitter **Context**: The emitter must be exercised without registering Ruby in -`UNIVERSAL_ADAPTERS`. Production continues through the established generic +`UNIVERSAL_EVIDENCE_PIPELINES`. Production continues through the established generic path during this phase. **Primary files to add or update**: @@ -598,7 +598,7 @@ framework-pack contract as Spring, ASP.NET, and PHP frameworks. - current Rails symbol, hash-rocket, and namespace fixtures remain exact; - dynamic or ambiguous handler/path/controller forms publish no invented exact edge; -- the universal pack runs only on validated Ruby candidate evidence and does +- the universal pack runs only on validated Ruby qualifying evidence and does not rescan source lines with regex as its semantic authority; - the established `rails-routes` pack remains production-active until Phase 5, but qualification invokes only the universal pack—never both in one graph. @@ -615,7 +615,7 @@ three corpora. **Primary files to update**: -- `crates/compass-languages/src/adapters.rs`; +- `crates/compass-languages/src/evidence_pipeline.rs`; - `crates/compass-languages/src/evidence/mod.rs` and `engine.rs`; - `crates/compass-resolve/src/members.rs`; - framework pack/expansion registries; @@ -625,9 +625,9 @@ three corpora. **Steps**: -1. Add sorted `RUBY_CAPABILITIES` and adapter profile - `id="compass.ruby.candidate"`, `language="ruby"`, `version=1`, - `profile=UniversalCandidate`. +1. Add sorted `RUBY_CAPABILITIES` and producer/pipeline metadata + `id="compass.ruby"`, `language="ruby"`, `version=1`, + `qualification=Qualifying`. 2. Route normal `.rb`, `.rake`, fixed filenames, and Ruby shebang extraction to the dedicated emitter. The same registered emitter must back the hidden qualification API. @@ -636,14 +636,14 @@ three corpora. established languages that share `engine.rs` or `members.rs`. 4. Replace the established `rails-routes` registration with `rails-ruby` and enable its matching expansion adapter in the same commit. -5. Invalidate Ruby cache entries through adapter identity/version. Cached Ruby +5. Invalidate Ruby cache entries through producer identity/version. Cached Ruby entries containing replaced raw nodes/edges/calls must fail compatibility and reextract; non-Ruby caches remain reusable. 6. Expand strict fixture vocabulary/producer assertions for Ruby declarations, trait modules, calls, construction, inheritance, mixins, imports, aliases, and Rails routes. Require zero Ruby publication omissions and identity collisions. -7. Update current-state docs to say hard-cut Ruby `UniversalCandidate`, and +7. Update current-state docs to say hard-cut Ruby `Qualifying`, and explicitly state that complete audit gates remain pending. **Acceptance criteria**: @@ -664,11 +664,11 @@ three corpora. intentionally includes the expanded vocabulary; - unknown-major/version validation, cache rejection, and atomic publication tests pass; -- Ruby remains `UniversalCandidate` in code and documentation. +- Ruby remains `Qualifying` in code and documentation. **Verify**: run every command in “Commands executors will need,” then run the three pinned-corpus qualification mode. Every command exits 0 and each corpus -summary reports schema v1, Ruby adapter v1, zero validation errors, zero Ruby +summary reports schema v1, Ruby producer v1, zero validation errors, zero Ruby publication omissions/collisions, and deterministic comparisons all true. ## Phase 6: Optimize cold, warm, and incremental Ruby builds @@ -715,7 +715,7 @@ bytes before and after each change. - all limits, ambiguity, ordering, and fixture gates remain green. **Verify**: the Ruby performance mode emits a versioned JSON report containing -raw samples, medians, corpus/graph digests, adapter version, changed/reused file +raw samples, medians, corpus/graph digests, producer version, changed/reused file counts, and stage timings; its regression evaluator exits 0. ## Phase 7: Complete the quality audit and release qualification @@ -740,8 +740,8 @@ without weakening them for Ruby's dynamic semantics. 4. Add scheduled exact-commit qualification and a release gate only after the corpus process is reproducible on supported CI infrastructure. Generated graphs and private data remain outside the repository. -5. Promote to `UniversalComplete` only in a separate reviewed change after - every threshold passes. Otherwise retain `UniversalCandidate` and publish +5. Promote to `Qualified` only in a separate reviewed change after + every threshold passes. Otherwise retain `Qualifying` and publish the failing strata as actionable follow-up work. **Acceptance criteria**: @@ -762,11 +762,11 @@ without weakening them for Ruby's dynamic semantics. forced, incremental restore, worker-count, input-order, and relocated-path permutations; - performance gates from Phase 6 pass; -- a failing gate leaves Ruby explicitly `UniversalCandidate` and fails the +- a failing gate leaves Ruby explicitly `Qualifying` and fails the completion/release claim rather than changing thresholds. **Verify**: the checked-in audit validator exits 0 only when all thresholds -above are met and emits a stable `compass.quality-audit` qualification summary. +above are met and emits a stable `compass.quality-audit/2` qualification summary. ## Cross-phase test plan @@ -804,9 +804,9 @@ Use `php_universal_conformance.rs`, `kotlin_universal_conformance.rs`, inventories, and an independent bounded Ruby source oracle. - [x] Ruby identity, reopen, module/trait, and method-space contracts are documented and tested before broad extraction. -- [x] The candidate emitter has independent fixture/oracle coverage and is - the sole production Ruby publisher after the atomic cut; the profile remains - a candidate until the complete corpus gates pass. +- [x] The evidence emitter has independent fixture/oracle coverage and is + the sole production Ruby publisher after the atomic cut; the pipeline remains + `Qualifying` until the complete corpus gates pass. - [x] Ruby resolution never uses terminal-name similarity as unique evidence. - [x] Rails is one universal pack consuming validated Ruby evidence. - [x] The atomic cut removes only Ruby's replaced publisher/resolver/framework @@ -816,7 +816,7 @@ Use `php_universal_conformance.rs`, `kotlin_universal_conformance.rs`, audit has zero critical violations. - [x] Performance gates pass with reproducible reports; RSS is recorded but non-blocking. -- [x] Ruby remains `UniversalCandidate` until every complete audit threshold +- [x] Ruby remains `Qualifying` until every complete audit threshold passes. - [x] Docs, compatibility notes, migration guidance, changelog, and `advisor-plans/README.md` reflect the actual shipped state. @@ -825,7 +825,7 @@ Use `php_universal_conformance.rs`, `kotlin_universal_conformance.rs`, Stop and report rather than improvising if: -- live adapter/evidence/cache contracts differ from this plan's assumptions; +- live producer/evidence/cache contracts differ from this plan's assumptions; - correct Ruby module or method-space representation requires a new public graph kind or weakening endpoint validation; - Ripper cannot provide exact complete source-oracle coverage for a required @@ -856,7 +856,7 @@ Stop and report rather than improvising if: - Rails autoloading and inflection are configurable. Never assume path-to- constant equivalence from snake/camel conversion alone; add a bounded, versioned Zeitwerk evidence design if later qualification proves it necessary. -- Adapter-version increments are required whenever meaning-affecting Ruby +- Producer-version increments are required whenever meaning-affecting Ruby evidence changes. The shared evidence schema version changes only for a true contract-major change. - Reviewers should inspect ambiguity/negative tests before graph-count gains. @@ -880,5 +880,5 @@ Stop and report rather than improvising if: - **Use Graphify as the qualification oracle**: rejected because it is a hypothesis source, not independent truth, and cannot become a Compass runtime/test/fallback dependency. -- **Promote immediately to `UniversalComplete` after hard cut**: rejected - because candidate architecture and audited quality are separate gates. +- **Promote immediately to `Qualified` after hard cut**: rejected + because pipeline architecture and audited quality are separate gates. diff --git a/advisor-plans/README.md b/advisor-plans/README.md index 98df5d46..d8996cab 100644 --- a/advisor-plans/README.md +++ b/advisor-plans/README.md @@ -34,14 +34,14 @@ advisory risk separate from deterministic merge gates. Plans 015–018 are self-contained notebook, PHP framework, execution-flow, and MCP workflow programs planned at Compass commit `6680842c` on 2026-08-10. -Plan 019 is the Ruby universal-candidate program. It was planned at Compass +Plan 019 is the Ruby universal-evidence program. It was planned at Compass commit `b53c3ea2` on 2026-08-16. It freezes established Ruby evidence, builds an -independent Ripper oracle and qualification-only adapter, adds conservative +independent Ripper oracle and qualification-only producer, adds conservative Ruby project/resolution semantics, converts Rails to a universal framework pack, performs one atomic hard cut, and then measures optimization and complete quality gates. The pinned three-corpus audit now passes (89,981 accepted relationships, 100% observed precision, 98.5567% recall); Ruby remains -`UniversalCandidate` until a separate promotion decision. +`Qualifying` until a separate promotion decision. ## Execution order and status @@ -65,7 +65,7 @@ relationships, 100% observed precision, 98.5567% recall); Ruby remains | 016 | Complete Composer, Blade, and Eloquent framework resolution | P1 | L | — | TODO | | 017 | Derive bounded, ranked execution flows from entry points | P2 | L | Existing universal call graph | TODO | | 018 | Expose five native MCP workflow prompts | P2 | M | — | TODO | -| 019 | Hard-cut Ruby to a qualified universal candidate | P1 | XL | —; final gate should consume 005 or equivalent | IN PROGRESS | +| 019 | Hard-cut Ruby to a qualifying universal evidence pipeline | P1 | XL | —; final gate should consume 005 or equivalent | IN PROGRESS | Status values: `TODO`, `IN PROGRESS`, `DONE`, `BLOCKED`, or `REJECTED`. @@ -91,7 +91,7 @@ Status values: `TODO`, `IN PROGRESS`, `DONE`, `BLOCKED`, or `REJECTED`. - Plan 012 complements plan 005 rather than depending on it: plan 005 owns the broad production release gate; plan 012 owns document-specific evidence. - Plan 013 is deliberately staged: independent truth and project semantics land - before a test-only universal adapter, production changes in one hard cut, and + before a test-only universal evidence pipeline, production changes in one hard cut, and compiler/framework enhancements follow only after the native graph qualifies. Its final public claim should consume plan 005's exact-production-evidence model or an equivalent release-candidate gate. diff --git a/benchmarks/performance/README.md b/benchmarks/performance/README.md index 962bfba1..e1dc704c 100644 --- a/benchmarks/performance/README.md +++ b/benchmarks/performance/README.md @@ -225,7 +225,7 @@ The test compares exact source-byte coverage for a mixed TSX fixture. It is ignored by ordinary workspace tests so native Compass remains Node-free. For developer-only same-file target adjudication on a pinned real corpus, use -the separate checker oracle and keep the candidate adapter out of production: +the separate checker oracle and keep the qualifying producer out of production: ```bash RUST_MIN_STACK=33554432 \ @@ -239,12 +239,12 @@ The report separates exact local targets, missing targets, wrong targets, external positives, and unresolved/ambiguous outcomes by capability. It is an adjudication instrument rather than a release claim: accepted labels, Wilson intervals, cross-file/project strata, framework tiers, and an equivalent -Graphify/SCIP comparison must be frozen before an adapter can be registered. +Graphify/SCIP comparison must be frozen before a producer can be registered. The target harness can persist its source-backed observations with `COMPASS_TS_TARGET_REPORT`; see `benchmarks/performance/oracles/README.md` for the report and reviewed -`compass.typescript-target-scorecard/1` workflow. The scorecard evaluator is +`compass.typescript-target-scorecard/2` workflow. The scorecard evaluator is deliberately separate from the checker oracle: automatic checker outcomes are not accepted precision labels, and diagnostic scorecards are never eligible for a public quality claim. @@ -258,7 +258,7 @@ python3 benchmarks/performance/harness.py audit-candidates \ --graph /path/to/pinned/compass/graph.json \ --corpus /path/to/pinned/django \ --name django \ - --adapter python \ + --producer python \ --output target/performance/audits/django-candidates.json ``` diff --git a/benchmarks/performance/audits/universal-core.json b/benchmarks/performance/audits/universal-core.json index 5a261ffd..8bfa353c 100644 --- a/benchmarks/performance/audits/universal-core.json +++ b/benchmarks/performance/audits/universal-core.json @@ -1,5 +1,5 @@ { - "schema": "compass.quality-audit", + "schema": "compass.quality-audit/2", "mode": "conformance", "corpora": [ { @@ -13,7 +13,7 @@ "sourceOracles": [ { "corpus": "universal-core", - "adapter": "python", + "producer": "python", "provider": "python_ast", "scannedFiles": 1, "parsedFiles": 1, @@ -21,9 +21,9 @@ } ], "advertisedCapabilities": [ - {"adapter": "python", "capability": "calls"}, - {"adapter": "python", "capability": "decorators"}, - {"adapter": "python", "capability": "type_references"} + {"producer": "python", "capability": "calls"}, + {"producer": "python", "capability": "decorators"}, + {"producer": "python", "capability": "type_references"} ], "requiredRelations": ["calls", "decorates", "references"], "records": [ @@ -31,7 +31,7 @@ "id": "accepted-correct", "corpus": "universal-core", "pool": "accepted", - "adapter": "python", + "producer": "python", "capability": "calls", "language": "python", "relation": "calls", @@ -52,7 +52,7 @@ "id": "accepted-cross-language", "corpus": "universal-core", "pool": "accepted", - "adapter": "python", + "producer": "python", "capability": "calls", "language": "python", "relation": "calls", @@ -73,7 +73,7 @@ "id": "accepted-external", "corpus": "universal-core", "pool": "accepted", - "adapter": "python", + "producer": "python", "capability": "calls", "language": "python", "relation": "calls", @@ -94,7 +94,7 @@ "id": "accepted-fabricated-occurrence", "corpus": "universal-core", "pool": "accepted", - "adapter": "python", + "producer": "python", "capability": "calls", "language": "python", "relation": "calls", @@ -115,7 +115,7 @@ "id": "accepted-invalid", "corpus": "universal-core", "pool": "accepted", - "adapter": "python", + "producer": "python", "capability": "calls", "language": "python", "relation": "calls", @@ -136,7 +136,7 @@ "id": "accepted-unsafe-substitution", "corpus": "universal-core", "pool": "accepted", - "adapter": "python", + "producer": "python", "capability": "calls", "language": "python", "relation": "calls", @@ -157,7 +157,7 @@ "id": "oracle-ambiguous", "corpus": "universal-core", "pool": "source_oracle", - "adapter": "python", + "producer": "python", "capability": "calls", "language": "python", "relation": "calls", @@ -178,7 +178,7 @@ "id": "oracle-missing", "corpus": "universal-core", "pool": "source_oracle", - "adapter": "python", + "producer": "python", "capability": "decorators", "language": "python", "relation": "decorates", @@ -199,7 +199,7 @@ "id": "oracle-represented-elsewhere", "corpus": "universal-core", "pool": "source_oracle", - "adapter": "python", + "producer": "python", "capability": "type_references", "language": "python", "relation": "references", diff --git a/benchmarks/performance/compass/audit.py b/benchmarks/performance/compass/audit.py index bd69bf5e..f327d31b 100644 --- a/benchmarks/performance/compass/audit.py +++ b/benchmarks/performance/compass/audit.py @@ -44,8 +44,8 @@ ) -AUDIT_SCHEMA = "compass.quality-audit" -AUDIT_RESULT_SCHEMA = "compass.quality-audit-result" +AUDIT_SCHEMA = "compass.quality-audit/2" +AUDIT_RESULT_SCHEMA = "compass.quality-audit-result/2" QUALIFICATION_MINIMUM = 2_000 CORPUS_MINIMUM = 400 RELATION_MINIMUM = 100 @@ -128,8 +128,8 @@ def _source_line_range(root: Path, source_file: str, location: str) -> tuple[int return start, end, hashlib.sha256(normalized).hexdigest() -def _capability_for_relation(relation: str, adapter: str | None = None) -> str: - if adapter == "ruby" and relation == "implements": +def _capability_for_relation(relation: str, producer: str | None = None) -> str: + if producer == "ruby" and relation == "implements": return "traits" return { "accesses": "members", @@ -238,7 +238,7 @@ def _compass_accepted_candidates( corpus: str, corpus_commit: str, graph_sha256: str, - adapter: str, + producer: str, compass_nodes: dict[str, Any], ) -> list[dict[str, Any]]: """Export every exact source-bounded Compass relationship for precision audit.""" @@ -295,8 +295,8 @@ def _compass_accepted_candidates( "id": candidate_id, "candidateSource": "compass_graph", "suggestedPool": "accepted", - "adapter": adapter, - "capability": _capability_for_relation(relation, adapter), + "producer": producer, + "capability": _capability_for_relation(relation, producer), "language": source_node.language, "relation": relation, "confidence": confidence, @@ -335,7 +335,7 @@ def _source_oracle_candidates( corpus_root: Path, corpus: str, corpus_commit: str, - adapter: str, + producer: str, compass_nodes: dict[str, Any], ) -> tuple[list[dict[str, Any]], dict[str, Any]]: """Export independently parsed source constructs for recall adjudication.""" @@ -346,7 +346,7 @@ def _source_oracle_candidates( if node.qualified_name: owners[node.qualified_name.casefold()].append(identifier) - inventory = independent_source_inventory(corpus_root, adapter) + inventory = independent_source_inventory(corpus_root, producer) candidates: list[dict[str, Any]] = [] for construct in inventory.constructs: bounded = source_index.exact_range( @@ -361,7 +361,7 @@ def _source_oracle_candidates( "source_oracle", corpus, corpus_commit, - adapter, + producer, construct.source_file, construct.relation, construct.owner_qualified_name, @@ -375,7 +375,7 @@ def _source_oracle_candidates( ).hexdigest()[:24] source: dict[str, Any] = { "qualifiedName": construct.owner_qualified_name, - "language": adapter, + "language": producer, } exact_owners = owners.get(construct.owner_qualified_name.casefold(), ()) if len(exact_owners) == 1: @@ -385,9 +385,9 @@ def _source_oracle_candidates( "id": candidate_id, "candidateSource": "independent_source", "suggestedPool": "source_oracle", - "adapter": adapter, + "producer": producer, "capability": construct.capability, - "language": adapter, + "language": producer, "relation": construct.relation, "confidence": "source_ast", "targetCluster": _target_cluster( @@ -398,7 +398,7 @@ def _source_oracle_candidates( "target": { "spelling": construct.target_spelling, "qualifier": construct.qualifier, - "language": adapter, + "language": producer, }, "occurrence": { "file": construct.source_file, @@ -418,8 +418,8 @@ def _source_oracle_candidates( } ) coverage = { - "provider": independent_source_provider_identity(adapter), - "providerAvailable": has_independent_source_provider(adapter), + "provider": independent_source_provider_identity(producer), + "providerAvailable": has_independent_source_provider(producer), "scannedFiles": inventory.scanned_files, "parsedFiles": inventory.parsed_files, "rejectedFiles": list(inventory.rejected_files), @@ -428,9 +428,9 @@ def _source_oracle_candidates( if inventory.provider_metadata else {} ), - "inventorySha256": source_construct_inventory_sha256(adapter, inventory), + "inventorySha256": source_construct_inventory_sha256(producer, inventory), "complete": ( - has_independent_source_provider(adapter) + has_independent_source_provider(producer) and inventory.scanned_files == inventory.parsed_files and not inventory.rejected_files ), @@ -443,13 +443,13 @@ def export_comparison_candidates( graph_path: Path, corpus_root: Path, corpus: str, - adapter: str, + producer: str, destination: Path, ) -> Path: """Export deterministic, unjudged source-bounded comparison hypotheses.""" corpus = _text(corpus, "corpus", identity=True) - adapter = _text(adapter, "adapter", identity=True) + producer = _text(producer, "producer", identity=True) if not database_path.is_file(): raise AuditError(f"comparison database does not exist: {database_path}") if not graph_path.is_file(): @@ -486,14 +486,14 @@ def export_comparison_candidates( corpus, corpus_commit, graph_sha256, - adapter, + producer, compass_nodes, ) source_oracle_candidates, source_oracle_coverage = _source_oracle_candidates( corpus_root, corpus, corpus_commit, - adapter, + producer, compass_nodes, ) comparison_candidates: list[dict[str, Any]] = [] @@ -533,7 +533,7 @@ def export_comparison_candidates( corpus, corpus_commit, graph_sha256, - adapter, + producer, graphify.relation, source_id, target_id, @@ -550,7 +550,7 @@ def export_comparison_candidates( "id": candidate_id, "candidateSource": "graphify_comparison", "suggestedPool": "graphify_hypothesis", - "adapter": adapter, + "producer": producer, "capability": _capability_for_relation(graphify.relation), "language": language, "relation": graphify.relation, @@ -584,7 +584,7 @@ def export_comparison_candidates( candidates = accepted_candidates + source_oracle_candidates + comparison_candidates candidates.sort(key=lambda candidate: candidate["id"]) payload = { - "schema": "compass.quality-audit-candidates", + "schema": "compass.quality-audit-candidates/2", "corpus": { "name": corpus, "commit": corpus_commit, @@ -594,7 +594,7 @@ def export_comparison_candidates( "graphNodes": graph_nodes, "graphEdges": graph_edges, }, - "adapter": adapter, + "producer": producer, "recordsAreUnjudged": True, "populations": { "compassAccepted": len(accepted_candidates), @@ -747,13 +747,13 @@ def _capability(value: object, index: int) -> AuditCapabilityIdentity: item = _expect_object(value, context) _keys( item, - required=("adapter", "capability"), + required=("producer", "capability"), optional=("frameworkPack",), context=context, ) framework_pack = item.get("frameworkPack") return AuditCapabilityIdentity( - adapter=_text(item["adapter"], f"{context}.adapter", identity=True), + producer=_text(item["producer"], f"{context}.producer", identity=True), capability=_text(item["capability"], f"{context}.capability", identity=True), framework_pack=( _text(framework_pack, f"{context}.frameworkPack", identity=True) @@ -770,7 +770,7 @@ def _source_oracle(value: object, index: int) -> AuditSourceOracle: item, required=( "corpus", - "adapter", + "producer", "provider", "scannedFiles", "parsedFiles", @@ -795,7 +795,7 @@ def _source_oracle(value: object, index: int) -> AuditSourceOracle: ) return AuditSourceOracle( corpus=_text(item["corpus"], f"{context}.corpus", identity=True), - adapter=_text(item["adapter"], f"{context}.adapter", identity=True), + producer=_text(item["producer"], f"{context}.producer", identity=True), provider=_text(item["provider"], f"{context}.provider", identity=True), scanned_files=scanned, parsed_files=parsed, @@ -815,7 +815,7 @@ def _record(value: object, index: int) -> AuditRecord: "id", "corpus", "pool", - "adapter", + "producer", "capability", "language", "relation", @@ -859,7 +859,7 @@ def _record(value: object, index: int) -> AuditRecord: record_id=_text(item["id"], f"{context}.id", identity=True), corpus=_text(item["corpus"], f"{context}.corpus", identity=True), pool=pool, - adapter=_text(item["adapter"], f"{context}.adapter", identity=True), + producer=_text(item["producer"], f"{context}.producer", identity=True), framework_pack=( _text(framework_pack, f"{context}.frameworkPack", identity=True) if framework_pack is not None @@ -924,7 +924,7 @@ def load_manifest(path: Path) -> AuditManifest: ) ) source_oracle_keys = [ - (entry.corpus, entry.adapter) for entry in source_oracles + (entry.corpus, entry.producer) for entry in source_oracles ] if source_oracle_keys != sorted(set(source_oracle_keys)): raise AuditError( @@ -942,7 +942,7 @@ def load_manifest(path: Path) -> AuditManifest: if not capabilities: raise AuditError("manifest.advertisedCapabilities must not be empty") capability_keys = [ - (entry.adapter, entry.framework_pack, entry.capability) for entry in capabilities + (entry.producer, entry.framework_pack, entry.capability) for entry in capabilities ] if capability_keys != sorted(set(capability_keys)): raise AuditError( @@ -971,7 +971,7 @@ def load_manifest(path: Path) -> AuditManifest: f"{source_oracle.corpus!r}" ) advertised = { - (entry.adapter, entry.framework_pack, entry.capability) for entry in capabilities + (entry.producer, entry.framework_pack, entry.capability) for entry in capabilities } for record in records: if record.corpus not in corpus_names: @@ -979,7 +979,7 @@ def load_manifest(path: Path) -> AuditManifest: f"record {record.record_id!r} references unknown corpus {record.corpus!r}" ) capability_key = ( - record.adapter, + record.producer, record.framework_pack, record.capability, ) @@ -995,7 +995,7 @@ def load_manifest(path: Path) -> AuditManifest: ) if record.pool == "source_oracle" and ( record.corpus, - record.adapter, + record.producer, ) not in source_oracle_key_set: raise AuditError( f"record {record.record_id!r} has no pinned source-oracle inventory" @@ -1190,14 +1190,14 @@ def _validate_record_inputs( for source_oracle in manifest.source_oracles: root = corpus_roots[source_oracle.corpus] - provider = independent_source_provider_identity(source_oracle.adapter) + provider = independent_source_provider_identity(source_oracle.producer) if provider != source_oracle.provider: raise AuditError( - f"source oracle {(source_oracle.corpus, source_oracle.adapter)!r} " + f"source oracle {(source_oracle.corpus, source_oracle.producer)!r} " f"provider mismatch: expected {source_oracle.provider!r}, " f"observed {provider!r}" ) - inventory = independent_source_inventory(root, source_oracle.adapter) + inventory = independent_source_inventory(root, source_oracle.producer) observed_counts = (inventory.scanned_files, inventory.parsed_files) expected_counts = ( source_oracle.scanned_files, @@ -1205,16 +1205,16 @@ def _validate_record_inputs( ) if observed_counts != expected_counts: raise AuditError( - f"source oracle {(source_oracle.corpus, source_oracle.adapter)!r} " + f"source oracle {(source_oracle.corpus, source_oracle.producer)!r} " f"coverage mismatch: expected {expected_counts}, observed {observed_counts}" ) observed_digest = source_construct_inventory_sha256( - source_oracle.adapter, + source_oracle.producer, inventory, ) if observed_digest != source_oracle.inventory_sha256: raise AuditError( - f"source oracle {(source_oracle.corpus, source_oracle.adapter)!r} " + f"source oracle {(source_oracle.corpus, source_oracle.producer)!r} " "inventory digest mismatch: " f"expected {source_oracle.inventory_sha256}, observed {observed_digest}" ) @@ -1375,7 +1375,7 @@ def _source_coverage( ) -> dict[str, dict[str, int | float]]: by_language: dict[str, list[AuditSourceOracle]] = defaultdict(list) for source_oracle in manifest.source_oracles: - by_language[source_oracle.adapter].append(source_oracle) + by_language[source_oracle.producer].append(source_oracle) coverage: dict[str, dict[str, int | float]] = {} for language in sorted(by_language): scanned = sum(item.scanned_files for item in by_language[language]) @@ -1400,7 +1400,7 @@ def _qualification_failures( for source_oracle in manifest.source_oracles: if source_oracle.parsed_files != source_oracle.scanned_files: failures.append( - f"source oracle {(source_oracle.corpus, source_oracle.adapter)!r} " + f"source oracle {(source_oracle.corpus, source_oracle.producer)!r} " f"parsed {source_oracle.parsed_files}/{source_oracle.scanned_files} files; " "complete source coverage is required" ) @@ -1427,14 +1427,14 @@ def _qualification_failures( values = [ record for record in records - if record.adapter == identity.adapter + if record.producer == identity.producer and record.framework_pack == identity.framework_pack and record.capability == identity.capability ] accepted = sum(record.pool == "accepted" for record in values) if accepted < CAPABILITY_MINIMUM: failures.append( - f"capability {(identity.adapter, identity.framework_pack, identity.capability)!r} " + f"capability {(identity.producer, identity.framework_pack, identity.capability)!r} " f"has {accepted} accepted records; {CAPABILITY_MINIMUM} required" ) correct = sum( @@ -1445,7 +1445,7 @@ def _qualification_failures( capability_precision = correct / accepted if accepted else 0.0 if accepted and capability_precision < CAPABILITY_PRECISION_GATE: failures.append( - f"capability {(identity.adapter, identity.framework_pack, identity.capability)!r} " + f"capability {(identity.producer, identity.framework_pack, identity.capability)!r} " f"precision {capability_precision:.6f} is below " f"{CAPABILITY_PRECISION_GATE:.3f}" ) @@ -1464,12 +1464,12 @@ def _qualification_failures( ) if recall_denominator == 0: failures.append( - f"capability {(identity.adapter, identity.framework_pack, identity.capability)!r} " + f"capability {(identity.producer, identity.framework_pack, identity.capability)!r} " "has no source-derived recall candidates" ) elif capability_recall < CAPABILITY_RECALL_GATE: failures.append( - f"capability {(identity.adapter, identity.framework_pack, identity.capability)!r} " + f"capability {(identity.producer, identity.framework_pack, identity.capability)!r} " f"recall {capability_recall:.6f} is below " f"{CAPABILITY_RECALL_GATE:.3f}" ) diff --git a/benchmarks/performance/compass/model.py b/benchmarks/performance/compass/model.py index 8bfb0578..509909ce 100644 --- a/benchmarks/performance/compass/model.py +++ b/benchmarks/performance/compass/model.py @@ -220,7 +220,7 @@ class AuditGraphFact: @dataclass(frozen=True) class AuditCapabilityIdentity: - adapter: str + producer: str capability: str framework_pack: str | None = None @@ -228,7 +228,7 @@ class AuditCapabilityIdentity: @dataclass(frozen=True) class AuditSourceOracle: corpus: str - adapter: str + producer: str provider: str scanned_files: int parsed_files: int @@ -240,7 +240,7 @@ class AuditRecord: record_id: str corpus: str pool: str - adapter: str + producer: str framework_pack: str | None capability: str language: str diff --git a/benchmarks/performance/compass/typescript_scorecard.py b/benchmarks/performance/compass/typescript_scorecard.py index 909321d4..1bc26cdf 100644 --- a/benchmarks/performance/compass/typescript_scorecard.py +++ b/benchmarks/performance/compass/typescript_scorecard.py @@ -35,8 +35,8 @@ ) -SCORECARD_SCHEMA = "compass.typescript-target-scorecard/1" -RESULT_SCHEMA = "compass.typescript-target-scorecard-result/1" +SCORECARD_SCHEMA = "compass.typescript-target-scorecard/2" +RESULT_SCHEMA = "compass.typescript-target-scorecard-result/2" PROVIDER = "typescript_checker_api_5_9_3" MIN_RELEASE_CORPORA = 4 LEADERSHIP_PRECISION_GATE = 0.997 @@ -75,7 +75,7 @@ class TypeScriptScorecardError(ValueError): class ScorecardRecord: record_id: str corpus: str - adapter: str + producer: str framework_pack: str | None language: str capability: str @@ -155,7 +155,7 @@ def _record(value: object, index: int) -> ScorecardRecord: required = { "id", "corpus", - "adapter", + "producer", "language", "capability", "relation", @@ -228,7 +228,7 @@ def _record(value: object, index: int) -> ScorecardRecord: return ScorecardRecord( record_id=_text(item["id"], f"{context}.id", identity=True), corpus=_text(item["corpus"], f"{context}.corpus", identity=True), - adapter=_text(item["adapter"], f"{context}.adapter", identity=True), + producer=_text(item["producer"], f"{context}.producer", identity=True), framework_pack=( _text(item["frameworkPack"], f"{context}.frameworkPack", identity=True) if item.get("frameworkPack") is not None @@ -265,14 +265,14 @@ def _corpus(value: object, index: int) -> tuple[str, str]: def _capability(value: object, index: int) -> tuple[str, str, str | None]: context = f"advertisedCapabilities[{index}]" item = _object(value, context) - allowed = {"adapter", "capability", "frameworkPack"} - if set(item) - allowed or not {"adapter", "capability"} <= set(item): + allowed = {"producer", "capability", "frameworkPack"} + if set(item) - allowed or not {"producer", "capability"} <= set(item): raise TypeScriptScorecardError( - f"{context} must contain adapter, capability, and optional frameworkPack" + f"{context} must contain producer, capability, and optional frameworkPack" ) framework = item.get("frameworkPack") return ( - _text(item["adapter"], f"{context}.adapter", identity=True), + _text(item["producer"], f"{context}.producer", identity=True), _text(item["capability"], f"{context}.capability", identity=True), ( _text(framework, f"{context}.frameworkPack", identity=True) @@ -314,7 +314,7 @@ def _load(path: Path) -> tuple[dict[str, Any], tuple[ScorecardRecord, ...]]: "mode", "provider", "oracleScriptSha256", - "candidateAdapter", + "producer", "corpora", "releaseGateCorpora", "advertisedCapabilities", @@ -342,7 +342,7 @@ def _load(path: Path) -> tuple[dict[str, Any], tuple[ScorecardRecord, ...]]: f"scorecard.provider must be {PROVIDER!r}, got {item['provider']!r}" ) _hex(item["oracleScriptSha256"], "scorecard.oracleScriptSha256", HEX_64) - _text(item["candidateAdapter"], "scorecard.candidateAdapter", identity=True) + _text(item["producer"], "scorecard.producer", identity=True) corpora = tuple( _corpus(value, index) for index, value in enumerate(_array(item["corpora"], "scorecard.corpora")) @@ -395,7 +395,7 @@ def _load(path: Path) -> tuple[dict[str, Any], tuple[ScorecardRecord, ...]]: raise TypeScriptScorecardError( f"record {record.record_id!r} references unknown corpus" ) - if (record.adapter, record.capability, record.framework_pack) not in advertised: + if (record.producer, record.capability, record.framework_pack) not in advertised: raise TypeScriptScorecardError( f"record {record.record_id!r} references unadvertised capability" ) @@ -442,7 +442,7 @@ def _contribution(record: ScorecardRecord) -> tuple[int, int, int, int]: def _strata(records: tuple[ScorecardRecord, ...]) -> dict[str, dict[str, dict[str, Any]]]: dimensions = { "corpus": lambda record: record.corpus, - "adapter": lambda record: record.adapter, + "producer": lambda record: record.producer, "frameworkPack": lambda record: record.framework_pack or "none", "language": lambda record: record.language, "relation": lambda record: record.relation, @@ -522,20 +522,20 @@ def _failures( ) recall_gate = LEADERSHIP_RECALL_GATE if leadership else CAPABILITY_RECALL_GATE for identity in item["advertisedCapabilities"]: - adapter = identity["adapter"] + producer = identity["producer"] capability = identity["capability"] framework = identity.get("frameworkPack") values = [ record for record in records - if record.adapter == adapter + if record.producer == producer and record.framework_pack == framework and record.capability == capability ] accepted = sum(record.pool == "accepted" for record in values) if accepted < CAPABILITY_MINIMUM: failures.append( - f"capability {(adapter, framework, capability)!r} has {accepted} " + f"capability {(producer, framework, capability)!r} has {accepted} " f"accepted records; {CAPABILITY_MINIMUM} required" ) correct = sum( @@ -545,7 +545,7 @@ def _failures( ) if accepted and correct / accepted < capability_precision_gate: failures.append( - f"capability {(adapter, framework, capability)!r} precision " + f"capability {(producer, framework, capability)!r} precision " f"{correct / accepted:.6f} is below {capability_precision_gate:.3f}" ) recall_values = [ @@ -560,12 +560,12 @@ def _failures( ) if recall_denominator == 0: failures.append( - f"capability {(adapter, framework, capability)!r} has no " + f"capability {(producer, framework, capability)!r} has no " "source-derived recall candidates" ) elif recall_numerator / recall_denominator < recall_gate: failures.append( - f"capability {(adapter, framework, capability)!r} recall " + f"capability {(producer, framework, capability)!r} recall " f"{recall_numerator / recall_denominator:.6f} is below {recall_gate:.3f}" ) precision_gate = LEADERSHIP_PRECISION_GATE if leadership else PRECISION_GATE @@ -586,7 +586,7 @@ def _failures( for dimension, groups in strata.items(): if dimension not in { "corpus", - "adapter", + "producer", "frameworkPack", "language", "relation", @@ -600,7 +600,7 @@ def _failures( if record.pool == "accepted" and { "corpus": record.corpus, - "adapter": record.adapter, + "producer": record.producer, "frameworkPack": record.framework_pack or "none", "language": record.language, "relation": record.relation, @@ -669,7 +669,7 @@ def scorecard_result(path: Path) -> dict[str, Any]: "passed": not failures, "eligibleForQualityClaim": item["mode"] != "diagnostic" and not failures, "provider": item["provider"], - "candidateAdapter": item["candidateAdapter"], + "producer": item["producer"], "releaseGateCorpora": list(item["releaseGateCorpora"]), "auditedRecords": len(records), "precision": precision, diff --git a/benchmarks/performance/harness.py b/benchmarks/performance/harness.py index 6642648f..8af6ccc9 100644 --- a/benchmarks/performance/harness.py +++ b/benchmarks/performance/harness.py @@ -608,7 +608,7 @@ def audit_candidates(args: argparse.Namespace) -> int: args.graph, args.corpus, args.name, - args.adapter, + args.producer, args.output, ) print(destination) @@ -708,7 +708,7 @@ def build_parser() -> argparse.ArgumentParser: candidate_parser.add_argument("--graph", type=Path, required=True) candidate_parser.add_argument("--corpus", type=Path, required=True) candidate_parser.add_argument("--name", required=True) - candidate_parser.add_argument("--adapter", required=True) + candidate_parser.add_argument("--producer", required=True) candidate_parser.add_argument("--output", type=Path, required=True) scorecard_parser = subparsers.add_parser( "typescript-scorecard", diff --git a/benchmarks/performance/oracles/README.md b/benchmarks/performance/oracles/README.md index 1e719bb3..da4c9b7e 100644 --- a/benchmarks/performance/oracles/README.md +++ b/benchmarks/performance/oracles/README.md @@ -104,7 +104,7 @@ candidate observations, automatic checker outcomes, and capability strata. It does not contain manual judgments. A reviewed scorecard must add explicit `accepted` and `source_oracle` pools, `judgmentSource: "manual"`, and a review reason for every non-correct label under -`compass.typescript-target-scorecard/1`, then run: +`compass.typescript-target-scorecard/2`, then run: ```bash python3 benchmarks/performance/harness.py typescript-scorecard \ diff --git a/benchmarks/performance/tests/test_audit.py b/benchmarks/performance/tests/test_audit.py index 612ec6ad..4934cda0 100644 --- a/benchmarks/performance/tests/test_audit.py +++ b/benchmarks/performance/tests/test_audit.py @@ -9,6 +9,7 @@ import unittest from benchmarks.performance.compass.audit import ( + AUDIT_RESULT_SCHEMA, AuditError, audit_result_json_value, export_comparison_candidates, @@ -56,6 +57,7 @@ def test_conformance_result_is_deterministic_and_ineligible(self) -> None: second = run_audit(BASE_MANIFEST, BASE_GRAPH, BASE_CORPUS) self.assertEqual(first, second) payload = audit_result_json_value(first) + self.assertEqual(payload["schema"], AUDIT_RESULT_SCHEMA) self.assertFalse(payload["eligibleForQualityClaim"]) self.assertEqual(6, payload["auditedAcceptedEdges"]) self.assertEqual(2, payload["precision"]["numerator"]) @@ -315,7 +317,8 @@ def test_comparison_candidate_export_is_pinned_deterministic_and_unjudged(self) ) self.assertEqual(first.read_bytes(), second.read_bytes()) payload = json.loads(first.read_text(encoding="utf-8")) - self.assertEqual(payload["schema"], "compass.quality-audit-candidates") + self.assertEqual(payload["schema"], "compass.quality-audit-candidates/2") + self.assertEqual(payload["producer"], "python") self.assertTrue(payload["recordsAreUnjudged"]) self.assertEqual(payload["corpus"]["commit"], "1" * 40) self.assertEqual( @@ -344,6 +347,9 @@ def test_comparison_candidate_export_is_pinned_deterministic_and_unjudged(self) self.assertTrue( all(candidate["judgment"] is None for candidate in payload["candidates"]) ) + self.assertTrue( + all(candidate["producer"] == "python" for candidate in payload["candidates"]) + ) accepted = [ candidate for candidate in payload["candidates"] diff --git a/benchmarks/performance/tests/test_typescript_scorecard.py b/benchmarks/performance/tests/test_typescript_scorecard.py index ba2133d4..dfe60b04 100644 --- a/benchmarks/performance/tests/test_typescript_scorecard.py +++ b/benchmarks/performance/tests/test_typescript_scorecard.py @@ -7,6 +7,7 @@ import unittest from benchmarks.performance.compass.typescript_scorecard import ( + RESULT_SCHEMA, SCORECARD_SCHEMA, TypeScriptScorecardError, scorecard_result, @@ -28,7 +29,7 @@ def record( value: dict[str, object] = { "id": record_id, "corpus": corpus, - "adapter": "typescript", + "producer": "typescript", "language": "typescript", "capability": capability, "relation": relation, @@ -53,7 +54,7 @@ def scorecard(*, mode: str = "diagnostic") -> dict[str, object]: "mode": mode, "provider": "typescript_checker_api_5_9_3", "oracleScriptSha256": "a" * 64, - "candidateAdapter": "compass.typescript.candidate", + "producer": "compass.typescript", "corpora": [ {"name": "axios", "commit": "1" * 40}, {"name": "nest", "commit": "2" * 40}, @@ -62,8 +63,8 @@ def scorecard(*, mode: str = "diagnostic") -> dict[str, object]: ], "releaseGateCorpora": ["axios", "nest", "vite", "zod"], "advertisedCapabilities": [ - {"adapter": "typescript", "capability": "calls"}, - {"adapter": "typescript", "capability": "members"}, + {"producer": "typescript", "capability": "calls"}, + {"producer": "typescript", "capability": "members"}, ], "requiredRelations": ["accesses", "calls"], "comparators": [ @@ -125,6 +126,9 @@ def test_diagnostic_result_is_deterministic_and_not_claim_eligible(self) -> None second = scorecard_result(path) self.assertEqual(first, second) + self.assertEqual(first["schema"], RESULT_SCHEMA) + self.assertEqual(first["scorecardSchema"], SCORECARD_SCHEMA) + self.assertEqual(first["producer"], "compass.typescript") self.assertTrue(first["passed"]) self.assertFalse(first["eligibleForQualityClaim"]) self.assertEqual(first["precision"]["numerator"], 1) diff --git a/crates/compass-core/src/pipeline.rs b/crates/compass-core/src/pipeline.rs index 7f7dfc15..fc0eb445 100644 --- a/crates/compass-core/src/pipeline.rs +++ b/crates/compass-core/src/pipeline.rs @@ -284,7 +284,7 @@ fn compact_extraction(extraction: &mut Extraction) { } } if let Some(evidence) = extraction.semantic_evidence.as_mut() { - compact_vec(&mut evidence.adapter.capabilities); + compact_vec(&mut evidence.pipeline.capabilities); compact_vec(&mut evidence.declarations); compact_vec(&mut evidence.scopes); compact_vec(&mut evidence.bindings); @@ -1279,7 +1279,7 @@ impl Serialize for FactDigestSemanticEvidence<'_> { }) }); let mut map = serializer.serialize_map(Some(7))?; - map.serialize_entry("adapter", &self.batch.adapter)?; + map.serialize_entry("pipeline", &self.batch.pipeline)?; map.serialize_entry( "declarations", &FactDigestDeclarations { @@ -7335,17 +7335,17 @@ fn cached_framework_evidence_matches( } fn cached_universal_evidence_matches(extraction: &Extraction, path: &Path) -> bool { - let Some(profile) = Registry::universal_adapter(path) else { + let Some(pipeline) = Registry::universal_evidence_pipeline(path) else { return true; }; extraction.raw_calls.is_none() && extraction.semantic_evidence.as_ref().is_some_and(|batch| { - batch.adapter.id == profile.id - && batch.adapter.language == profile.language - && batch.adapter.version == profile.version - && batch.adapter.evidence_schema == profile.evidence_schema - && batch.adapter.profile == profile.profile - && batch.adapter.capabilities.as_slice() == profile.capabilities + batch.pipeline.id == pipeline.producer.id + && batch.pipeline.language == pipeline.producer.language + && batch.pipeline.version == pipeline.producer.version + && batch.pipeline.evidence_schema == pipeline.producer.evidence_schema + && batch.pipeline.qualification == pipeline.qualification + && batch.pipeline.capabilities.as_slice() == pipeline.producer.capabilities && compass_languages::validate_evidence( batch, compass_languages::EvidenceLimits::default(), @@ -8079,7 +8079,7 @@ mod tests { } #[test] - fn universal_adapter_cache_requires_current_valid_evidence() -> Result<(), Box> { + fn universal_evidence_cache_requires_current_valid_evidence() -> Result<(), Box> { let python = Path::new("src/example.py"); let rust = Path::new("src/example.rs"); assert!(!cached_universal_evidence_matches( @@ -8107,7 +8107,7 @@ mod tests { .semantic_evidence .as_mut() .ok_or_else(|| std::io::Error::other("universal evidence is missing"))? - .adapter + .pipeline .capabilities .clear(); assert!(!cached_universal_evidence_matches(&invalid, python)); diff --git a/crates/compass-languages/src/engine.rs b/crates/compass-languages/src/engine.rs index 294cd960..32b92f17 100644 --- a/crates/compass-languages/src/engine.rs +++ b/crates/compass-languages/src/engine.rs @@ -130,17 +130,16 @@ impl Engine { Ok(extraction) } - /// Extract qualification-only Ruby/TypeScript/JavaScript universal evidence - /// directly from source. The same source-backed emitters are used by the - /// production registry after the language cutover so qualification cannot - /// drift from published output. + /// Extract qualification-only universal evidence directly from source. + /// The same source-backed emitters are used by the production registry so + /// qualification cannot drift from published output. /// - /// This hidden API remains useful for qualification fixtures, but it now - /// calls the same registered candidate emitter used by normal Compass + /// This hidden API remains useful for qualification fixtures, but it calls + /// the same registered evidence pipeline used by normal Compass /// extraction. Keeping both paths on one implementation prevents a /// qualification-only graph from diverging from production output. #[doc(hidden)] - pub fn extract_source_universal_candidate_evidence( + pub fn extract_source_universal_evidence( &mut self, path: &Path, source_file: &str, @@ -148,32 +147,16 @@ impl Engine { ) -> Result { let spec = Registry::resolve(path).ok_or_else(|| ExtractError::Unsupported(path.to_path_buf()))?; - if !matches!( - spec.name, - "typescript" | "tsx" | "javascript" | "kotlin" | "ruby" - ) { - return Err(ExtractError::Unsupported(path.to_path_buf())); - } + let pipeline = Registry::universal_evidence_pipeline_for_spec(spec) + .ok_or_else(|| ExtractError::Unsupported(path.to_path_buf()))?; let tree = self.parse(path, spec, source)?; - let evidence = if matches!(spec.name, "kotlin" | "ruby") { - let profile = Registry::universal_profile_for_spec(spec) - .ok_or_else(|| ExtractError::Unsupported(path.to_path_buf()))?; - crate::evidence::extract_tree_evidence( - path, - source_file, - source, - tree.root_node(), - profile, - ) - } else { - crate::evidence::extract_candidate_tree_evidence( - path, - source_file, - source, - tree.root_node(), - spec.name, - ) - }; + let evidence = crate::evidence::extract_tree_evidence( + path, + source_file, + source, + tree.root_node(), + pipeline, + ); evidence.map_err(|error| ExtractError::InvalidProgramEvidence { path: path.to_path_buf(), detail: error.to_string(), @@ -231,9 +214,9 @@ impl Engine { program: None, }); } - let universal_profile = Registry::universal_profile_for_spec(spec); + let pipeline = Registry::universal_evidence_pipeline_for_spec(spec); if spec.kind == ExtractorKind::Generic - && universal_profile.is_some() + && pipeline.is_some() && !crate::program::supports_language(spec.name) { let tree = self.parse(path, spec, source)?; @@ -382,8 +365,8 @@ impl Engine { root: Node<'_>, ) -> Extraction { let config = generic_config(spec); - let universal_profile = Registry::universal_profile_for_spec(spec); - let mut extraction = if universal_profile.is_some() { + let pipeline = Registry::universal_evidence_pipeline_for_spec(spec); + let mut extraction = if pipeline.is_some() { Extraction::default() } else { match spec.name { @@ -403,17 +386,17 @@ impl Engine { if spec.name == "python" { add_python_rationale(path, source, root, &mut extraction); } - if universal_profile.is_none() { + if pipeline.is_none() { attach_definition_metadata(&mut extraction, source, root, &config, spec.name); crate::semantic::enrich(path, source, root, spec.name, &mut extraction); } - if let Some(profile) = universal_profile { + if let Some(pipeline) = pipeline { match crate::evidence::extract_tree_evidence( path, evidence_source_file, source, root, - profile, + pipeline, ) { Ok(evidence) => { extraction.semantic_evidence = Some(evidence); @@ -431,7 +414,7 @@ impl Engine { // (for example `src/routes/**`, `app/routes/**`, and `src/app/**`) to // classify a file. Pipeline callers supply that authoritative identity; // direct extraction derives a bounded portable fallback from the path. - let framework_source = universal_profile.map(|_| { + let framework_source = pipeline.map(|_| { if evidence_source_is_explicit && !evidence_source_file.is_empty() && Path::new(evidence_source_file).is_relative() @@ -450,7 +433,7 @@ impl Engine { self.project_evidence(path), &mut extraction, ); - if universal_profile.is_some() && extraction.semantic_evidence.is_some() { + if pipeline.is_some() && extraction.semantic_evidence.is_some() { extraction.raw_calls = None; } if root.has_error() { @@ -4559,7 +4542,7 @@ public class OrdersController : ControllerBase { }) .ok_or("missing prototype method")?; assert_eq!(method.kind, "property"); - let direct = Engine::default().extract_source_universal_candidate_evidence( + let direct = Engine::default().extract_source_universal_evidence( &source, "widget.js", &source_bytes, diff --git a/crates/compass-languages/src/evidence/build.rs b/crates/compass-languages/src/evidence/build.rs index 7388be66..456da423 100644 --- a/crates/compass-languages/src/evidence/build.rs +++ b/crates/compass-languages/src/evidence/build.rs @@ -5,13 +5,13 @@ use std::path::{Path, PathBuf}; use sha2::{Digest, Sha256}; use tree_sitter::Node; -use crate::{AdapterProfile, EXTRACTION_SEMANTICS_VERSION, file_stem, make_id}; +use crate::{EXTRACTION_SEMANTICS_VERSION, UniversalEvidencePipeline, file_stem, make_id}; use super::model::{ - AdapterIdentity, BindingFact, BindingKind, CandidateRelation, DeclarationFact, - EvidenceDiagnostic, EvidenceRange, HierarchyConstraint, OccurrenceFact, - ReceiverDispatchStrategy, RelationshipCandidate, ResolutionConstraint, ScopeFact, - SemanticEvidenceBatch, SemanticRole, SymbolNamespace, + BindingFact, BindingKind, CandidateRelation, DeclarationFact, EvidenceDiagnostic, + EvidenceRange, HierarchyConstraint, OccurrenceFact, ReceiverDispatchStrategy, + RelationshipCandidate, ResolutionConstraint, ScopeFact, SemanticEvidenceBatch, SemanticRole, + SymbolNamespace, UniversalEvidenceIdentity, }; use super::validate::{EvidenceError, EvidenceErrorCode, EvidenceLimits, validate_evidence}; @@ -21,7 +21,7 @@ use super::validate::{EvidenceError, EvidenceErrorCode, EvidenceLimits, validate // unresolved method. const GO_TYPE_INFERENCE_DEPTH_LIMIT: usize = 16; -/// Bounded direct-construction API shared by hard-cut language adapters. +/// Bounded direct-construction API shared by hard-cut language producers. pub struct EvidenceBuilder { batch: SemanticEvidenceBatch, source_file: String, @@ -43,33 +43,33 @@ struct DeclarationMetadata { impl EvidenceBuilder { #[must_use] pub fn new( - profile: &'static AdapterProfile, - producer: impl Into, + pipeline: &'static UniversalEvidencePipeline, + emitter: impl Into, source_file: impl Into, limits: EvidenceLimits, ) -> Self { - Self::new_with_dialect(profile, producer, source_file, limits, None) + Self::new_with_dialect(pipeline, emitter, source_file, limits, None) } #[must_use] pub fn new_with_dialect( - profile: &'static AdapterProfile, - producer: impl Into, + pipeline: &'static UniversalEvidencePipeline, + emitter: impl Into, source_file: impl Into, limits: EvidenceLimits, dialect: Option<&str>, ) -> Self { Self { batch: SemanticEvidenceBatch { - adapter: AdapterIdentity { - id: profile.id.to_owned(), - language: profile.language.to_owned(), + pipeline: UniversalEvidenceIdentity { + id: pipeline.producer.id.to_owned(), + language: pipeline.producer.language.to_owned(), dialect: dialect.map(str::to_owned), - version: profile.version, - evidence_schema: profile.evidence_schema.to_owned(), - profile: profile.profile, - producer: producer.into(), - capabilities: profile.capabilities.to_vec(), + version: pipeline.producer.version, + evidence_schema: pipeline.producer.evidence_schema.to_owned(), + qualification: pipeline.qualification, + emitter: emitter.into(), + capabilities: pipeline.producer.capabilities.to_vec(), }, declarations: Vec::new(), scopes: Vec::new(), @@ -134,10 +134,10 @@ impl EvidenceBuilder { /// Add a declaration with a bounded source-level signature fragment. /// - /// Candidate adapters use this for type-shape facts that the shared + /// Qualifying producers use this for type-shape facts that the shared /// resolver can consume without inventing a target from a terminal name. /// Keeping the entry point crate-private avoids expanding the public - /// builder surface while allowing adapters implemented in sibling + /// builder surface while allowing producers implemented in sibling /// modules to publish the existing, versioned `signature` field. #[allow(clippy::too_many_arguments)] pub(crate) fn declare_with_signature( @@ -172,7 +172,7 @@ impl EvidenceBuilder { /// Add a callable declaration with the source-proven signature shape used /// by deterministic overload selection. /// - /// Language adapters retain responsibility for canonicalizing their own + /// Language producers retain responsibility for canonicalizing their own /// parameter spellings. The builder only publishes the bounded, typed /// fields already present in the universal evidence schema. #[allow(clippy::too_many_arguments)] @@ -310,7 +310,7 @@ impl EvidenceBuilder { let id = self.stable_id("declaration", &identity); self.batch.declarations.push(DeclarationFact { id: id.clone(), - language: self.batch.adapter.language.clone(), + language: self.batch.pipeline.language.clone(), graph_node_id: graph_node_id.to_owned(), kind: kind.to_owned(), name: name.to_owned(), @@ -352,7 +352,7 @@ impl EvidenceBuilder { ); self.batch.scopes.push(ScopeFact { id: id.clone(), - language: self.batch.adapter.language.clone(), + language: self.batch.pipeline.language.clone(), kind: kind.to_owned(), owner_declaration_id: owner_declaration_id.map(str::to_owned), parent_scope_id: parent_scope_id.map(str::to_owned), @@ -510,7 +510,7 @@ impl EvidenceBuilder { let id = self.stable_id("binding", &identity); self.batch.bindings.push(BindingFact { id: id.clone(), - language: self.batch.adapter.language.clone(), + language: self.batch.pipeline.language.clone(), kind, spelling: spelling.to_owned(), qualified_target: qualified_target.to_owned(), @@ -579,7 +579,7 @@ impl EvidenceBuilder { ); self.batch.occurrences.push(OccurrenceFact { id: id.clone(), - language: self.batch.adapter.language.clone(), + language: self.batch.pipeline.language.clone(), role, owner_declaration_id: owner_declaration_id.to_owned(), spelling: spelling.to_owned(), @@ -675,7 +675,7 @@ impl EvidenceBuilder { let id = self.stable_id("candidate", &identity); self.batch.candidates.push(RelationshipCandidate { id: id.clone(), - language: self.batch.adapter.language.clone(), + language: self.batch.pipeline.language.clone(), relation, source_declaration_id: source_declaration_id.to_owned(), occurrence_id: occurrence_id.map(str::to_owned), @@ -709,7 +709,7 @@ impl EvidenceBuilder { } self.batch.diagnostics.push(EvidenceDiagnostic { code: code.to_owned(), - language: self.batch.adapter.language.clone(), + language: self.batch.pipeline.language.clone(), fact_id: fact_id.map(str::to_owned), range, message: message.to_owned(), @@ -718,8 +718,8 @@ impl EvidenceBuilder { } pub fn finish(mut self) -> Result { - self.batch.adapter.capabilities.sort_unstable(); - self.batch.adapter.capabilities.dedup(); + self.batch.pipeline.capabilities.sort_unstable(); + self.batch.pipeline.capabilities.dedup(); self.batch .declarations .sort_unstable_by(|left, right| left.id.cmp(&right.id)); @@ -755,8 +755,8 @@ impl EvidenceBuilder { let mut digest = Sha256::new(); for part in [ EXTRACTION_SEMANTICS_VERSION, - self.batch.adapter.language.as_str(), - self.batch.adapter.producer.as_str(), + self.batch.pipeline.language.as_str(), + self.batch.pipeline.emitter.as_str(), self.source_file.as_str(), category, ] @@ -823,47 +823,47 @@ pub(crate) fn extract_tree_evidence( source_file: &str, source: &[u8], root: Node<'_>, - profile: &'static AdapterProfile, + pipeline: &'static UniversalEvidencePipeline, ) -> Result { - if profile.language == "csharp" { - return super::csharp::extract_candidate_tree_evidence(path, source_file, source, root); + if pipeline.producer.language == "csharp" { + return super::csharp::emit_tree_evidence(path, source_file, source, root); } - if profile.language == "php" { - return super::php::extract_candidate_tree_evidence(path, source_file, source, root); + if pipeline.producer.language == "php" { + return super::php::emit_tree_evidence(path, source_file, source, root); } - if profile.language == "kotlin" { - return super::kotlin::extract_candidate_tree_evidence(path, source_file, source, root); + if pipeline.producer.language == "kotlin" { + return super::kotlin::emit_tree_evidence(path, source_file, source, root); } - if profile.language == "ruby" { - return super::ruby::extract_candidate_tree_evidence(path, source_file, source, root); + if pipeline.producer.language == "ruby" { + return super::ruby::emit_tree_evidence(path, source_file, source, root); } - if matches!(profile.language, "javascript" | "typescript") { - return super::typescript::extract_candidate_tree_evidence( + if matches!(pipeline.producer.language, "javascript" | "typescript") { + return super::typescript::emit_tree_evidence( path, source_file, source, root, - profile.language, + pipeline.producer.language, ); } - let mut state = DirectAdapterState::new(path, source_file, source, root, profile); + let mut state = DirectEvidenceState::new(path, source_file, source, root, pipeline); state.add_file(root)?; if root.end_byte() == root.start_byte() { - let DirectAdapterState { builder, .. } = state; + let DirectEvidenceState { builder, .. } = state; return builder.finish(); } state.capture_parser_errors(root); - match profile.language { + match pipeline.producer.language { "python" => state.extract_python(root)?, "go" => state.extract_go(root)?, "java" => state.extract_java(root)?, "rust" => state.extract_rust(root)?, _ => { return Err(EvidenceError::new( - EvidenceErrorCode::InvalidAdapter, + EvidenceErrorCode::InvalidPipeline, format!( "language {:?} has no direct universal extractor", - profile.language + pipeline.producer.language ), )); } @@ -876,7 +876,7 @@ pub(crate) fn extract_tree_evidence( "parser recovered from malformed source; emitted evidence remains source-bounded", )?; } - let DirectAdapterState { builder, .. } = state; + let DirectEvidenceState { builder, .. } = state; builder.finish() } @@ -924,7 +924,7 @@ enum RustPlatformCfg { Windows, } -struct DirectAdapterState<'source> { +struct DirectEvidenceState<'source> { path: &'source Path, source_file: &'source str, source: &'source [u8], @@ -980,22 +980,22 @@ struct DirectAdapterState<'source> { builder: EvidenceBuilder, } -impl<'source> DirectAdapterState<'source> { +impl<'source> DirectEvidenceState<'source> { fn new( path: &'source Path, source_file: &'source str, source: &'source [u8], root: Node<'_>, - profile: &'static AdapterProfile, + pipeline: &'static UniversalEvidencePipeline, ) -> Self { let stem = file_stem(path); - let module_or_package = if profile.language == "python" { + let module_or_package = if pipeline.producer.language == "python" { python_module_identity(path, source_file) - } else if profile.language == "go" { + } else if pipeline.producer.language == "go" { go_package_identity(path, source_file, source, root) - } else if profile.language == "java" { + } else if pipeline.producer.language == "java" { java_package_identity(source).unwrap_or_else(|| "".to_owned()) - } else if profile.language == "rust" { + } else if pipeline.producer.language == "rust" { rust_module_identity(path, source_file) } else { path.parent() @@ -1008,9 +1008,9 @@ impl<'source> DirectAdapterState<'source> { path, source_file, source, - language: profile.language, + language: pipeline.producer.language, module_or_package, - rust_namespace_aliases: if profile.language == "rust" { + rust_namespace_aliases: if pipeline.producer.language == "rust" { rust_manifest_dependency_aliases(path) } else { HashMap::new() @@ -1062,8 +1062,8 @@ impl<'source> DirectAdapterState<'source> { graph_ids: HashSet::new(), parser_error_ranges: Vec::new(), builder: EvidenceBuilder::new( - profile, - format!("compass.languages.{}.universal", profile.language), + pipeline, + format!("compass.languages.{}.universal", pipeline.producer.language), source_file, EvidenceLimits::default(), ), @@ -8674,7 +8674,7 @@ fn rust_qualify_local_path(module: &str, raw: &str) -> String { } fn rust_qualify_imported_path( - state: &DirectAdapterState<'_>, + state: &DirectEvidenceState<'_>, owner: &DeclarationContext, raw: &str, use_start: usize, @@ -8696,7 +8696,7 @@ fn rust_qualify_imported_path( } fn rust_qualify_evidence_path( - state: &DirectAdapterState<'_>, + state: &DirectEvidenceState<'_>, owner: &DeclarationContext, raw: &str, use_start: usize, @@ -9213,7 +9213,7 @@ fn python_super_call_is_builtin( receiver: Node<'_>, call: Node<'_>, owner: &DeclarationContext, - state: &DirectAdapterState<'_>, + state: &DirectEvidenceState<'_>, ) -> bool { if owner.kind != "method" || owner.enclosing_type_qualified_name.is_none() { return false; diff --git a/crates/compass-languages/src/evidence/csharp.rs b/crates/compass-languages/src/evidence/csharp.rs index ec858c34..cc589df0 100644 --- a/crates/compass-languages/src/evidence/csharp.rs +++ b/crates/compass-languages/src/evidence/csharp.rs @@ -1,6 +1,6 @@ //! Direct universal evidence for C# and .NET source. //! -//! The adapter is deliberately AST-first and project-neutral. It emits exact +//! The producer is deliberately AST-first and project-neutral. It emits exact //! declarations, scopes, bindings, occurrences, and constrained relationship //! candidates; cross-file target choice remains owned by `compass-resolve`. @@ -15,7 +15,7 @@ use super::model::{ ResolutionConstraint, SemanticEvidenceBatch, SemanticRole, SymbolNamespace, }; use super::validate::{EvidenceError, EvidenceErrorCode, EvidenceLimits}; -use crate::{AdapterRegistry, file_stem, make_id}; +use crate::{UniversalEvidenceRegistry, file_stem, make_id}; const PRODUCER: &str = "compass.languages.csharp.universal"; const MAX_TRAVERSAL_DEPTH: usize = 512; @@ -70,20 +70,20 @@ struct State<'source> { direct_class_bases: BTreeMap>, } -pub(super) fn extract_candidate_tree_evidence( +pub(super) fn emit_tree_evidence( path: &Path, source_file: &str, source: &[u8], root: Node<'_>, ) -> Result { - let profile = AdapterRegistry::universal_profile("csharp").ok_or_else(|| { + let pipeline = UniversalEvidenceRegistry::pipeline("csharp").ok_or_else(|| { EvidenceError::new( - EvidenceErrorCode::InvalidAdapter, - "C# universal adapter is not registered", + EvidenceErrorCode::InvalidPipeline, + "C# universal evidence pipeline is not registered", ) })?; let mut builder = - EvidenceBuilder::new(profile, PRODUCER, source_file, EvidenceLimits::default()); + EvidenceBuilder::new(pipeline, PRODUCER, source_file, EvidenceLimits::default()); let file_graph_id = make_id(&[source_file]); let file_module = file_stem(Path::new(source_file)); let file_id = builder.declare( diff --git a/crates/compass-languages/src/evidence/kotlin.rs b/crates/compass-languages/src/evidence/kotlin.rs index 39e506f8..affa7d7c 100644 --- a/crates/compass-languages/src/evidence/kotlin.rs +++ b/crates/compass-languages/src/evidence/kotlin.rs @@ -15,7 +15,7 @@ use super::model::{ ResolutionConstraint, SemanticEvidenceBatch, SemanticRole, SymbolNamespace, }; use super::validate::{EvidenceError, EvidenceErrorCode, EvidenceLimits}; -use crate::{AdapterRegistry, file_stem, make_id}; +use crate::{UniversalEvidenceRegistry, file_stem, make_id}; const PRODUCER: &str = "compass.languages.kotlin.universal"; const MAX_TRAVERSAL_DEPTH: usize = 128; @@ -52,20 +52,20 @@ struct State<'source> { parser_errors: Vec<(usize, usize)>, } -pub(super) fn extract_candidate_tree_evidence( +pub(super) fn emit_tree_evidence( path: &Path, source_file: &str, source: &[u8], root: Node<'_>, ) -> Result { - let profile = AdapterRegistry::universal_profile("kotlin").ok_or_else(|| { + let pipeline = UniversalEvidenceRegistry::pipeline("kotlin").ok_or_else(|| { EvidenceError::new( - EvidenceErrorCode::InvalidAdapter, - "Kotlin universal adapter is not registered", + EvidenceErrorCode::InvalidPipeline, + "Kotlin universal evidence pipeline is not registered", ) })?; let mut builder = - EvidenceBuilder::new(profile, PRODUCER, source_file, EvidenceLimits::default()); + EvidenceBuilder::new(pipeline, PRODUCER, source_file, EvidenceLimits::default()); let file_range = range_for_file(source_file, source); let file_graph_id = make_id(&[source_file]); let file_id = builder.declare( diff --git a/crates/compass-languages/src/evidence/mod.rs b/crates/compass-languages/src/evidence/mod.rs index b1bd57c8..a3bdb114 100644 --- a/crates/compass-languages/src/evidence/mod.rs +++ b/crates/compass-languages/src/evidence/mod.rs @@ -7,15 +7,14 @@ mod ruby; mod typescript; mod validate; -pub const UNIVERSAL_EVIDENCE_SCHEMA: &str = "compass.languages.evidence/1"; +pub const UNIVERSAL_EVIDENCE_SCHEMA: &str = "compass.languages.evidence/2"; pub(crate) use build::extract_tree_evidence; pub use build::{EvidenceBuilder, range_for_node}; pub use model::{ - AdapterIdentity, BindingFact, BindingKind, CandidateRelation, DeclarationFact, - EvidenceDiagnostic, EvidenceRange, HierarchyConstraint, LanguageCapability, OccurrenceFact, + BindingFact, BindingKind, CandidateRelation, DeclarationFact, EvidenceDiagnostic, + EvidenceRange, HierarchyConstraint, LanguageCapability, OccurrenceFact, ReceiverDispatchStrategy, RelationshipCandidate, ResolutionConstraint, ScopeFact, - SemanticEvidenceBatch, SemanticRole, SymbolNamespace, + SemanticEvidenceBatch, SemanticRole, SymbolNamespace, UniversalEvidenceIdentity, }; -pub(crate) use typescript::extract_candidate_tree_evidence; pub use validate::{EvidenceError, EvidenceErrorCode, EvidenceLimits, validate_evidence}; diff --git a/crates/compass-languages/src/evidence/model.rs b/crates/compass-languages/src/evidence/model.rs index bf2336bb..e192d938 100644 --- a/crates/compass-languages/src/evidence/model.rs +++ b/crates/compass-languages/src/evidence/model.rs @@ -1,21 +1,21 @@ use serde::{Deserialize, Serialize}; -use crate::UniversalAdapterProfile; +use crate::UniversalEvidenceQualification; -/// Identity and truthful capability declaration for one semantic adapter. +/// Identity and truthful capability declaration for one evidence pipeline. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct AdapterIdentity { +pub struct UniversalEvidenceIdentity { pub id: String, pub language: String, /// Parser dialect preserved separately from the semantic language family. - /// `None` is retained for legacy adapters that predate dialect metadata. + /// `None` is retained for legacy producers that predate dialect metadata. #[serde(default, skip_serializing_if = "Option::is_none")] pub dialect: Option, pub version: u32, pub evidence_schema: String, - pub profile: UniversalAdapterProfile, - pub producer: String, + pub qualification: UniversalEvidenceQualification, + pub emitter: String, pub capabilities: Vec, } @@ -32,7 +32,7 @@ pub struct EvidenceRange { pub end_column: u32, } -/// Semantic operations that an adapter may truthfully advertise. +/// Semantic operations that an evidence producer may truthfully advertise. #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum LanguageCapability { @@ -213,7 +213,7 @@ pub struct DeclarationFact { pub name: String, pub qualified_name: String, /// Optional value/type/namespace identity. `None` preserves legacy - /// adapters that predate explicit symbol-space evidence. + /// producers that predate explicit symbol-space evidence. #[serde(default, skip_serializing_if = "Option::is_none")] pub namespace: Option, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -289,7 +289,7 @@ pub struct BindingFact { /// Zero-based result selected by a source-level destructuring assignment. #[serde(default, skip_serializing_if = "Option::is_none")] pub output_index: Option, - /// Exact nominal result type proven by the adapter for this call result. + /// Exact nominal result type proven by the producer for this call result. #[serde(default, skip_serializing_if = "Option::is_none")] pub result_type_qualified_name: Option, /// Earlier call result whose nominal type receives this method call. @@ -323,7 +323,7 @@ pub struct OccurrenceFact { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ResolutionConstraint { - /// Exact declaration proven by the adapter from the same source construct. + /// Exact declaration proven by the producer from the same source construct. #[serde(default, skip_serializing_if = "Option::is_none")] pub exact_target_declaration_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -406,11 +406,11 @@ pub struct EvidenceDiagnostic { pub message: String, } -/// Direct output of one universal semantic adapter. +/// Direct output of one universal semantic evidence pipeline. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct SemanticEvidenceBatch { - pub adapter: AdapterIdentity, + pub pipeline: UniversalEvidenceIdentity, pub declarations: Vec, pub scopes: Vec, pub bindings: Vec, diff --git a/crates/compass-languages/src/evidence/php.rs b/crates/compass-languages/src/evidence/php.rs index 7dda8899..dfb59162 100644 --- a/crates/compass-languages/src/evidence/php.rs +++ b/crates/compass-languages/src/evidence/php.rs @@ -1,7 +1,7 @@ //! Direct universal semantic evidence for PHP. //! //! PHP class, interface, trait, enum, function, and method lookup is -//! case-insensitive. The adapter therefore retains source spelling in +//! case-insensitive. The producer therefore retains source spelling in //! declaration names while using case-folded qualified identities for those //! symbol families. Properties, variables, and constants keep source case. @@ -16,7 +16,7 @@ use super::model::{ ResolutionConstraint, SemanticEvidenceBatch, SemanticRole, SymbolNamespace, }; use super::validate::{EvidenceError, EvidenceErrorCode, EvidenceLimits}; -use crate::{AdapterRegistry, make_id}; +use crate::{UniversalEvidenceRegistry, make_id}; const PRODUCER: &str = "compass.languages.php.universal"; const MAX_TRAVERSAL_DEPTH: usize = 512; @@ -77,20 +77,20 @@ struct State<'source> { value_types: HashMap<(String, String), String>, } -pub(super) fn extract_candidate_tree_evidence( +pub(super) fn emit_tree_evidence( path: &Path, source_file: &str, source: &[u8], root: Node<'_>, ) -> Result { - let profile = AdapterRegistry::universal_profile("php").ok_or_else(|| { + let pipeline = UniversalEvidenceRegistry::pipeline("php").ok_or_else(|| { EvidenceError::new( - EvidenceErrorCode::InvalidAdapter, - "PHP universal adapter is not registered", + EvidenceErrorCode::InvalidPipeline, + "PHP universal evidence pipeline is not registered", ) })?; let mut builder = - EvidenceBuilder::new(profile, PRODUCER, source_file, EvidenceLimits::default()); + EvidenceBuilder::new(pipeline, PRODUCER, source_file, EvidenceLimits::default()); let file_range = range_for_file(source_file, source); // Tree-sitter represents an empty source file with a zero-width root. The // universal inventory contract admits that exact range for file/module diff --git a/crates/compass-languages/src/evidence/ruby.rs b/crates/compass-languages/src/evidence/ruby.rs index a436e344..dac040a4 100644 --- a/crates/compass-languages/src/evidence/ruby.rs +++ b/crates/compass-languages/src/evidence/ruby.rs @@ -26,10 +26,10 @@ use crate::make_id; const MAX_TRAVERSAL_DEPTH: usize = 32; const MAX_LITERAL_BYTES: usize = 4 * 1024; -/// Ruby remains a candidate until the independent corpus audit is complete; -/// the production registry intentionally exposes the candidate identity so the -/// hard-cut path and qualification tooling exercise the same emitter. -use crate::adapters::RUBY_ADAPTER_PROFILE; +/// Ruby remains in qualification while the independent corpus audit is +/// complete; the production registry intentionally exposes the same pipeline +/// used by qualification tooling so the two paths cannot drift. +use crate::evidence_pipeline::RUBY_EVIDENCE_PIPELINE; #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum MethodSpace { @@ -89,7 +89,7 @@ struct RubyState<'source> { emitted_diagnostics: HashSet, } -pub(crate) fn extract_candidate_tree_evidence( +pub(crate) fn emit_tree_evidence( path: &Path, source_file: &str, source: &[u8], @@ -108,8 +108,8 @@ impl<'source> RubyState<'source> { root: Node<'_>, ) -> Result { let mut builder = EvidenceBuilder::new_with_dialect( - &RUBY_ADAPTER_PROFILE, - "compass.languages.ruby.universal.candidate", + &RUBY_EVIDENCE_PIPELINE, + "compass.languages.ruby.universal", source_file, EvidenceLimits::default(), Some("ruby"), diff --git a/crates/compass-languages/src/evidence/typescript.rs b/crates/compass-languages/src/evidence/typescript.rs index 302708de..f03d35ab 100644 --- a/crates/compass-languages/src/evidence/typescript.rs +++ b/crates/compass-languages/src/evidence/typescript.rs @@ -1,7 +1,7 @@ //! Direct universal evidence for the ECMAScript family. //! //! TypeScript and JavaScript share the bounded source-grounded emitter while -//! retaining distinct adapter identities. The production registry dispatches +//! retaining distinct language identities. The production registry dispatches //! both languages here; qualification callers use the same entry point so //! there is no shadow implementation to drift from the shipped graph path. @@ -18,7 +18,7 @@ use super::model::{ SemanticEvidenceBatch, SemanticRole, SymbolNamespace, }; use super::validate::{EvidenceError, EvidenceErrorCode, EvidenceLimits}; -use crate::{AdapterRegistry, make_id}; +use crate::{UniversalEvidenceRegistry, make_id}; const MAX_TRAVERSAL_DEPTH: usize = 512; const MAX_INLINE_OBJECT_PROPERTIES: usize = 256; @@ -298,7 +298,7 @@ struct CandidateState<'source, 'tree> { _tree: std::marker::PhantomData>, } -pub(crate) fn extract_candidate_tree_evidence( +pub(crate) fn emit_tree_evidence( path: &Path, source_file: &str, source: &[u8], @@ -310,22 +310,22 @@ pub(crate) fn extract_candidate_tree_evidence( "javascript" => "javascript", _ => { return Err(EvidenceError::new( - EvidenceErrorCode::InvalidAdapter, - format!("unsupported ECMAScript candidate dialect {dialect:?}"), + EvidenceErrorCode::InvalidPipeline, + format!("unsupported ECMAScript dialect {dialect:?}"), )); } }; - let profile = AdapterRegistry::universal_profile(language).ok_or_else(|| { + let pipeline = UniversalEvidenceRegistry::pipeline(language).ok_or_else(|| { EvidenceError::new( - EvidenceErrorCode::InvalidAdapter, - format!("ECMAScript universal adapter {language:?} is not registered"), + EvidenceErrorCode::InvalidPipeline, + format!("ECMAScript universal evidence pipeline {language:?} is not registered"), ) })?; let module_name = module_name(path, source_file); let dialect_name = dialect_for_path(path, dialect); let mut builder = EvidenceBuilder::new_with_dialect( - profile, - format!("compass.languages.{language}.universal.candidate"), + pipeline, + format!("compass.languages.{language}.universal"), source_file, EvidenceLimits::default(), Some(&dialect_name), diff --git a/crates/compass-languages/src/evidence/validate.rs b/crates/compass-languages/src/evidence/validate.rs index 72cf9287..cb774a61 100644 --- a/crates/compass-languages/src/evidence/validate.rs +++ b/crates/compass-languages/src/evidence/validate.rs @@ -10,7 +10,7 @@ use super::model::{ SemanticRole, SymbolNamespace, }; -/// Hard resource ceilings for a single adapter evidence batch. +/// Hard resource ceilings for a single semantic evidence batch. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct EvidenceLimits { pub declarations: usize, @@ -45,7 +45,7 @@ impl Default for EvidenceLimits { #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum EvidenceErrorCode { - InvalidAdapter, + InvalidPipeline, ResourceLimit, DuplicateId, InvalidPath, @@ -90,14 +90,14 @@ pub fn validate_evidence( batch: &SemanticEvidenceBatch, limits: EvidenceLimits, ) -> Result<(), EvidenceError> { - validate_adapter(batch)?; + validate_pipeline(batch)?; validate_limits(batch, limits)?; - let capabilities: BTreeSet<_> = batch.adapter.capabilities.iter().copied().collect(); - if capabilities.len() != batch.adapter.capabilities.len() { + let capabilities: BTreeSet<_> = batch.pipeline.capabilities.iter().copied().collect(); + if capabilities.len() != batch.pipeline.capabilities.len() { return Err(EvidenceError::new( - EvidenceErrorCode::InvalidAdapter, - "adapter capabilities must be unique", + EvidenceErrorCode::InvalidPipeline, + "pipeline capabilities must be unique", )); } @@ -182,7 +182,7 @@ pub fn validate_evidence( for fact in facts { validate_fact( fact, - batch.adapter.language.as_str(), + batch.pipeline.language.as_str(), &capabilities, &declarations, &scopes, @@ -201,10 +201,10 @@ pub fn validate_evidence( .then_with(|| left.message.cmp(&right.message)) }); for diagnostic in diagnostics { - if diagnostic.code.is_empty() || diagnostic.language != batch.adapter.language { + if diagnostic.code.is_empty() || diagnostic.language != batch.pipeline.language { return Err(EvidenceError::new( EvidenceErrorCode::InvalidFact, - "diagnostic code and adapter language must be valid", + "diagnostic code and pipeline language must be valid", )); } if diagnostic.message.len() > limits.diagnostic_message_bytes { @@ -232,11 +232,11 @@ pub fn validate_evidence( Ok(()) } -fn validate_adapter(batch: &SemanticEvidenceBatch) -> Result<(), EvidenceError> { - if batch.adapter.language.trim().is_empty() || batch.adapter.producer.trim().is_empty() { +fn validate_pipeline(batch: &SemanticEvidenceBatch) -> Result<(), EvidenceError> { + if batch.pipeline.language.trim().is_empty() || batch.pipeline.emitter.trim().is_empty() { return Err(EvidenceError::new( - EvidenceErrorCode::InvalidAdapter, - "adapter language and producer must not be empty", + EvidenceErrorCode::InvalidPipeline, + "pipeline language and emitter must not be empty", )); } Ok(()) @@ -291,7 +291,7 @@ impl<'a> Fact<'a> { #[allow(clippy::too_many_arguments)] fn validate_fact( fact: Fact<'_>, - adapter_language: &str, + pipeline_language: &str, capabilities: &BTreeSet, declarations: &AHashMap<&str, &DeclarationFact>, scopes: &AHashMap<&str, &ScopeFact>, @@ -301,7 +301,7 @@ fn validate_fact( ) -> Result<(), EvidenceError> { match fact { Fact::Declaration(fact) => { - validate_language(&fact.id, &fact.language, adapter_language)?; + validate_language(&fact.id, &fact.language, pipeline_language)?; validate_range(&fact.range, &fact.id, fact.kind == "file")?; require_capability(&fact.id, LanguageCapability::Declarations, capabilities)?; if fact.graph_node_id.is_empty() @@ -345,7 +345,7 @@ fn validate_fact( } } Fact::Scope(fact) => { - validate_language(&fact.id, &fact.language, adapter_language)?; + validate_language(&fact.id, &fact.language, pipeline_language)?; let empty_file_scope = fact.kind == "module" && fact .owner_declaration_id @@ -371,7 +371,7 @@ fn validate_fact( )?; } Fact::Binding(fact) => { - validate_language(&fact.id, &fact.language, adapter_language)?; + validate_language(&fact.id, &fact.language, pipeline_language)?; validate_range(&fact.range, &fact.id, false)?; require_capability(&fact.id, fact.kind.required_capability(), capabilities)?; if fact.spelling.is_empty() || fact.qualified_target.is_empty() { @@ -471,7 +471,7 @@ fn validate_fact( require_optional_reference(&fact.id, "scope", fact.scope_id.as_deref(), scopes)?; } Fact::Occurrence(fact) => { - validate_language(&fact.id, &fact.language, adapter_language)?; + validate_language(&fact.id, &fact.language, pipeline_language)?; validate_range(&fact.range, &fact.id, false)?; require_capability(&fact.id, fact.role.required_capability(), capabilities)?; if fact.spelling.is_empty() { @@ -486,7 +486,7 @@ fn validate_fact( require_optional_reference(&fact.id, "scope", fact.scope_id.as_deref(), scopes)?; } Fact::Candidate(fact) => { - validate_language(&fact.id, &fact.language, adapter_language)?; + validate_language(&fact.id, &fact.language, pipeline_language)?; require_capability(&fact.id, fact.relation.required_capability(), capabilities)?; require_reference( &fact.id, @@ -797,12 +797,14 @@ fn validate_callable_types( fn validate_language( id: &str, language: &str, - adapter_language: &str, + pipeline_language: &str, ) -> Result<(), EvidenceError> { - if language != adapter_language { + if language != pipeline_language { return Err(EvidenceError::new( EvidenceErrorCode::LanguageMismatch, - format!("fact {id:?} language {language:?} differs from adapter {adapter_language:?}"), + format!( + "fact {id:?} language {language:?} differs from pipeline {pipeline_language:?}" + ), )); } Ok(()) diff --git a/crates/compass-languages/src/adapters.rs b/crates/compass-languages/src/evidence_pipeline.rs similarity index 51% rename from crates/compass-languages/src/adapters.rs rename to crates/compass-languages/src/evidence_pipeline.rs index f43200a4..c0b4f800 100644 --- a/crates/compass-languages/src/adapters.rs +++ b/crates/compass-languages/src/evidence_pipeline.rs @@ -1,104 +1,121 @@ use crate::LanguageCapability; -/// Publication maturity of one hard-cut universal adapter. +/// Qualification state of one universal evidence pipeline. #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -pub enum UniversalAdapterProfile { - UniversalCandidate, - UniversalComplete, +pub enum UniversalEvidenceQualification { + /// The shared evidence route is production-active while its audit runs. + Qualifying, + /// The shared evidence route passed the complete audit gates. + Qualified, } -/// The universal evidence capabilities implemented by one hard-cut adapter. +/// Language-specific metadata for a universal evidence producer. #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct AdapterProfile { +pub struct UniversalEvidenceProducer { pub id: &'static str, pub language: &'static str, pub version: u32, pub evidence_schema: &'static str, - pub profile: UniversalAdapterProfile, pub capabilities: &'static [LanguageCapability], } +/// The shared evidence pipeline and the qualification state of its producer. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct UniversalEvidencePipeline { + pub producer: UniversalEvidenceProducer, + pub qualification: UniversalEvidenceQualification, +} + #[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)] -pub enum AdapterRegistryError { - #[error("universal adapter id must not be empty")] +pub enum UniversalEvidenceRegistryError { + #[error("universal evidence producer id must not be empty")] EmptyId, - #[error("universal adapter language must not be empty")] + #[error("universal evidence producer language must not be empty")] EmptyLanguage, - #[error("duplicate universal adapter id {0:?}")] + #[error("duplicate universal evidence producer id {0:?}")] DuplicateId(&'static str), - #[error("universal adapter {0:?} must declare at least one capability")] + #[error("universal evidence producer {0:?} must declare at least one capability")] EmptyCapabilities(&'static str), - #[error("duplicate universal adapter language {0:?}")] + #[error("duplicate universal evidence producer language {0:?}")] DuplicateLanguage(&'static str), - #[error("universal adapter languages must be sorted before {0:?}")] + #[error("universal evidence producer languages must be sorted before {0:?}")] UnsortedLanguages(&'static str), - #[error("universal adapter {0:?} capabilities must be sorted and unique")] + #[error("universal evidence producer {0:?} capabilities must be sorted and unique")] InvalidCapabilityOrder(&'static str), - #[error("universal adapter {0:?} must declare a positive adapter version")] + #[error("universal evidence producer {0:?} must declare a positive producer version")] InvalidVersion(&'static str), - #[error("universal adapter {0:?} declares an unsupported evidence schema")] + #[error("universal evidence producer {0:?} declares an unsupported evidence schema")] InvalidEvidenceSchema(&'static str), } -/// Registry of languages that have atomically hard-cut to universal evidence. +/// Registry of languages that have atomically hard-cut to the shared evidence pipeline. #[derive(Debug, Default)] -pub struct AdapterRegistry; +pub struct UniversalEvidenceRegistry; -impl AdapterRegistry { +impl UniversalEvidenceRegistry { #[must_use] - pub fn universal_profile(language: &str) -> Option<&'static AdapterProfile> { - UNIVERSAL_ADAPTERS - .binary_search_by_key(&language, |profile| profile.language) + pub fn pipeline(language: &str) -> Option<&'static UniversalEvidencePipeline> { + UNIVERSAL_EVIDENCE_PIPELINES + .binary_search_by_key(&language, |pipeline| pipeline.producer.language) .ok() - .map(|index| &UNIVERSAL_ADAPTERS[index]) + .map(|index| &UNIVERSAL_EVIDENCE_PIPELINES[index]) } #[must_use] - pub const fn universal_profiles() -> &'static [AdapterProfile] { - UNIVERSAL_ADAPTERS + pub const fn pipelines() -> &'static [UniversalEvidencePipeline] { + UNIVERSAL_EVIDENCE_PIPELINES } - pub fn validate() -> Result<(), AdapterRegistryError> { + pub fn validate() -> Result<(), UniversalEvidenceRegistryError> { let mut previous_language = None; let mut ids = std::collections::BTreeSet::new(); - for profile in UNIVERSAL_ADAPTERS { - if profile.id.is_empty() { - return Err(AdapterRegistryError::EmptyId); + for pipeline in UNIVERSAL_EVIDENCE_PIPELINES { + let producer = pipeline.producer; + if producer.id.is_empty() { + return Err(UniversalEvidenceRegistryError::EmptyId); } - if profile.language.is_empty() { - return Err(AdapterRegistryError::EmptyLanguage); + if producer.language.is_empty() { + return Err(UniversalEvidenceRegistryError::EmptyLanguage); } - if !ids.insert(profile.id) { - return Err(AdapterRegistryError::DuplicateId(profile.id)); + if !ids.insert(producer.id) { + return Err(UniversalEvidenceRegistryError::DuplicateId(producer.id)); } - if profile.version == 0 { - return Err(AdapterRegistryError::InvalidVersion(profile.language)); + if producer.version == 0 { + return Err(UniversalEvidenceRegistryError::InvalidVersion( + producer.language, + )); } - if profile.evidence_schema != crate::UNIVERSAL_EVIDENCE_SCHEMA { - return Err(AdapterRegistryError::InvalidEvidenceSchema( - profile.language, + if producer.evidence_schema != crate::UNIVERSAL_EVIDENCE_SCHEMA { + return Err(UniversalEvidenceRegistryError::InvalidEvidenceSchema( + producer.language, )); } - if profile.capabilities.is_empty() { - return Err(AdapterRegistryError::EmptyCapabilities(profile.language)); + if producer.capabilities.is_empty() { + return Err(UniversalEvidenceRegistryError::EmptyCapabilities( + producer.language, + )); } - if previous_language == Some(profile.language) { - return Err(AdapterRegistryError::DuplicateLanguage(profile.language)); + if previous_language == Some(producer.language) { + return Err(UniversalEvidenceRegistryError::DuplicateLanguage( + producer.language, + )); } - if previous_language.is_some_and(|previous| previous > profile.language) { - return Err(AdapterRegistryError::UnsortedLanguages(profile.language)); + if previous_language.is_some_and(|previous| previous > producer.language) { + return Err(UniversalEvidenceRegistryError::UnsortedLanguages( + producer.language, + )); } - if profile + if producer .capabilities .windows(2) .any(|pair| pair[0] >= pair[1]) { - return Err(AdapterRegistryError::InvalidCapabilityOrder( - profile.language, + return Err(UniversalEvidenceRegistryError::InvalidCapabilityOrder( + producer.language, )); } - previous_language = Some(profile.language); + previous_language = Some(producer.language); } Ok(()) } @@ -285,87 +302,107 @@ pub(crate) const RUBY_CAPABILITIES: &[LanguageCapability] = &[ LanguageCapability::ExternalReferences, ]; -pub(crate) const RUBY_ADAPTER_PROFILE: AdapterProfile = AdapterProfile { - id: "compass.ruby.candidate", - language: "ruby", - version: 1, - evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, - profile: UniversalAdapterProfile::UniversalCandidate, - capabilities: RUBY_CAPABILITIES, -}; - -const UNIVERSAL_ADAPTERS: &[AdapterProfile] = &[ - AdapterProfile { - id: "compass.csharp.candidate", - language: "csharp", +pub(crate) const RUBY_EVIDENCE_PIPELINE: UniversalEvidencePipeline = UniversalEvidencePipeline { + producer: UniversalEvidenceProducer { + id: "compass.ruby", + language: "ruby", version: 1, evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, - profile: UniversalAdapterProfile::UniversalCandidate, - capabilities: CSHARP_CAPABILITIES, + capabilities: RUBY_CAPABILITIES, }, - AdapterProfile { - id: "compass.go", - language: "go", - version: 3, - evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, - profile: UniversalAdapterProfile::UniversalCandidate, - capabilities: GO_CAPABILITIES, + qualification: UniversalEvidenceQualification::Qualifying, +}; + +const UNIVERSAL_EVIDENCE_PIPELINES: &[UniversalEvidencePipeline] = &[ + UniversalEvidencePipeline { + producer: UniversalEvidenceProducer { + id: "compass.csharp", + language: "csharp", + version: 1, + evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, + capabilities: CSHARP_CAPABILITIES, + }, + qualification: UniversalEvidenceQualification::Qualifying, }, - AdapterProfile { - id: "compass.java", - language: "java", - version: 3, - evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, - profile: UniversalAdapterProfile::UniversalCandidate, - capabilities: JAVA_CAPABILITIES, + UniversalEvidencePipeline { + producer: UniversalEvidenceProducer { + id: "compass.go", + language: "go", + version: 3, + evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, + capabilities: GO_CAPABILITIES, + }, + qualification: UniversalEvidenceQualification::Qualifying, }, - AdapterProfile { - id: "compass.javascript.candidate", - language: "javascript", - version: 5, - evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, - profile: UniversalAdapterProfile::UniversalCandidate, - capabilities: JAVASCRIPT_CAPABILITIES, + UniversalEvidencePipeline { + producer: UniversalEvidenceProducer { + id: "compass.java", + language: "java", + version: 3, + evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, + capabilities: JAVA_CAPABILITIES, + }, + qualification: UniversalEvidenceQualification::Qualifying, }, - AdapterProfile { - id: "compass.kotlin.candidate", - language: "kotlin", - version: 1, - evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, - profile: UniversalAdapterProfile::UniversalCandidate, - capabilities: KOTLIN_CAPABILITIES, + UniversalEvidencePipeline { + producer: UniversalEvidenceProducer { + id: "compass.javascript", + language: "javascript", + version: 5, + evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, + capabilities: JAVASCRIPT_CAPABILITIES, + }, + qualification: UniversalEvidenceQualification::Qualifying, }, - AdapterProfile { - id: "compass.php", - language: "php", - version: 1, - evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, - profile: UniversalAdapterProfile::UniversalCandidate, - capabilities: PHP_CAPABILITIES, + UniversalEvidencePipeline { + producer: UniversalEvidenceProducer { + id: "compass.kotlin", + language: "kotlin", + version: 1, + evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, + capabilities: KOTLIN_CAPABILITIES, + }, + qualification: UniversalEvidenceQualification::Qualifying, }, - AdapterProfile { - id: "compass.python", - language: "python", - version: 11, - evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, - profile: UniversalAdapterProfile::UniversalCandidate, - capabilities: PYTHON_CAPABILITIES, + UniversalEvidencePipeline { + producer: UniversalEvidenceProducer { + id: "compass.php", + language: "php", + version: 1, + evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, + capabilities: PHP_CAPABILITIES, + }, + qualification: UniversalEvidenceQualification::Qualifying, }, - RUBY_ADAPTER_PROFILE, - AdapterProfile { - id: "compass.rust", - language: "rust", - version: 15, - evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, - profile: UniversalAdapterProfile::UniversalCandidate, - capabilities: RUST_CAPABILITIES, + UniversalEvidencePipeline { + producer: UniversalEvidenceProducer { + id: "compass.python", + language: "python", + version: 11, + evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, + capabilities: PYTHON_CAPABILITIES, + }, + qualification: UniversalEvidenceQualification::Qualifying, }, - AdapterProfile { - id: "compass.typescript.candidate", - language: "typescript", - version: 5, - evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, - profile: UniversalAdapterProfile::UniversalCandidate, - capabilities: TYPESCRIPT_CAPABILITIES, + RUBY_EVIDENCE_PIPELINE, + UniversalEvidencePipeline { + producer: UniversalEvidenceProducer { + id: "compass.rust", + language: "rust", + version: 15, + evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, + capabilities: RUST_CAPABILITIES, + }, + qualification: UniversalEvidenceQualification::Qualifying, + }, + UniversalEvidencePipeline { + producer: UniversalEvidenceProducer { + id: "compass.typescript", + language: "typescript", + version: 5, + evidence_schema: crate::UNIVERSAL_EVIDENCE_SCHEMA, + capabilities: TYPESCRIPT_CAPABILITIES, + }, + qualification: UniversalEvidenceQualification::Qualifying, }, ]; diff --git a/crates/compass-languages/src/frameworks/csharp.rs b/crates/compass-languages/src/frameworks/csharp.rs index 38a14f51..8405e9dc 100644 --- a/crates/compass-languages/src/frameworks/csharp.rs +++ b/crates/compass-languages/src/frameworks/csharp.rs @@ -19,7 +19,7 @@ const FRAMEWORK: &str = "aspnet"; const MVC_NAMESPACE: &str = "Microsoft.AspNetCore.Mvc"; pub(super) fn detect(context: &UniversalDetectionContext<'_, '_>) -> Vec { - if context.evidence.adapter.language != "csharp" { + if context.evidence.pipeline.language != "csharp" { return Vec::new(); } let activated = context.project.is_some_and(|project| { diff --git a/crates/compass-languages/src/frameworks/pack.rs b/crates/compass-languages/src/frameworks/pack.rs index 7501c79b..a33af432 100644 --- a/crates/compass-languages/src/frameworks/pack.rs +++ b/crates/compass-languages/src/frameworks/pack.rs @@ -1,6 +1,6 @@ use std::collections::BTreeSet; -use crate::{AdapterRegistry, LanguageCapability, SemanticRole}; +use crate::{LanguageCapability, SemanticRole, UniversalEvidenceRegistry}; use super::FrameworkLimits; @@ -323,14 +323,14 @@ fn validate_descriptor( } } for &language in descriptor.languages { - let Some(profile) = AdapterRegistry::universal_profile(language) else { + let Some(pipeline) = UniversalEvidenceRegistry::pipeline(language) else { return Err(FrameworkPackRegistryError::NonUniversalLanguage { pack: descriptor.id, language, }); }; for &capability in descriptor.required_capabilities { - if !profile.capabilities.contains(&capability) { + if !pipeline.producer.capabilities.contains(&capability) { return Err(FrameworkPackRegistryError::UnsupportedCapability { pack: descriptor.id, language, diff --git a/crates/compass-languages/src/frameworks/php.rs b/crates/compass-languages/src/frameworks/php.rs index b80f0243..13998dce 100644 --- a/crates/compass-languages/src/frameworks/php.rs +++ b/crates/compass-languages/src/frameworks/php.rs @@ -25,7 +25,7 @@ struct LaravelCall { } pub(super) fn detect(context: &UniversalDetectionContext<'_, '_>) -> Vec { - if context.evidence.adapter.language != "php" { + if context.evidence.pipeline.language != "php" { return Vec::new(); } let route_calls = universal_laravel_call_sites(context); diff --git a/crates/compass-languages/src/frameworks/ruby.rs b/crates/compass-languages/src/frameworks/ruby.rs index c2fb7204..14289409 100644 --- a/crates/compass-languages/src/frameworks/ruby.rs +++ b/crates/compass-languages/src/frameworks/ruby.rs @@ -15,7 +15,7 @@ use super::{ pub(super) fn detect_universal( context: &UniversalDetectionContext<'_, '_>, ) -> Vec { - if context.evidence.adapter.language != "ruby" { + if context.evidence.pipeline.language != "ruby" { return Vec::new(); } let source_file = context diff --git a/crates/compass-languages/src/frameworks/spring.rs b/crates/compass-languages/src/frameworks/spring.rs index 34bd310c..6db08252 100644 --- a/crates/compass-languages/src/frameworks/spring.rs +++ b/crates/compass-languages/src/frameworks/spring.rs @@ -38,7 +38,7 @@ fn detect_language( language: &str, dependency_markers: &[&str], ) -> Vec { - if context.evidence.adapter.language != language { + if context.evidence.pipeline.language != language { return Vec::new(); } let declarations = context @@ -258,7 +258,7 @@ fn unique_binding_map(context: &UniversalDetectionContext<'_, '_>) -> Map) -> Vec { - if context.evidence.adapter.language == "kotlin" { + if context.evidence.pipeline.language == "kotlin" { let mut values = BTreeMap::new(); collect_kotlin_constant_values(context.root, context.source, &mut values); return context @@ -447,7 +447,7 @@ fn producer_facts( candidates: &HashMap<&str, &crate::RelationshipCandidate>, ) -> Vec { let mut call_nodes = BTreeMap::new(); - if context.evidence.adapter.language == "kotlin" { + if context.evidence.pipeline.language == "kotlin" { collect_kotlin_call_nodes(context.root, &mut call_nodes); } else { collect_named_nodes(context.root, "method_invocation", "name", &mut call_nodes); @@ -490,7 +490,7 @@ fn producer_facts( call, argument_index, context.source, - context.evidence.adapter.language.as_str(), + context.evidence.pipeline.language.as_str(), )?; let mut detail = Map::new(); detail.insert( @@ -655,7 +655,7 @@ fn constructor_facts( .filter_map(|declaration| { let mut targets = injection_targets(context, declaration.id.as_str(), candidates); if targets.is_empty() - && context.evidence.adapter.language == "kotlin" + && context.evidence.pipeline.language == "kotlin" && let Some(owner) = declaration .qualified_name .rsplit_once("::") diff --git a/crates/compass-languages/src/lib.rs b/crates/compass-languages/src/lib.rs index 20855477..2acdad9e 100644 --- a/crates/compass-languages/src/lib.rs +++ b/crates/compass-languages/src/lib.rs @@ -1,6 +1,5 @@ //! Statically linked deterministic language extraction for Compass. -mod adapters; mod apex; mod bash; mod builtins; @@ -12,12 +11,13 @@ mod dotnet_project; mod elixir; mod engine; pub mod evidence; +mod evidence_pipeline; mod facts; mod fortran; pub mod frameworks; /// Version of the extraction contract consumed by graph publication. -pub const EXTRACTION_SEMANTICS_VERSION: &str = "compass.languages.extraction/2"; +pub const EXTRACTION_SEMANTICS_VERSION: &str = "compass.languages.extraction/3"; mod go; mod groovy; mod html; @@ -46,17 +46,19 @@ mod verilog; mod xaml; mod zig; -pub use adapters::{ - AdapterProfile, AdapterRegistry, AdapterRegistryError, UniversalAdapterProfile, -}; #[doc(hidden)] pub use builtins::{is_language_builtin_global, is_language_builtin_qualified_target}; pub use evidence::{ - AdapterIdentity, BindingFact, BindingKind, CandidateRelation, DeclarationFact, EvidenceBuilder, + BindingFact, BindingKind, CandidateRelation, DeclarationFact, EvidenceBuilder, EvidenceDiagnostic, EvidenceError, EvidenceErrorCode, EvidenceLimits, EvidenceRange, HierarchyConstraint, LanguageCapability, OccurrenceFact, ReceiverDispatchStrategy, RelationshipCandidate, ResolutionConstraint, ScopeFact, SemanticEvidenceBatch, SemanticRole, - SymbolNamespace, UNIVERSAL_EVIDENCE_SCHEMA, range_for_node, validate_evidence, + SymbolNamespace, UNIVERSAL_EVIDENCE_SCHEMA, UniversalEvidenceIdentity, range_for_node, + validate_evidence, +}; +pub use evidence_pipeline::{ + UniversalEvidencePipeline, UniversalEvidenceProducer, UniversalEvidenceQualification, + UniversalEvidenceRegistry, UniversalEvidenceRegistryError, }; pub use facts::{Extraction, RawCall, RawEdgeRecord, RawNodeRecord}; pub use frameworks::{ diff --git a/crates/compass-languages/src/registry.rs b/crates/compass-languages/src/registry.rs index f85656c6..a99c4e9a 100644 --- a/crates/compass-languages/src/registry.rs +++ b/crates/compass-languages/src/registry.rs @@ -1,6 +1,6 @@ use std::path::Path; -use crate::{AdapterProfile, AdapterRegistry}; +use crate::{UniversalEvidencePipeline, UniversalEvidenceRegistry}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ExtractorKind { @@ -70,24 +70,26 @@ impl Registry { REGISTRY_CASES } - /// Resolve a path only when its language has hard-cut to universal evidence. + /// Resolve a path only when its language has hard-cut to the evidence pipeline. #[must_use] - pub fn universal_adapter(path: &Path) -> Option<&'static AdapterProfile> { - Self::resolve(path).and_then(Self::universal_profile_for_spec) + pub fn universal_evidence_pipeline(path: &Path) -> Option<&'static UniversalEvidencePipeline> { + Self::resolve(path).and_then(Self::universal_evidence_pipeline_for_spec) } - /// Return the hard-cut adapter associated with a resolved language. + /// Return the evidence pipeline associated with a resolved language. /// - /// TSX is a parser dialect of the canonical TypeScript universal adapter; + /// TSX is a parser dialect of the canonical TypeScript evidence pipeline; /// keep the registry's `tsx` grammar name while publishing one stable /// semantic language identity for resolution and cache invalidation. #[must_use] - pub fn universal_profile_for_spec(spec: LanguageSpec) -> Option<&'static AdapterProfile> { + pub fn universal_evidence_pipeline_for_spec( + spec: LanguageSpec, + ) -> Option<&'static UniversalEvidencePipeline> { let language = match spec.name { "tsx" => "typescript", language => language, }; - AdapterRegistry::universal_profile(language) + UniversalEvidenceRegistry::pipeline(language) } #[must_use] diff --git a/crates/compass-languages/tests/csharp_universal_conformance.rs b/crates/compass-languages/tests/csharp_universal_conformance.rs index 0416de7c..31dea1d6 100644 --- a/crates/compass-languages/tests/csharp_universal_conformance.rs +++ b/crates/compass-languages/tests/csharp_universal_conformance.rs @@ -2,8 +2,8 @@ use std::error::Error; use std::path::Path; use compass_languages::{ - AdapterRegistry, BindingKind, CandidateRelation, Engine, EvidenceLimits, LanguageCapability, - SemanticRole, UniversalAdapterProfile, validate_evidence, + BindingKind, CandidateRelation, Engine, EvidenceLimits, LanguageCapability, SemanticRole, + UniversalEvidenceQualification, UniversalEvidenceRegistry, validate_evidence, }; #[test] @@ -63,11 +63,11 @@ public partial class UsersController : ControllerBase, IWorker .as_ref() .ok_or("missing C# universal evidence")?; validate_evidence(evidence, EvidenceLimits::default())?; - assert_eq!(evidence.adapter.id, "compass.csharp.candidate"); - assert_eq!(evidence.adapter.version, 1); + assert_eq!(evidence.pipeline.id, "compass.csharp"); + assert_eq!(evidence.pipeline.version, 1); assert_eq!( - evidence.adapter.profile, - UniversalAdapterProfile::UniversalCandidate + evidence.pipeline.qualification, + UniversalEvidenceQualification::Qualifying ); for capability in [ LanguageCapability::Namespaces, @@ -81,7 +81,7 @@ public partial class UsersController : ControllerBase, IWorker LanguageCapability::Members, LanguageCapability::Receivers, ] { - assert!(evidence.adapter.capabilities.contains(&capability)); + assert!(evidence.pipeline.capabilities.contains(&capability)); } let controller = evidence .declarations @@ -218,8 +218,11 @@ fn csharp_evidence_identity_is_independent_of_checkout_root() -> Result<(), Box< } #[test] -fn csharp_profile_is_registered_as_a_universal_candidate() -> Result<(), Box> { - let profile = AdapterRegistry::universal_profile("csharp").ok_or("missing C# profile")?; - assert_eq!(profile.profile, UniversalAdapterProfile::UniversalCandidate); +fn csharp_pipeline_is_registered_as_qualifying() -> Result<(), Box> { + let pipeline = UniversalEvidenceRegistry::pipeline("csharp").ok_or("missing C# pipeline")?; + assert_eq!( + pipeline.qualification, + UniversalEvidenceQualification::Qualifying + ); Ok(()) } diff --git a/crates/compass-languages/tests/engine_edge_coverage.rs b/crates/compass-languages/tests/engine_edge_coverage.rs index 86679574..00557b14 100644 --- a/crates/compass-languages/tests/engine_edge_coverage.rs +++ b/crates/compass-languages/tests/engine_edge_coverage.rs @@ -420,9 +420,9 @@ fn repeated_rust_calls_keep_exact_sites_and_known_producer_metadata() -> Result< .collect::>(); assert_eq!(calls.len(), 2, "occurrences={:?}", evidence.occurrences); - assert_eq!(evidence.adapter.language, "rust"); + assert_eq!(evidence.pipeline.language, "rust"); assert_eq!( - evidence.adapter.producer, + evidence.pipeline.emitter, "compass.languages.rust.universal" ); @@ -929,7 +929,7 @@ class Service(Base[ExternalType]): value: Annotated[Optional[Union[InputType, None]], "meta"], ) -> tuple[OutputType, ...]: """A function rationale long enough to be indexed safely.""" - # WHY: retain this adapter for compatibility with old callers + # WHY: retain this evidence path for compatibility with old callers local = callback assigned = (external_factory, local) consume(external_argument, named=external_keyword) diff --git a/crates/compass-languages/tests/java_universal_conformance.rs b/crates/compass-languages/tests/java_universal_conformance.rs index 2e47841d..bf4c1ea0 100644 --- a/crates/compass-languages/tests/java_universal_conformance.rs +++ b/crates/compass-languages/tests/java_universal_conformance.rs @@ -4,7 +4,7 @@ use std::path::Path; use compass_languages::{ BindingKind, CandidateRelation, Engine, EvidenceLimits, LanguageCapability, SemanticRole, - UniversalAdapterProfile, validate_evidence, + UniversalEvidenceQualification, validate_evidence, }; #[test] @@ -68,14 +68,14 @@ enum Status { let evidence = extraction.graph.semantic_evidence.expect("Java evidence"); validate_evidence(&evidence, EvidenceLimits::default()).expect("valid Java evidence"); - assert_eq!(evidence.adapter.version, 3); + assert_eq!(evidence.pipeline.version, 3); assert_eq!( - evidence.adapter.profile, - UniversalAdapterProfile::UniversalCandidate + evidence.pipeline.qualification, + UniversalEvidenceQualification::Qualifying ); assert!( evidence - .adapter + .pipeline .capabilities .contains(&LanguageCapability::Namespaces) ); diff --git a/crates/compass-languages/tests/kotlin_universal_conformance.rs b/crates/compass-languages/tests/kotlin_universal_conformance.rs index 52003c0b..861f4357 100644 --- a/crates/compass-languages/tests/kotlin_universal_conformance.rs +++ b/crates/compass-languages/tests/kotlin_universal_conformance.rs @@ -1,6 +1,6 @@ use std::path::Path; -use compass_languages::{CandidateRelation, Engine, SemanticRole, UniversalAdapterProfile}; +use compass_languages::{CandidateRelation, Engine, SemanticRole, UniversalEvidenceQualification}; const SOURCE: &[u8] = br#" package demo.api @@ -47,17 +47,17 @@ fn kotlin_emits_modern_universal_evidence_with_exact_anchors() .semantic_evidence .as_ref() .ok_or_else(|| format!("missing Kotlin evidence: {:?}", extraction.error))?; - let qualification = Engine::default().extract_source_universal_candidate_evidence( + let qualification = Engine::default().extract_source_universal_evidence( Path::new("src/main/kotlin/demo/api/Controller.kt"), "src/main/kotlin/demo/api/Controller.kt", SOURCE, )?; assert_eq!(evidence, &qualification); - assert_eq!(evidence.adapter.language, "kotlin"); - assert_eq!(evidence.adapter.version, 1); + assert_eq!(evidence.pipeline.language, "kotlin"); + assert_eq!(evidence.pipeline.version, 1); assert_eq!( - evidence.adapter.profile, - UniversalAdapterProfile::UniversalCandidate + evidence.pipeline.qualification, + UniversalEvidenceQualification::Qualifying ); for (kind, qualified) in [ ("annotation_type", "demo.api.Audit"), diff --git a/crates/compass-languages/tests/php_universal_conformance.rs b/crates/compass-languages/tests/php_universal_conformance.rs index cbca5709..1455d1a5 100644 --- a/crates/compass-languages/tests/php_universal_conformance.rs +++ b/crates/compass-languages/tests/php_universal_conformance.rs @@ -2,8 +2,9 @@ use std::error::Error; use std::path::Path; use compass_languages::{ - AdapterRegistry, BindingKind, CandidateRelation, Engine, EvidenceLimits, LanguageCapability, - RawFrameworkFact, RawFrameworkOrigin, SemanticRole, UniversalAdapterProfile, validate_evidence, + BindingKind, CandidateRelation, Engine, EvidenceLimits, LanguageCapability, RawFrameworkFact, + RawFrameworkOrigin, SemanticRole, UniversalEvidenceQualification, UniversalEvidenceRegistry, + validate_evidence, }; #[test] @@ -85,11 +86,11 @@ function top(): \Closure .as_ref() .ok_or("missing PHP universal evidence")?; validate_evidence(evidence, EvidenceLimits::default())?; - assert_eq!(evidence.adapter.id, "compass.php"); - assert_eq!(evidence.adapter.version, 1); + assert_eq!(evidence.pipeline.id, "compass.php"); + assert_eq!(evidence.pipeline.version, 1); assert_eq!( - evidence.adapter.profile, - UniversalAdapterProfile::UniversalCandidate + evidence.pipeline.qualification, + UniversalEvidenceQualification::Qualifying ); for capability in [ LanguageCapability::Namespaces, @@ -104,7 +105,7 @@ function top(): \Closure LanguageCapability::Members, LanguageCapability::Receivers, ] { - assert!(evidence.adapter.capabilities.contains(&capability)); + assert!(evidence.pipeline.capabilities.contains(&capability)); } for (qualified_name, kind, source_name) in [ @@ -220,10 +221,13 @@ function top(): \Closure } #[test] -fn php_profile_is_registered_in_sorted_universal_registry() -> Result<(), Box> { - AdapterRegistry::validate()?; - let profile = AdapterRegistry::universal_profile("php").ok_or("missing PHP profile")?; - assert_eq!(profile.profile, UniversalAdapterProfile::UniversalCandidate); +fn php_pipeline_is_registered_in_sorted_universal_registry() -> Result<(), Box> { + UniversalEvidenceRegistry::validate()?; + let pipeline = UniversalEvidenceRegistry::pipeline("php").ok_or("missing PHP pipeline")?; + assert_eq!( + pipeline.qualification, + UniversalEvidenceQualification::Qualifying + ); Ok(()) } diff --git a/crates/compass-languages/tests/registry.rs b/crates/compass-languages/tests/registry.rs index e02f2349..bb4724ec 100644 --- a/crates/compass-languages/tests/registry.rs +++ b/crates/compass-languages/tests/registry.rs @@ -5,8 +5,8 @@ use std::fs; use std::path::Path; use compass_languages::{ - AdapterRegistry, LanguageCapability, Registry, UNIVERSAL_EVIDENCE_SCHEMA, - UniversalAdapterProfile, file_stem, make_id, normalize_id, + LanguageCapability, Registry, UNIVERSAL_EVIDENCE_SCHEMA, UniversalEvidenceQualification, + UniversalEvidenceRegistry, file_stem, make_id, normalize_id, }; #[test] @@ -114,13 +114,16 @@ fn ids_match_python_unicode_casefold_contract() { } #[test] -fn rust_is_a_version_fifteen_hard_cut_candidate_descriptor() { - let rust = AdapterRegistry::universal_profile("rust").expect("Rust universal profile"); - assert_eq!(rust.id, "compass.rust"); - assert_eq!(rust.language, "rust"); - assert_eq!(rust.evidence_schema, UNIVERSAL_EVIDENCE_SCHEMA); - assert_eq!(rust.version, 15); - assert_eq!(rust.profile, UniversalAdapterProfile::UniversalCandidate); +fn rust_pipeline_is_version_fifteen_and_qualifying() { + let rust = UniversalEvidenceRegistry::pipeline("rust").expect("Rust universal pipeline"); + assert_eq!(rust.producer.id, "compass.rust"); + assert_eq!(rust.producer.language, "rust"); + assert_eq!(rust.producer.evidence_schema, UNIVERSAL_EVIDENCE_SCHEMA); + assert_eq!(rust.producer.version, 15); + assert_eq!( + rust.qualification, + UniversalEvidenceQualification::Qualifying + ); for capability in [ LanguageCapability::Namespaces, LanguageCapability::Traits, @@ -133,20 +136,23 @@ fn rust_is_a_version_fifteen_hard_cut_candidate_descriptor() { LanguageCapability::ExternalReferences, ] { assert!( - rust.capabilities.contains(&capability), + rust.producer.capabilities.contains(&capability), "missing {capability:?}: {rust:?}" ); } let typescript = - AdapterRegistry::universal_profile("typescript").expect("TypeScript universal profile"); - assert_eq!(typescript.id, "compass.typescript.candidate"); - assert_eq!(typescript.language, "typescript"); - assert_eq!(typescript.evidence_schema, UNIVERSAL_EVIDENCE_SCHEMA); - assert_eq!(typescript.version, 5); + UniversalEvidenceRegistry::pipeline("typescript").expect("TypeScript universal pipeline"); + assert_eq!(typescript.producer.id, "compass.typescript"); + assert_eq!(typescript.producer.language, "typescript"); + assert_eq!( + typescript.producer.evidence_schema, + UNIVERSAL_EVIDENCE_SCHEMA + ); + assert_eq!(typescript.producer.version, 5); assert_eq!( - typescript.profile, - UniversalAdapterProfile::UniversalCandidate + typescript.qualification, + UniversalEvidenceQualification::Qualifying ); for capability in [ LanguageCapability::Namespaces, @@ -160,30 +166,33 @@ fn rust_is_a_version_fifteen_hard_cut_candidate_descriptor() { LanguageCapability::ExternalReferences, ] { assert!( - typescript.capabilities.contains(&capability), + typescript.producer.capabilities.contains(&capability), "missing {capability:?}: {typescript:?}" ); } let javascript = - AdapterRegistry::universal_profile("javascript").expect("JavaScript universal profile"); - assert_eq!(javascript.id, "compass.javascript.candidate"); - assert_eq!(javascript.language, "javascript"); - assert_eq!(javascript.version, 5); + UniversalEvidenceRegistry::pipeline("javascript").expect("JavaScript universal pipeline"); + assert_eq!(javascript.producer.id, "compass.javascript"); + assert_eq!(javascript.producer.language, "javascript"); + assert_eq!(javascript.producer.version, 5); assert_eq!( - javascript.profile, - UniversalAdapterProfile::UniversalCandidate + javascript.qualification, + UniversalEvidenceQualification::Qualifying ); } #[test] -fn java_is_a_version_three_hard_cut_candidate_descriptor() { - let java = AdapterRegistry::universal_profile("java").expect("Java universal profile"); - assert_eq!(java.id, "compass.java"); - assert_eq!(java.language, "java"); - assert_eq!(java.evidence_schema, UNIVERSAL_EVIDENCE_SCHEMA); - assert_eq!(java.version, 3); - assert_eq!(java.profile, UniversalAdapterProfile::UniversalCandidate); +fn java_pipeline_is_version_three_and_qualifying() { + let java = UniversalEvidenceRegistry::pipeline("java").expect("Java universal pipeline"); + assert_eq!(java.producer.id, "compass.java"); + assert_eq!(java.producer.language, "java"); + assert_eq!(java.producer.evidence_schema, UNIVERSAL_EVIDENCE_SCHEMA); + assert_eq!(java.producer.version, 3); + assert_eq!( + java.qualification, + UniversalEvidenceQualification::Qualifying + ); for capability in [ LanguageCapability::Namespaces, LanguageCapability::Imports, @@ -196,14 +205,14 @@ fn java_is_a_version_three_hard_cut_candidate_descriptor() { LanguageCapability::ExternalReferences, ] { assert!( - java.capabilities.contains(&capability), + java.producer.capabilities.contains(&capability), "missing {capability:?}: {java:?}" ); } } #[test] -fn only_hard_cut_languages_expose_universal_profiles() { +fn only_hard_cut_languages_expose_pipelines() { let python = Registry::resolve(Path::new("src/example.py")).expect("python spec"); let go = Registry::resolve(Path::new("src/example.go")).expect("go spec"); let java = Registry::resolve(Path::new("src/Example.java")).expect("java spec"); @@ -215,47 +224,58 @@ fn only_hard_cut_languages_expose_universal_profiles() { let javascript = Registry::resolve(Path::new("src/example.js")).expect("javascript spec"); assert_eq!( - Registry::universal_profile_for_spec(python).map(|profile| profile.language), + Registry::universal_evidence_pipeline_for_spec(python) + .map(|pipeline| pipeline.producer.language), Some("python") ); assert_eq!( - Registry::universal_profile_for_spec(go).map(|profile| profile.language), + Registry::universal_evidence_pipeline_for_spec(go) + .map(|pipeline| pipeline.producer.language), Some("go") ); assert_eq!( - Registry::universal_profile_for_spec(java).map(|profile| profile.language), + Registry::universal_evidence_pipeline_for_spec(java) + .map(|pipeline| pipeline.producer.language), Some("java") ); assert_eq!( - Registry::universal_profile_for_spec(kotlin).map(|profile| profile.language), + Registry::universal_evidence_pipeline_for_spec(kotlin) + .map(|pipeline| pipeline.producer.language), Some("kotlin") ); assert_eq!( - Registry::universal_profile_for_spec(ruby).map(|profile| profile.language), + Registry::universal_evidence_pipeline_for_spec(ruby) + .map(|pipeline| pipeline.producer.language), Some("ruby") ); assert_eq!( - Registry::universal_profile_for_spec(rust).map(|profile| profile.language), + Registry::universal_evidence_pipeline_for_spec(rust) + .map(|pipeline| pipeline.producer.language), Some("rust") ); assert_eq!( - Registry::universal_profile_for_spec(typescript).map(|profile| profile.language), + Registry::universal_evidence_pipeline_for_spec(typescript) + .map(|pipeline| pipeline.producer.language), Some("typescript") ); assert_eq!( - Registry::universal_profile_for_spec(tsx).map(|profile| profile.language), + Registry::universal_evidence_pipeline_for_spec(tsx) + .map(|pipeline| pipeline.producer.language), Some("typescript") ); assert_eq!( - Registry::universal_profile_for_spec(javascript).map(|profile| profile.language), + Registry::universal_evidence_pipeline_for_spec(javascript) + .map(|pipeline| pipeline.producer.language), Some("javascript") ); assert_eq!( - Registry::universal_adapter(Path::new("src/example.py")).map(|profile| profile.language), + Registry::universal_evidence_pipeline(Path::new("src/example.py")) + .map(|pipeline| pipeline.producer.language), Some("python") ); assert_eq!( - Registry::universal_adapter(Path::new("src/example.tsx")).map(|profile| profile.language), + Registry::universal_evidence_pipeline(Path::new("src/example.tsx")) + .map(|pipeline| pipeline.producer.language), Some("typescript") ); } diff --git a/crates/compass-languages/tests/ruby_universal_conformance.rs b/crates/compass-languages/tests/ruby_universal_conformance.rs index 02874a09..a3542df2 100644 --- a/crates/compass-languages/tests/ruby_universal_conformance.rs +++ b/crates/compass-languages/tests/ruby_universal_conformance.rs @@ -6,8 +6,8 @@ use compass_languages::{CandidateRelation, Engine, SemanticRole, validate_eviden fn extract(source: &[u8]) -> compass_languages::SemanticEvidenceBatch { Engine::default() - .extract_source_universal_candidate_evidence(Path::new("fixture.rb"), "fixture.rb", source) - .expect("Ruby candidate evidence") + .extract_source_universal_evidence(Path::new("fixture.rb"), "fixture.rb", source) + .expect("Ruby universal evidence") } #[test] @@ -196,7 +196,7 @@ fn deep_recovery_is_bounded_and_reports_a_typed_limit() { } #[test] -fn candidate_is_deterministic_under_repeated_extraction() { +fn universal_evidence_is_deterministic_under_repeated_extraction() { let source = "class Café\n def naïve(value = 1)\n value\n end\nend\n".as_bytes(); let left = extract(source); let right = extract(source); @@ -276,8 +276,8 @@ end extraction .semantic_evidence .as_ref() - .map(|evidence| evidence.adapter.id.as_str()), - Some("compass.ruby.candidate") + .map(|evidence| evidence.pipeline.id.as_str()), + Some("compass.ruby") ); assert!(extraction.raw_calls.is_none()); assert!(extraction.framework_facts.iter().any(|fact| { diff --git a/crates/compass-languages/tests/rust_universal_conformance.rs b/crates/compass-languages/tests/rust_universal_conformance.rs index 51674bf3..fad68e58 100644 --- a/crates/compass-languages/tests/rust_universal_conformance.rs +++ b/crates/compass-languages/tests/rust_universal_conformance.rs @@ -2,7 +2,7 @@ use std::error::Error; use compass_languages::{ BindingKind, CandidateRelation, Engine, HierarchyConstraint, LanguageCapability, SemanticRole, - UniversalAdapterProfile, + UniversalEvidenceQualification, }; #[test] @@ -31,15 +31,15 @@ fn build() { .as_ref() .ok_or("missing Rust semantic evidence")?; - assert_eq!(evidence.adapter.id, "compass.rust"); - assert_eq!(evidence.adapter.version, 15); + assert_eq!(evidence.pipeline.id, "compass.rust"); + assert_eq!(evidence.pipeline.version, 15); assert_eq!( - evidence.adapter.evidence_schema, - "compass.languages.evidence/1" + evidence.pipeline.evidence_schema, + "compass.languages.evidence/2" ); assert_eq!( - evidence.adapter.profile, - UniversalAdapterProfile::UniversalCandidate + evidence.pipeline.qualification, + UniversalEvidenceQualification::Qualifying ); for capability in [ LanguageCapability::Namespaces, @@ -51,7 +51,7 @@ fn build() { LanguageCapability::HierarchyDispatch, LanguageCapability::ExternalReferences, ] { - assert!(evidence.adapter.capabilities.contains(&capability)); + assert!(evidence.pipeline.capabilities.contains(&capability)); } assert!(extraction.nodes.is_empty()); assert!(extraction.edges.is_empty()); diff --git a/crates/compass-languages/tests/rust_universal_phase2.rs b/crates/compass-languages/tests/rust_universal_phase2.rs index 23b2811d..b2a66b90 100644 --- a/crates/compass-languages/tests/rust_universal_phase2.rs +++ b/crates/compass-languages/tests/rust_universal_phase2.rs @@ -168,7 +168,7 @@ fn rust_phase2_emits_direct_test_relationship_candidates() -> Result<(), Box observed.extend(candidate_source_keys(&batch)), + match engine.extract_source_universal_evidence(&path, &source_file, &source) { + Ok(batch) => observed.extend(evidence_source_keys(&batch)), Err(error) => extraction_errors.push(format!("{source_file}: {error}")), } } assert!( extraction_errors.is_empty(), - "candidate extraction failed for {} files: {}", + "universal evidence extraction failed for {} files: {}", extraction_errors.len(), extraction_errors .iter() @@ -239,7 +239,7 @@ fn source_key(construct: &OracleConstruct) -> SourceKey { ) } -fn candidate_source_keys(batch: &SemanticEvidenceBatch) -> BTreeSet { +fn evidence_source_keys(batch: &SemanticEvidenceBatch) -> BTreeSet { let mut keys = BTreeSet::new(); for declaration in &batch.declarations { keys.insert(( @@ -261,7 +261,7 @@ fn candidate_source_keys(batch: &SemanticEvidenceBatch) -> BTreeSet { else { continue; }; - let Some((relation, capability)) = candidate_key_names(candidate, occurrence) else { + let Some((relation, capability)) = evidence_key_names(candidate, occurrence) else { continue; }; keys.insert(( @@ -275,7 +275,7 @@ fn candidate_source_keys(batch: &SemanticEvidenceBatch) -> BTreeSet { keys } -fn candidate_key_names( +fn evidence_key_names( candidate: &compass_languages::RelationshipCandidate, occurrence: &compass_languages::OccurrenceFact, ) -> Option<(&'static str, &'static str)> { diff --git a/crates/compass-languages/tests/typescript_inherited_members.rs b/crates/compass-languages/tests/typescript_inherited_members.rs index 51fcb50e..28c51a44 100644 --- a/crates/compass-languages/tests/typescript_inherited_members.rs +++ b/crates/compass-languages/tests/typescript_inherited_members.rs @@ -16,12 +16,12 @@ class Child extends Base { } "#; let batch = Engine::default() - .extract_source_universal_candidate_evidence( + .extract_source_universal_evidence( Path::new("src/inherited.ts"), "src/inherited.ts", source, ) - .expect("candidate evidence"); + .expect("universal evidence"); let declaration = batch .declarations .iter() diff --git a/crates/compass-languages/tests/typescript_oracle_differential.rs b/crates/compass-languages/tests/typescript_oracle_differential.rs index 34842161..d0b822ae 100644 --- a/crates/compass-languages/tests/typescript_oracle_differential.rs +++ b/crates/compass-languages/tests/typescript_oracle_differential.rs @@ -1,5 +1,5 @@ //! Developer-only differential qualification for the TypeScript/JavaScript -//! universal candidate. +//! universal evidence pipeline. //! //! The test is deliberately ignored in the normal Rust suite: the production //! boundary must not require Node.js or the TypeScript compiler. Run it with @@ -37,7 +37,7 @@ struct OracleConstruct { #[test] #[ignore = "developer-only TypeScript compiler differential"] -fn pinned_compiler_constructs_have_candidate_source_coverage() { +fn pinned_compiler_constructs_have_universal_evidence_source_coverage() { let root = tempdir().expect("temporary differential root"); let main = root.path().join("src/main.tsx"); let ui = root.path().join("src/ui.ts"); @@ -77,9 +77,9 @@ fn pinned_compiler_constructs_have_candidate_source_coverage() { let mut engine = Engine::default(); let source = fs::read(&main).expect("main source"); let batch = engine - .extract_source_universal_candidate_evidence(&main, "src/main.tsx", &source) - .expect("candidate evidence"); - let observed = candidate_source_keys(&batch); + .extract_source_universal_evidence(&main, "src/main.tsx", &source) + .expect("universal evidence"); + let observed = evidence_source_keys(&batch); let expected = oracle .constructs .iter() @@ -144,7 +144,7 @@ fn supported_construct(construct: &OracleConstruct) -> bool { ) } -fn candidate_source_keys(batch: &SemanticEvidenceBatch) -> BTreeSet<(String, String, u64, u64)> { +fn evidence_source_keys(batch: &SemanticEvidenceBatch) -> BTreeSet<(String, String, u64, u64)> { let mut keys = BTreeSet::new(); for declaration in &batch.declarations { keys.insert(( diff --git a/crates/compass-languages/tests/typescript_target_differential.rs b/crates/compass-languages/tests/typescript_target_differential.rs index 50cc3a50..ab244a19 100644 --- a/crates/compass-languages/tests/typescript_target_differential.rs +++ b/crates/compass-languages/tests/typescript_target_differential.rs @@ -1,5 +1,5 @@ //! Developer-only compiler-checker target adjudication for the -//! TypeScript/JavaScript universal candidate. +//! TypeScript/JavaScript universal evidence pipeline. //! //! The test is intentionally ignored. It compares exact local declaration //! anchors from an independent TypeScript 5.9.3 checker oracle with candidate @@ -115,7 +115,7 @@ struct TargetAdjudicationStratum { struct TargetAdjudicationReport { schema: &'static str, provider: &'static str, - candidate_adapter: &'static str, + producer_id: &'static str, metadata: Option, scanned_files: usize, parsed_files: usize, @@ -135,7 +135,7 @@ struct TargetAdjudicationReport { #[test] #[ignore = "developer-only real TypeScript/JavaScript target adjudication"] -fn checker_oracle_adjudicates_local_candidate_targets() { +fn checker_oracle_adjudicates_local_evidence_targets() { let root = env::var_os("COMPASS_TS_QUALIFICATION_ROOT") .map(PathBuf::from) .expect("set COMPASS_TS_QUALIFICATION_ROOT to a read-only pinned corpus"); @@ -193,14 +193,14 @@ fn checker_oracle_adjudicates_local_candidate_targets() { // files; a fresh engine preserves bounded default-stack runs without // changing production parser/cache behavior. let mut engine = Engine::default(); - match engine.extract_source_universal_candidate_evidence(&path, &source_file, &source) { - Ok(batch) => merge_candidate_targets(&mut observed, &batch), + match engine.extract_source_universal_evidence(&path, &source_file, &source) { + Ok(batch) => merge_evidence_targets(&mut observed, &batch), Err(error) => extraction_errors.push(format!("{source_file}: {error}")), } } assert!( extraction_errors.is_empty(), - "candidate extraction failed: {}", + "universal evidence extraction failed: {}", extraction_errors .iter() .take(20) @@ -415,7 +415,7 @@ fn checker_oracle_adjudicates_local_candidate_targets() { &TargetAdjudicationReport { schema: TARGET_REPORT_SCHEMA, provider: ORACLE_PROVIDER, - candidate_adapter: "compass.ecmascript.candidate", + producer_id: "compass.typescript", metadata: oracle.metadata.clone(), scanned_files: oracle.scanned_files, parsed_files: oracle.parsed_files, @@ -549,7 +549,7 @@ fn is_supported_construct(construct: &OracleConstruct) -> bool { ) } -fn merge_candidate_targets( +fn merge_evidence_targets( observed: &mut BTreeMap>, batch: &SemanticEvidenceBatch, ) { diff --git a/crates/compass-languages/tests/typescript_type_scope.rs b/crates/compass-languages/tests/typescript_type_scope.rs index b30b74b9..5bbb734d 100644 --- a/crates/compass-languages/tests/typescript_type_scope.rs +++ b/crates/compass-languages/tests/typescript_type_scope.rs @@ -12,12 +12,8 @@ type First = { [K in keyof T]: T[K] }; type Second = { [K in keyof T]?: T[K] }; "#; let batch = Engine::default() - .extract_source_universal_candidate_evidence( - Path::new("src/types.ts"), - "src/types.ts", - source, - ) - .expect("candidate evidence"); + .extract_source_universal_evidence(Path::new("src/types.ts"), "src/types.ts", source) + .expect("universal evidence"); for (qualified_suffix, spelling) in [ (".Intersection.I", "I"), @@ -50,12 +46,8 @@ async function Page(props: { params: Promise<{ slug?: string[] }> }) { } "#; let batch = Engine::default() - .extract_source_universal_candidate_evidence( - Path::new("src/page.tsx"), - "src/page.tsx", - source, - ) - .expect("candidate evidence"); + .extract_source_universal_evidence(Path::new("src/page.tsx"), "src/page.tsx", source) + .expect("universal evidence"); let property = batch .declarations .iter() diff --git a/crates/compass-languages/tests/typescript_universal_candidate.rs b/crates/compass-languages/tests/typescript_universal_evidence.rs similarity index 94% rename from crates/compass-languages/tests/typescript_universal_candidate.rs rename to crates/compass-languages/tests/typescript_universal_evidence.rs index 5d3bade1..fda13106 100644 --- a/crates/compass-languages/tests/typescript_universal_candidate.rs +++ b/crates/compass-languages/tests/typescript_universal_evidence.rs @@ -6,14 +6,14 @@ use compass_languages::{ CandidateRelation, Engine, EvidenceLimits, SemanticRole, SymbolNamespace, validate_evidence, }; -fn candidate(path: &str, source: &[u8]) -> compass_languages::SemanticEvidenceBatch { +fn universal_evidence(path: &str, source: &[u8]) -> compass_languages::SemanticEvidenceBatch { Engine::default() - .extract_source_universal_candidate_evidence(Path::new(path), path, source) - .expect("candidate evidence") + .extract_source_universal_evidence(Path::new(path), path, source) + .expect("universal evidence") } #[test] -fn typescript_candidate_emits_direct_declarations_scopes_imports_and_calls() { +fn universal_evidence_emits_direct_declarations_scopes_imports_and_calls() { let source = br#"import type { User as UserType } from "./types"; import Widget, * as widgets from "./widget"; interface User { id: string } @@ -26,11 +26,11 @@ const helper = (value: User): ID => value.id; export { App, helper }; new App(); "#; - let batch = candidate("src/app.ts", source); + let batch = universal_evidence("src/app.ts", source); validate_evidence(&batch, EvidenceLimits::default()).expect("valid evidence"); - assert_eq!(batch.adapter.language, "typescript"); - assert_eq!(batch.adapter.version, 5); - assert_eq!(batch.adapter.dialect.as_deref(), Some("ts")); + assert_eq!(batch.pipeline.language, "typescript"); + assert_eq!(batch.pipeline.version, 5); + assert_eq!(batch.pipeline.dialect.as_deref(), Some("ts")); assert!( batch .declarations @@ -111,7 +111,7 @@ new App(); } #[test] -fn typescript_candidate_emits_using_resource_bindings_and_initializer_calls() { +fn universal_evidence_emits_using_resource_bindings_and_initializer_calls() { let source = br#"interface Resource { close(): void } declare function acquire(): Resource; declare function acquireAsync(): Promise; @@ -122,7 +122,7 @@ export function run() { asyncResource.close(); } "#; - let batch = candidate("src/resources.ts", source); + let batch = universal_evidence("src/resources.ts", source); validate_evidence(&batch, EvidenceLimits::default()).expect("using evidence"); assert!( !batch @@ -165,7 +165,7 @@ export function run() { } #[test] -fn javascript_candidate_emits_jsx_commonjs_and_dynamic_import_evidence() { +fn universal_evidence_emits_jsx_commonjs_and_dynamic_import_evidence() { let source = br#"const Button = () => null; const title = "Hello"; const props = { title }; @@ -174,10 +174,10 @@ const helper = require("./helper"); module.exports = render; export async function load() { return import("./lazy.js"); } "#; - let batch = candidate("src/render.jsx", source); - assert_eq!(batch.adapter.language, "javascript"); - assert_eq!(batch.adapter.version, 5); - assert_eq!(batch.adapter.dialect.as_deref(), Some("jsx")); + let batch = universal_evidence("src/render.jsx", source); + assert_eq!(batch.pipeline.language, "javascript"); + assert_eq!(batch.pipeline.version, 5); + assert_eq!(batch.pipeline.dialect.as_deref(), Some("jsx")); assert!( batch .bindings @@ -226,7 +226,7 @@ export async function load() { return import("./lazy.js"); } candidate.relation == compass_languages::CandidateRelation::Reexports })); - let import_equals = candidate( + let import_equals = universal_evidence( "src/cjs-types.ts", br#"import axios = require("axios"); axios.get("/items"); @@ -247,7 +247,7 @@ axios.get("/items"); .is_some_and(|occurrence| occurrence.context.as_deref() == Some("import_equals")) })); - let javascript_inheritance = candidate( + let javascript_inheritance = universal_evidence( "src/canceled.js", br#"import AxiosError from "./AxiosError.js"; class CanceledError extends AxiosError {} @@ -259,7 +259,7 @@ class CanceledError extends AxiosError {} && candidate.constraints.qualified_name.as_deref() == Some("./AxiosError.js::default") })); - let namespace_jsx = candidate( + let namespace_jsx = universal_evidence( "src/components.tsx", br#"import * as UI from "./ui"; export function render() { return ; } @@ -282,8 +282,8 @@ export function render() { return ; } } #[test] -fn typescript_candidate_keeps_named_imports_used_as_object_values() { - let batch = candidate( +fn universal_evidence_keeps_named_imports_used_as_object_values() { + let batch = universal_evidence( "src/routes.tsx", br#"import { UserPage } from "./UserPage"; export const routes = [{ path: "/users", Component: UserPage }]; @@ -308,7 +308,7 @@ export const routes = [{ path: "/users", Component: UserPage }]; } #[test] -fn typescript_candidate_emits_jsx_value_and_spread_references() { +fn universal_evidence_emits_jsx_value_and_spread_references() { let source = br#"interface Props { title: string; onClick: () => void } const title = "Hello"; const props = { title }; @@ -317,7 +317,7 @@ export function View(label: string) { return ; } "#; - let batch = candidate("src/view.tsx", source); + let batch = universal_evidence("src/view.tsx", source); validate_evidence(&batch, EvidenceLimits::default()).expect("JSX value evidence"); let declaration_id = |name: &str, kind: &str| { @@ -390,7 +390,7 @@ export function View(label: string) { } #[test] -fn typescript_candidate_emits_call_and_member_decorator_evidence() { +fn universal_evidence_emits_call_and_member_decorator_evidence() { let source = br#"import { Injectable, Controller as C } from "@nestjs/common"; import * as decorators from "./decorators"; function Local(value: string) {} @@ -402,7 +402,7 @@ class Service {} @unknownFactory(makePath()) class Dynamic {} "#; - let batch = candidate("src/service.ts", source); + let batch = universal_evidence("src/service.ts", source); validate_evidence(&batch, EvidenceLimits::default()).expect("decorator evidence"); let decorators = batch .candidates @@ -465,8 +465,8 @@ class Dynamic {} } #[test] -fn typescript_candidate_keeps_recovered_computed_decorator_unresolved() { - let batch = candidate( +fn universal_evidence_keeps_recovered_computed_decorator_unresolved() { + let batch = universal_evidence( "src/recovered-decorator.ts", br#"@decorators[factoryKey]() class Computed {} @@ -499,8 +499,8 @@ class Computed {} } #[test] -fn javascript_candidate_publishes_spread_free_default_object_members() { - let batch = candidate( +fn universal_evidence_publishes_spread_free_default_object_members() { + let batch = universal_evidence( "src/utils.js", br#"const isNumber = value => typeof value === 'number'; const isString = value => typeof value === 'string'; @@ -526,7 +526,7 @@ export default { isNumber, isString }; && binding.target_declaration_id.as_deref() == Some(default_object.id.as_str()) })); - let spread = candidate( + let spread = universal_evidence( "src/spread-default.js", br#"const base = { isNumber: value => true }; export default { ...base, isString: value => true }; @@ -575,7 +575,7 @@ export default { ...base, isString: value => true }; }) })); - let imported = candidate( + let imported = universal_evidence( "src/imported-spread-default.js", br#"import base from './base.js'; export default { ...base, isString: value => true }; @@ -590,7 +590,7 @@ export default { ...base, isString: value => true }; && binding.target_declaration_id.is_none() })); - let unknown = candidate( + let unknown = universal_evidence( "src/unknown-spread-default.js", br#"const base = getBase(); export default { ...base, isString: value => true }; @@ -608,8 +608,8 @@ export default { ...base, isString: value => true }; } #[test] -fn typescript_candidate_publishes_wildcard_barrel_reexports() { - let batch = candidate( +fn universal_evidence_publishes_wildcard_barrel_reexports() { + let batch = universal_evidence( "src/index.ts", br#"export * from "./values"; export * as values from "./values"; @@ -685,8 +685,8 @@ export type * from "./types"; } #[test] -fn typescript_candidate_publishes_export_assignment_default() { - let batch = candidate( +fn universal_evidence_publishes_export_assignment_default() { + let batch = universal_evidence( "src/api.ts", br#"export function run() {} export = run; @@ -716,7 +716,7 @@ export = run; && candidate.target_spelling == "default" })); - let object = candidate( + let object = universal_evidence( "src/object-api.ts", br#"export = { run: (value: number) => value }; "#, @@ -746,7 +746,7 @@ api.run(); execute(); method(); "#; - let batch = candidate("src/consumer.js", source); + let batch = universal_evidence("src/consumer.js", source); validate_evidence(&batch, EvidenceLimits::default()).expect("valid require evidence"); let api = batch @@ -792,7 +792,7 @@ module.exports = { literal: true, }; "#; - let batch = candidate("src/object-export.js", source); + let batch = universal_evidence("src/object-export.js", source); validate_evidence(&batch, EvidenceLimits::default()).expect("CommonJS object evidence"); let reexports = batch @@ -842,7 +842,7 @@ module.exports = { Some(method.id.as_str()) ); - let spread = candidate( + let spread = universal_evidence( "src/object-export-spread.js", br#"const other = getOther(); module.exports = { run, ...other }; @@ -860,7 +860,7 @@ module.exports = { run, ...other }; } #[test] -fn typescript_candidate_preserves_typeof_import_queries_without_dynamic_calls() { +fn universal_evidence_preserves_typeof_import_queries_without_dynamic_calls() { let source = br#"const load = () => import("./runtime"); // typeof import("./ignored"); const text = "typeof import('./ignored-string')"; @@ -868,7 +868,7 @@ type Item = (typeof import("./items").items)[number]; type Simple = typeof import("./simple").items; type Plain = import("./plain").Item; "#; - let batch = candidate("src/import-type.ts", source); + let batch = universal_evidence("src/import-type.ts", source); validate_evidence(&batch, EvidenceLimits::default()).expect("valid evidence"); let type_imports = batch @@ -920,7 +920,7 @@ type Plain = import("./plain").Item; #[test] fn javascript_typeof_import_remains_a_dynamic_import() { - let batch = candidate( + let batch = universal_evidence( "src/runtime-probe.js", br#"const probe = typeof import("./runtime"); "#, @@ -952,7 +952,7 @@ fn typescript_import_type_query_inventory_is_bounded_and_diagnosed() { "type Item{index} = typeof import(\"./module{index}\").Item;\n" )); } - let batch = candidate("src/import-type-limit.ts", source.as_bytes()); + let batch = universal_evidence("src/import-type-limit.ts", source.as_bytes()); validate_evidence(&batch, EvidenceLimits::default()).expect("valid bounded evidence"); assert!( batch @@ -975,7 +975,7 @@ fn javascript_commonjs_object_spread_publishes_bounded_owner_aliases() { let source = br#"const base = { inherited() {} }; module.exports = { ...base, direct() {} }; "#; - let batch = candidate("src/object-export-spread-safe.js", source); + let batch = universal_evidence("src/object-export-spread-safe.js", source); validate_evidence(&batch, EvidenceLimits::default()).expect("CommonJS spread evidence"); let alias = batch @@ -1011,7 +1011,7 @@ fn javascript_namespace_and_require_spreads_publish_module_owner_aliases() { const cjs = require("./cjs"); module.exports = { ...esm, ...cjs }; "#; - let batch = candidate("src/namespace-spread.cjs", source); + let batch = universal_evidence("src/namespace-spread.cjs", source); validate_evidence(&batch, EvidenceLimits::default()).expect("namespace spread evidence"); let aliases = batch @@ -1033,7 +1033,7 @@ fn javascript_commonjs_object_assign_publishes_bounded_owner_aliases() { let source = br#"const base = { inherited() {} }; module.exports = Object.assign({}, base, { direct() {} }); "#; - let batch = candidate("src/object-assign-export.js", source); + let batch = universal_evidence("src/object-assign-export.js", source); validate_evidence(&batch, EvidenceLimits::default()).expect("Object.assign evidence"); assert!(batch.bindings.iter().any(|binding| { @@ -1048,7 +1048,7 @@ module.exports = Object.assign({}, base, { direct() {} }); binding.kind == compass_languages::BindingKind::Reexport && binding.spelling == "direct" })); - let mutation = candidate( + let mutation = universal_evidence( "src/object-assign-mutation.js", br#"const base = { inherited() {} }; Object.assign(exports, base); @@ -1062,7 +1062,7 @@ Object.assign(exports, base); && binding.qualified_target == "object-assign-mutation.base" })); - let unknown = candidate( + let unknown = universal_evidence( "src/object-assign-unknown.js", br#"const base = getBase(); module.exports = Object.assign({}, base, { direct() {} }); @@ -1088,7 +1088,7 @@ Object.defineProperty(exports, 'imported', { Object.defineProperty(exports, '__esModule', { value: true }); Object.defineProperty(exports, dynamic, { value: run }); "#; - let batch = candidate("src/define-property.cjs", source); + let batch = universal_evidence("src/define-property.cjs", source); validate_evidence(&batch, EvidenceLimits::default()).expect("defineProperty export evidence"); let reexports = batch @@ -1102,7 +1102,7 @@ Object.defineProperty(exports, dynamic, { value: run }); assert!(!reexports.contains("__esModule")); assert!(!reexports.contains("dynamic")); - let shadowed = candidate( + let shadowed = universal_evidence( "src/define-property-shadowed.cjs", br#"function Object() {} function run() {} @@ -1113,7 +1113,7 @@ Object.defineProperty(exports, 'run', { value: run }); binding.kind == compass_languages::BindingKind::Reexport && binding.spelling == "run" })); - let dynamic = candidate( + let dynamic = universal_evidence( "src/define-property-dynamic.cjs", br#"function run() {} Object.defineProperty(exports, 'run', { value: getRun() }); @@ -1146,7 +1146,7 @@ __exportStar(require("./wrong-target"), other); __exportStar(require("./lookalike"), exports); } "#; - let batch = candidate("src/barrel.cjs", source); + let batch = universal_evidence("src/barrel.cjs", source); validate_evidence(&batch, EvidenceLimits::default()).expect("exportStar evidence"); let aliases = batch .bindings @@ -1170,7 +1170,7 @@ tag`hello ${42}`; tag`hello ${"x"} ${'y'}`; getTag()`dynamic ${name}`; "#; - let batch = candidate("src/tagged.ts", source); + let batch = universal_evidence("src/tagged.ts", source); validate_evidence(&batch, EvidenceLimits::default()).expect("tagged template evidence"); let tag = batch .declarations @@ -1243,7 +1243,7 @@ getTag()`dynamic ${name}`; #[test] fn javascript_static_this_factory_tracks_new_instance_members() { - let batch = candidate( + let batch = universal_evidence( "src/factory.js", br#"class Factory { static create() { @@ -1283,7 +1283,7 @@ Factory.create(); #[test] fn javascript_function_constructor_prototypes_resolve_instance_members() { - let batch = candidate( + let batch = universal_evidence( "src/prototype.js", br#"function Legacy(value) { this.value = value; } Legacy.prototype.helper = function helper() { return this.value; }; @@ -1386,7 +1386,7 @@ unknown.prototype.run = function() {}; #[test] fn typescript_source_compatible_object_arguments_keep_exact_call_targets() { - let batch = candidate( + let batch = universal_evidence( "src/calls.ts", br#"type SourceLine = { text: string }; interface Options { enabled: boolean } @@ -1414,7 +1414,7 @@ select(options); #[test] fn typescript_constrained_generic_calls_keep_exact_targets() { - let batch = candidate( + let batch = universal_evidence( "src/generics.ts", br#"const arrayToEnum = (items: U) => items[0]; arrayToEnum(["one", "two"]); @@ -1435,7 +1435,7 @@ arrayToEnum(["one", "two"]); #[test] fn typescript_constrained_generic_member_chains_resolve_exact_targets() { - let batch = candidate( + let batch = universal_evidence( "src/generic_members.ts", br#"interface Shape { name: string } class Box { @@ -1459,7 +1459,7 @@ class Box { #[test] fn typescript_unconstrained_generic_member_chains_fail_closed() { - let batch = candidate( + let batch = universal_evidence( "src/generic_unconstrained.ts", br#"function read(value: T) { return value.name; @@ -1475,7 +1475,7 @@ fn typescript_unconstrained_generic_member_chains_fail_closed() { #[test] fn typescript_callable_property_members_accept_rest_calls() { - let batch = candidate( + let batch = universal_evidence( "src/callable_property.ts", br#"class Mocker { pick = (...args: any[]): any => args[0]; @@ -1498,7 +1498,7 @@ fn typescript_callable_property_members_accept_rest_calls() { #[test] fn typescript_contextual_callback_member_types_resolve_exact_targets() { - let batch = candidate( + let batch = universal_evidence( "src/contextual_callback.ts", br#"interface Common { issues: string[] } type Callback = { run: (value: string, ctx: Common) => void }; @@ -1532,7 +1532,7 @@ use((value, ctx) => ctx.issues); #[test] fn typescript_literal_indexed_type_aliases_resolve_nominal_receivers() { - let batch = candidate( + let batch = universal_evidence( "src/indexed-alias.ts", br#"interface Nested { run(): void } interface Item { nested: Nested; inspect(): void } @@ -1566,7 +1566,7 @@ function dynamic(value: Item, key: T) { #[test] fn typescript_indexed_type_alias_ambiguity_does_not_choose_a_union_member() { - let batch = candidate( + let batch = universal_evidence( "src/indexed-ambiguous.ts", br#"interface First { run(): void } interface Second { run(): void } @@ -1599,7 +1599,7 @@ function use(value: Maybe) { #[test] fn typescript_member_call_return_types_resolve_chained_members() { - let batch = candidate( + let batch = universal_evidence( "src/member_return.ts", br#"class Maybe { optional() { return this; } @@ -1637,7 +1637,7 @@ class Box { #[test] fn typescript_member_call_return_types_resolve_inherited_methods() { - let batch = candidate( + let batch = universal_evidence( "src/member_return_inherited.ts", br#"class Base { optional() { return this; } } class Child extends Base {} @@ -1662,7 +1662,7 @@ class Box { #[test] fn typescript_inherited_generic_member_types_resolve_exact_members() { - let batch = candidate( + let batch = universal_evidence( "src/inherited_generic.ts", br#"interface Def { flag: boolean } class Base { value!: T; } @@ -1684,7 +1684,7 @@ class Child extends Base { read() { const value = this.value; return value. #[test] fn typescript_interface_extends_members_resolve_exact_targets() { - let batch = candidate( + let batch = universal_evidence( "src/interface-extends.ts", br#"type Callback = (issue: string) => string; interface BaseDef { errorMap?: Callback | undefined } @@ -1709,7 +1709,7 @@ function read(def: ObjectDef) { #[test] fn typescript_generic_instantiated_member_chains_resolve_constraint_targets() { - let batch = candidate( + let batch = universal_evidence( "src/generic-member-chain.ts", br#"class Schema { _parseAsync() {} } interface Definition { left: T } @@ -1734,7 +1734,7 @@ class Intersection { #[test] fn typescript_callable_property_return_types_resolve_generic_member_chains() { - let batch = candidate( + let batch = universal_evidence( "src/callable-property-return.ts", br#"class Schema { _parse() {} } interface Definition { getter: () => T } @@ -1759,7 +1759,7 @@ class Lazy { #[test] fn typescript_static_callable_aliases_resolve_exact_property_targets() { - let batch = candidate( + let batch = universal_evidence( "src/static-callable-alias.ts", br#"function createSchema(value: string) { return value; } class SchemaFactory { @@ -1787,7 +1787,7 @@ class SchemaFactory { #[test] fn typescript_variable_factory_aliases_preserve_declared_return_receivers() { - let batch = candidate( + let batch = universal_evidence( "src/variable-factory-alias.ts", br#"class Schema { optional() {} @@ -1812,7 +1812,7 @@ function run() { create().optional(); } #[test] fn typescript_type_assertion_receivers_preserve_exact_generic_members() { - let batch = candidate( + let batch = universal_evidence( "src/type-assertion-receiver.ts", br#"class Schema { parseAsync() {} } interface Definition { value: T } @@ -1837,7 +1837,7 @@ class Holder { #[test] fn typescript_destructured_inline_object_types_resolve_exact_members() { - let batch = candidate( + let batch = universal_evidence( "src/featured.tsx", br#"type FeatureData = { name: string; link: string; lightImage: string; darkImage: string }; function Featured(props: { data: FeatureData }) { @@ -1868,7 +1868,7 @@ function Featured(props: { data: FeatureData }) { #[test] fn typescript_generic_member_call_returns_preserve_inherited_members() { - let batch = candidate( + let batch = universal_evidence( "src/generic-member-return.ts", br#"class Base { optional() {} } class Nullable extends Base {} @@ -1893,7 +1893,7 @@ class Schema { #[test] fn typescript_nominal_parameter_annotations_resolve_exact_members() { - let batch = candidate( + let batch = universal_evidence( "src/context.ts", br#"interface Common { issues: string[]; contextualErrorMap?: string } interface ParseContext { common: Common; path: string[]; data: unknown } @@ -1945,7 +1945,7 @@ function report(ctx: ParseContext) { #[test] fn typescript_discriminated_union_guards_resolve_exact_branch_members() { - let batch = candidate( + let batch = universal_evidence( "src/discriminated.ts", br#"type Success = { success: true; data: string }; type Failure = { success: false; error: Error }; @@ -1971,7 +1971,7 @@ function read(result: Result) { #[test] fn typescript_in_guards_resolve_the_unique_union_member_owner() { - let batch = candidate( + let batch = universal_evidence( "src/in-guard.ts", br#"type Ready = { run(): void }; type Pending = { wait(): void }; @@ -2006,7 +2006,7 @@ function use(state: State) { #[test] fn typescript_string_discriminant_guards_resolve_exact_branch_members() { - let batch = candidate( + let batch = universal_evidence( "src/string-discriminated.ts", br#"class ReadySchema { parse() {} } class OtherSchema { other() {} } @@ -2039,7 +2039,7 @@ function read(state: State) { #[test] fn typescript_string_discriminant_guards_resolve_callable_union_members() { - let batch = candidate( + let batch = universal_evidence( "src/callable-discriminated.ts", br#"type Transform = { kind: "transform"; transform: (value: string) => void }; type Refinement = { kind: "refinement"; refinement: (value: string) => void }; @@ -2070,7 +2070,7 @@ function run(effect: Effect) { #[test] fn typescript_string_discriminant_guards_follow_nullable_member_values() { - let batch = candidate( + let batch = universal_evidence( "src/nullable-callable-discriminated.ts", br#"type Transform = { kind: "transform"; transform: (value: string) => void }; type Refinement = { kind: "refinement"; refinement: (value: string) => void }; @@ -2100,7 +2100,7 @@ class Holder { #[test] fn typescript_union_inline_object_parameters_resolve_optional_members() { - let batch = candidate( + let batch = universal_evidence( "src/inline-union.ts", br#"class Schema { datetime(options?: string | { precision?: number | null; offset?: boolean }) { @@ -2126,7 +2126,7 @@ fn typescript_union_inline_object_parameters_resolve_optional_members() { #[test] fn typescript_implements_arguments_match_source_declared_interfaces() { - let batch = candidate( + let batch = universal_evidence( "src/implements-assignability.ts", br#"interface Input { value: string } class Lazy implements Input { value = "ok"; } @@ -2150,7 +2150,7 @@ consumer.run(lazy); #[test] fn typescript_index_signature_values_resolve_nominal_member_calls() { - let batch = candidate( + let batch = universal_evidence( "src/index-signature.ts", br#"class Schema { run() {} } interface Shape { [key: string]: Schema } @@ -2174,7 +2174,7 @@ function use(shape: Shape, key: string) { #[test] fn typescript_flow_sensitive_reassignment_selects_latest_nominal_receiver() { - let batch = candidate( + let batch = universal_evidence( "src/reassignment.ts", br#"class First { run() {} } class Second { run() {} } @@ -2215,7 +2215,7 @@ current.run(); #[test] fn typescript_flow_sensitive_branch_assignment_fails_closed() { - let batch = candidate( + let batch = universal_evidence( "src/branch-reassignment.ts", br#"class First { run() {} } class Second { run() {} } @@ -2235,7 +2235,7 @@ current.run(); #[test] fn typescript_flow_sensitive_unknown_assignment_blocks_stale_receiver() { - let batch = candidate( + let batch = universal_evidence( "src/unknown-reassignment.ts", br#"class First { run() {} } let current = new First(); @@ -2252,7 +2252,7 @@ current.run(); #[test] fn javascript_flow_sensitive_reassignment_selects_latest_nominal_receiver() { - let batch = candidate( + let batch = universal_evidence( "src/reassignment.js", br#"class First { run() {} } class Second { run() {} } @@ -2276,7 +2276,7 @@ current.run(); #[test] fn javascript_flow_sensitive_var_reassignment_inside_function_selects_latest_receiver() { - let batch = candidate( + let batch = universal_evidence( "src/var-reassignment.js", br#"class First { run() {} } class Second { run() {} } @@ -2302,7 +2302,7 @@ function use() { #[test] fn typescript_flow_sensitive_reassignment_is_ordered_by_use_site() { - let batch = candidate( + let batch = universal_evidence( "src/ordered-reassignment.ts", br#"class First { run() {} } class Second { run() {} } @@ -2354,7 +2354,7 @@ current.run(); #[test] fn typescript_flow_sensitive_compound_assignment_blocks_stale_receiver() { - let batch = candidate( + let batch = universal_evidence( "src/compound-reassignment.ts", br#"class First { run() {} } let current = new First(); @@ -2371,7 +2371,7 @@ current.run(); #[test] fn typescript_flow_sensitive_typed_call_assignment_uses_return_receiver() { - let batch = candidate( + let batch = universal_evidence( "src/call-reassignment.ts", br#"class First { run() {} } class Second { run() {} } @@ -2396,7 +2396,7 @@ current.run(); #[test] fn typescript_flow_sensitive_local_alias_preserves_source_receiver() { - let batch = candidate( + let batch = universal_evidence( "src/local-alias.ts", br#"class First { run() {} } let current = new First(); @@ -2484,7 +2484,7 @@ current.run(); ), ]; for (path, source) in cases { - let batch = candidate(path, source); + let batch = universal_evidence(path, source); assert!( !batch.candidates.iter().any(|candidate| { candidate.relation == compass_languages::CandidateRelation::Calls @@ -2498,7 +2498,7 @@ current.run(); #[test] fn javascript_const_this_alias_survives_a_closure_capture() { - let batch = candidate( + let batch = universal_evidence( "src/const-this-alias.js", br#"class CancelToken { constructor() { @@ -2531,7 +2531,7 @@ new CancelToken(); #[test] fn javascript_const_structural_alias_uses_property_scoped_mutation_barriers() { - let batch = candidate( + let batch = universal_evidence( "src/const-structural-closure.js", br#"const config = { inspect() {} @@ -2553,7 +2553,7 @@ later(); == Some(inspect.id.as_str()) })); - let overwritten = candidate( + let overwritten = universal_evidence( "src/const-structural-overwrite.js", br#"const config = { inspect() {} }; config.inspect = replacement; @@ -2566,7 +2566,7 @@ config.inspect(); && candidate.constraints.exact_target_declaration_id.is_some() })); - let spread = candidate( + let spread = universal_evidence( "src/const-structural-spread.js", br#"const base = { inspect() {} }; const config = { ...base }; @@ -2580,7 +2580,7 @@ later(); && candidate.constraints.exact_target_declaration_id.is_some() })); - let proven_spread = candidate( + let proven_spread = universal_evidence( "src/const-structural-proven-spread.js", br#"const base = { inspect() {} }; const config = { ...base }; @@ -2601,7 +2601,7 @@ config.inspect(); == Some(base_inspect.id.as_str()) })); - let overridden = candidate( + let overridden = universal_evidence( "src/const-structural-spread-order.js", br#"const base = { inspect() {} }; const config = { inspect() { return 'local'; }, ...base }; @@ -2635,7 +2635,7 @@ config.inspect(); == Some(config_target.id.as_str()) })); - let unknown = candidate( + let unknown = universal_evidence( "src/const-structural-unknown-spread.js", br#"const base = getBase(); const config = { ...base }; @@ -2648,7 +2648,7 @@ config.inspect(); && candidate.constraints.exact_target_declaration_id.is_some() })); - let ambiguous = candidate( + let ambiguous = universal_evidence( "src/const-structural-ambiguous-spread.js", br#"const base = { inspect() {}, inspect(value) {} }; const config = { ...base }; @@ -2661,7 +2661,7 @@ config.inspect(); && candidate.constraints.exact_target_declaration_id.is_some() })); - let inline = candidate( + let inline = universal_evidence( "src/inline-structural-closure.js", br#"const key = Symbol('state'); class Service { @@ -2692,7 +2692,7 @@ new Service(); == Some(inline_inspect.id.as_str()) })); - let inline_overwritten = candidate( + let inline_overwritten = universal_evidence( "src/inline-structural-overwrite.js", br#"const key = Symbol('state'); class Service { @@ -2711,7 +2711,7 @@ new Service(); && candidate.constraints.exact_target_declaration_id.is_some() })); - let separate = candidate( + let separate = universal_evidence( "src/inline-structural-separate.js", br#"const firstKey = Symbol('first'); const secondKey = Symbol('second'); @@ -2760,7 +2760,7 @@ new Service(); == Some(second_inspect.id.as_str()) })); - let chained = candidate( + let chained = universal_evidence( "src/inline-structural-chained.js", br#"const key = Symbol('state'); class Service { @@ -2791,7 +2791,7 @@ new Service(); == Some(chained_inspect.id.as_str()) })); - let chained_compound = candidate( + let chained_compound = universal_evidence( "src/inline-structural-chained-compound.js", br#"const key = Symbol('state'); class Service { @@ -2821,7 +2821,7 @@ fn javascript_nominal_member_writes_keep_exact_source_targets() { const service = new Service(); service.kind = 'updated'; "#; - let batch = candidate("src/nominal-member-write.js", source); + let batch = universal_evidence("src/nominal-member-write.js", source); let write_start = source .windows(b"service.kind".len()) .rposition(|window| window == b"service.kind") @@ -2856,7 +2856,7 @@ service.kind = 'updated'; const service = new Service(); service.kind += 1; "#; - let compound = candidate("src/nominal-member-compound.js", compound_source); + let compound = universal_evidence("src/nominal-member-compound.js", compound_source); let compound_start = compound_source .windows(b"service.kind".len()) .rposition(|window| window == b"service.kind") @@ -2890,7 +2890,7 @@ service.kind += 1; } Service.from(); "#; - let static_batch = candidate("src/nominal-static-write.js", static_source); + let static_batch = universal_evidence("src/nominal-static-write.js", static_source); let static_start = static_source .windows(b"service.kind".len()) .rposition(|window| window == b"service.kind") @@ -2924,7 +2924,7 @@ const service = new Service(); consume(service); service.kind; "#; - let escaped = candidate("src/nominal-escape-read.js", escaped_source); + let escaped = universal_evidence("src/nominal-escape-read.js", escaped_source); let escaped_start = escaped_source .windows(b"service.kind".len()) .rposition(|window| window == b"service.kind") @@ -2958,7 +2958,7 @@ const service = new Service(); consume(service); service.nested.kind = 'unknown'; "#; - let nested = candidate("src/nominal-nested-escape-write.js", nested_source); + let nested = universal_evidence("src/nominal-nested-escape-write.js", nested_source); let nested_start = nested_source .windows(b"service.nested.kind".len()) .rposition(|window| window == b"service.nested.kind") @@ -2983,7 +2983,7 @@ service.nested.kind = 'unknown'; #[test] fn typescript_homomorphic_mapped_alias_preserves_nominal_member_targets() { - let batch = candidate( + let batch = universal_evidence( "src/mapped-alias.ts", br#"interface Item { inspect(): void } type Copy = { [K in keyof Item]: Item[K] }; @@ -3010,7 +3010,7 @@ function use(value: Copy) { value.inspect(); } #[test] fn typescript_generic_homomorphic_mapped_alias_substitutes_nominal_target() { - let batch = candidate( + let batch = universal_evidence( "src/generic-mapped-alias.ts", br#"interface Item { inspect(): void } type Copy = { [K in keyof T]: T[K] }; @@ -3037,7 +3037,7 @@ function use(value: Copy) { value.inspect(); } #[test] fn typescript_generic_literal_indexed_alias_substitutes_nominal_target() { - let batch = candidate( + let batch = universal_evidence( "src/generic-indexed-alias.ts", br#"interface Nested { run(): void } interface Item { nested: Nested } @@ -3059,7 +3059,7 @@ function use(value: NestedOf) { value.run(); } #[test] fn typescript_keyof_identity_projection_preserves_nominal_members() { - let batch = candidate( + let batch = universal_evidence( "src/keyof-identity.ts", br#"interface Item { inspect(): void } type Copy = Pick; @@ -3093,7 +3093,7 @@ function rejected(value: Empty) { value.inspect(); } #[test] fn typescript_keyof_projection_with_competing_base_fails_closed() { - let batch = candidate( + let batch = universal_evidence( "src/keyof-ambiguous.ts", br#"interface First { inspect(): void } interface Second { inspect(): void } @@ -3111,7 +3111,7 @@ function use(value: Picked) { value.inspect(); } #[test] fn typescript_non_homomorphic_mapped_alias_fails_closed() { - let batch = candidate( + let batch = universal_evidence( "src/non-homomorphic-mapped-alias.ts", br#"interface Item { inspect(): void } type Getters = { [K in keyof T as `get${Capitalize}`]: () => T[K] }; @@ -3132,7 +3132,7 @@ function use(value: Getters) { value.inspect(); } #[test] fn typescript_nested_mapped_member_does_not_promote_outer_alias() { - let batch = candidate( + let batch = universal_evidence( "src/nested-mapped-alias.ts", br#"interface Item { inspect(): void } type Nested = { nested: { [K in keyof Item]: Item[K] } }; @@ -3148,7 +3148,7 @@ function use(value: Nested) { value.inspect(); } #[test] fn typescript_array_index_receiver_preserves_nominal_element_members() { - let batch = candidate( + let batch = universal_evidence( "src/array-index.ts", br#"interface Item { inspect(): void } function use(values: Item[]) { values[0].inspect(); } @@ -3169,7 +3169,7 @@ function use(values: Item[]) { values[0].inspect(); } #[test] fn typescript_tuple_index_receiver_preserves_nominal_element_members() { - let batch = candidate( + let batch = universal_evidence( "src/tuple-index.ts", br#"interface Item { inspect(): void } type Pair = [Item, string]; @@ -3191,7 +3191,7 @@ function use(pair: Pair) { pair[0].inspect(); } #[test] fn typescript_generic_array_member_chain_substitutes_element_receiver() { - let batch = candidate( + let batch = universal_evidence( "src/generic-array-index.ts", br#"interface Item { inspect(): void } interface Box { values: T[] } @@ -3213,7 +3213,7 @@ function use(box: Box) { box.values[0].inspect(); } #[test] fn typescript_standard_array_container_preserves_nominal_element_members() { - let batch = candidate( + let batch = universal_evidence( "src/readonly-array-index.ts", br#"interface Item { inspect(): void } function use(values: ReadonlyArray) { values[0].inspect(); } @@ -3234,7 +3234,7 @@ function use(values: ReadonlyArray) { values[0].inspect(); } #[test] fn typescript_dynamic_tuple_index_fails_closed() { - let batch = candidate( + let batch = universal_evidence( "src/dynamic-tuple-index.ts", br#"interface Item { inspect(): void } function use(pair: [Item, string], index: number) { pair[index].inspect(); } @@ -3249,7 +3249,7 @@ function use(pair: [Item, string], index: number) { pair[index].inspect(); } #[test] fn typescript_generic_tuple_member_chain_substitutes_element_receiver() { - let batch = candidate( + let batch = universal_evidence( "src/generic-tuple-index.ts", br#"interface Item { inspect(): void } interface Box { pair: [T, string] } @@ -3271,7 +3271,7 @@ function use(box: Box) { box.pair[0].inspect(); } #[test] fn typescript_generic_function_return_substitutes_nominal_argument() { - let batch = candidate( + let batch = universal_evidence( "src/generic-return.ts", br#"class Item { inspect(): void {} } function identity(value: T): T { return value; } @@ -3304,7 +3304,7 @@ function use() { #[test] fn typescript_imported_callable_return_publishes_bounded_marker() { - let batch = candidate( + let batch = universal_evidence( "src/imported-call.ts", br#"import { make } from "./factory"; class Item { inspect(): void {} } @@ -3350,7 +3350,7 @@ function use(value: Item) { make(value).inspect(); } #[test] fn typescript_imported_callable_return_preserves_explicit_generic_marker() { - let batch = candidate( + let batch = universal_evidence( "src/imported-explicit-call.ts", br#"import { identity } from "./factory"; class Item { inspect(): void {} } @@ -3375,7 +3375,7 @@ function use(value: Item) { identity(value).inspect(); } #[test] fn typescript_imported_callable_properties_publish_bounded_markers() { - let api_batch = candidate( + let api_batch = universal_evidence( "src/api.ts", br#"import type { Item } from "./item"; interface TypedApi { make: (value: Item) => Item } @@ -3398,7 +3398,7 @@ export const api = { .find(|declaration| declaration.qualified_name.ends_with(".typed")) .expect("typed object declaration"); assert_eq!(typed.signature.as_deref(), Some("|type:TypedApi")); - let batch = candidate( + let batch = universal_evidence( "src/imported-properties.ts", br#"import { api, typed } from "./api"; import type { Item } from "./item"; @@ -3436,7 +3436,7 @@ export function use(value: Item) { #[test] fn typescript_generic_function_array_return_preserves_element_receiver() { - let batch = candidate( + let batch = universal_evidence( "src/generic-return-array.ts", br#"class Item { inspect(): void {} } function collect(value: T): T[] { return [value]; } @@ -3458,7 +3458,7 @@ function use() { collect(new Item())[0].inspect(); } #[test] fn typescript_generic_function_nominal_container_return_preserves_member_receiver() { - let batch = candidate( + let batch = universal_evidence( "src/generic-return-container.ts", br#"class Item { inspect(): void {} } interface Box { value: T } @@ -3481,7 +3481,7 @@ function use() { box(new Item()).value.inspect(); } #[test] fn typescript_generic_function_return_fails_closed_without_inference() { - let batch = candidate( + let batch = universal_evidence( "src/generic-return-unknown.ts", br#"declare function identity(): T; function use() { identity().inspect(); } @@ -3496,7 +3496,7 @@ function use() { identity().inspect(); } #[test] fn typescript_generic_function_return_fails_closed_on_conflicting_inference() { - let batch = candidate( + let batch = universal_evidence( "src/generic-return-conflict.ts", br#"class Item { inspect(): void {} } class Other {} @@ -3513,7 +3513,7 @@ function use() { choose(new Item(), new Other()).inspect(); } #[test] fn typescript_non_nullable_receiver_preserves_nominal_member_target() { - let batch = candidate( + let batch = universal_evidence( "src/non-nullable-receiver.ts", br#"class Item { inspect(): void {} } function use(value: NonNullable) { value.inspect(); } @@ -3534,7 +3534,7 @@ function use(value: NonNullable) { value.inspect(); } #[test] fn typescript_awaited_and_readonly_receivers_preserve_nominal_member_targets() { - let batch = candidate( + let batch = universal_evidence( "src/utility-receivers.ts", br#"class Item { inspect(): void {} } function useAwaited(value: Awaited>) { value.inspect(); } @@ -3562,7 +3562,7 @@ function useReadonly(value: Readonly) { value.inspect(); } #[test] fn typescript_pick_and_omit_receivers_project_exact_members() { - let batch = candidate( + let batch = universal_evidence( "src/pick-omit-receivers.ts", br#"interface Options { enabled(): void; debug(): void } function use(picked: Pick, omitted: Omit) { @@ -3610,7 +3610,7 @@ function use(picked: Pick, omitted: Omit) .all(|candidate| candidate.constraints.exact_target_declaration_id.is_none()) ); - let unknown = candidate( + let unknown = universal_evidence( "src/pick-unknown-key.ts", br#"interface Options { enabled(): void } type Key = string; @@ -3626,7 +3626,7 @@ function use(value: Pick) { value.enabled(); } #[test] fn typescript_exclude_and_extract_narrow_nominal_union_receivers() { - let batch = candidate( + let batch = universal_evidence( "src/exclude-extract-receivers.ts", br#"class Item { inspect(): void {} } class Other { other(): void {} } @@ -3667,7 +3667,7 @@ function ambiguous(value: Exclude) { value.inspect(); } #[test] fn typescript_conditional_receivers_select_unique_nominal_branch() { - let batch = candidate( + let batch = universal_evidence( "src/conditional-receivers.ts", br#"class Item { inspect(): void {} } class Other { other(): void {} } @@ -3714,7 +3714,7 @@ function object(value: ChooseObject) { value.inspect(); } #[test] fn typescript_mapped_modifier_aliases_preserve_nominal_member_targets() { - let batch = candidate( + let batch = universal_evidence( "src/mapped-modifiers.ts", br#"class Item { inspect(): void {} } type MutableRequired = { -readonly [K in keyof T]-?: T[K] }; @@ -3745,7 +3745,7 @@ function use(mutable: MutableRequired, readonlyValue: ReadonlyOptional) { value.inspect(); #[test] fn typescript_generic_index_signature_values_resolve_member_calls() { - let batch = candidate( + let batch = universal_evidence( "src/generic-index-signature.ts", br#"class Schema { optional() {} } type Shape = { [key: string]: Schema }; @@ -3799,7 +3799,7 @@ class ObjectHolder { #[test] fn typescript_callable_member_aliases_resolve_property_targets() { - let batch = candidate( + let batch = universal_evidence( "src/callable-alias.ts", br#"class Service { run(value: string) {} @@ -3823,7 +3823,7 @@ fn typescript_callable_member_aliases_resolve_property_targets() { #[test] fn typescript_optional_callable_union_properties_resolve_exact_targets() { - let batch = candidate( + let batch = universal_evidence( "src/optional-callable.ts", br#"type Callback = (value: string) => string; interface Hooks { callback?: Callback | undefined } @@ -3846,8 +3846,8 @@ function use(hooks: Hooks) { } #[test] -fn candidate_preserves_named_and_anonymous_default_exports() { - let anonymous = candidate( +fn universal_evidence_preserves_named_and_anonymous_default_exports() { + let anonymous = universal_evidence( "src/default.ts", b"export default function() { return 1; }\n", ); @@ -3861,17 +3861,17 @@ fn candidate_preserves_named_and_anonymous_default_exports() { binding.spelling == "default" && binding.kind == compass_languages::BindingKind::Reexport })); - let named = candidate("src/named.ts", b"function run() {}\nexport default run;\n"); + let named = universal_evidence("src/named.ts", b"function run() {}\nexport default run;\n"); assert!(named.bindings.iter().any(|binding| { binding.spelling == "default" && binding.kind == compass_languages::BindingKind::Reexport })); } #[test] -fn candidate_evidence_is_deterministic_and_parser_recovery_is_diagnosed() { +fn universal_evidence_is_deterministic_and_parser_recovery_is_diagnosed() { let source = b"export function broken( { return 1;\n"; - let first = candidate("src/broken.ts", source); - let second = candidate("src/broken.ts", source); + let first = universal_evidence("src/broken.ts", source); + let second = universal_evidence("src/broken.ts", source); assert_eq!(first, second); assert!( first @@ -3886,7 +3886,7 @@ fn candidate_evidence_is_deterministic_and_parser_recovery_is_diagnosed() { } #[test] -fn candidate_resolves_only_proven_nominal_receivers_and_exact_members() { +fn universal_evidence_resolves_only_proven_nominal_receivers_and_exact_members() { let source = br#"class Widget { field = 1; run() { this.field; } @@ -3902,7 +3902,7 @@ object[key]; function run() {} unknown.run(); "#; - let batch = candidate("src/widget.ts", source); + let batch = universal_evidence("src/widget.ts", source); validate_evidence(&batch, EvidenceLimits::default()).expect("valid evidence"); let run = batch @@ -3953,7 +3953,7 @@ unknown.run(); && candidate.target_spelling == "key" })); - let imported = candidate( + let imported = universal_evidence( "src/imported.ts", b"import * as api from \"./api\";\napi.run();\n", ); @@ -3971,7 +3971,7 @@ unknown.run(); ); assert!(imported_call.binding_id.is_some()); - let overloads = candidate( + let overloads = universal_evidence( "src/overloads.ts", b"function run() {}\nfunction run(value: string) {}\nrun();\nrun(\"x\");\n", ); @@ -3993,7 +3993,7 @@ unknown.run(); && candidate.constraints.exact_target_declaration_id.is_some() })); - let typed_overloads = candidate( + let typed_overloads = universal_evidence( "src/typed-overloads.ts", b"function parse(value: string) {}\nfunction parse(value: number) {}\nparse(\"text\");\nparse(42);\n", ); @@ -4030,7 +4030,7 @@ unknown.run(); number_parse.constraints.exact_target_declaration_id ); - let constructed = candidate( + let constructed = universal_evidence( "src/constructed.ts", b"class Constructed { constructor(value: string) {} }\nnew Constructed(\"ok\");\n", ); @@ -4044,7 +4044,7 @@ unknown.run(); && candidate.constraints.argument_types == [Some("string".to_owned())] })); - let hierarchy = candidate( + let hierarchy = universal_evidence( "src/hierarchy.ts", b"class Base { run() {} }\nclass Child extends Base { call() { super.run(); } }\n", ); @@ -4060,7 +4060,7 @@ unknown.run(); == Some(base_run.id.as_str()) })); - let imported_hierarchy = candidate( + let imported_hierarchy = universal_evidence( "src/imported-hierarchy.ts", b"import * as bases from \"./bases\";\nclass Child extends bases.Base { call() { super.run(); } }\n", ); @@ -4089,7 +4089,7 @@ unknown.run(); Some("./bases::Base.run") ); - let inherited_constructor = candidate( + let inherited_constructor = universal_evidence( "src/inherited-constructor.ts", b"class Base { constructor(value: string, count: number) {} }\nclass Child extends Base {}\nnew Child(\"ok\", 1);\nnew Child();\n", ); @@ -4111,7 +4111,7 @@ unknown.run(); && candidate.constraints.exact_target_declaration_id.is_none() })); - let arity_negative = candidate( + let arity_negative = universal_evidence( "src/arity.ts", b"function exact(value: string) {}\nexact();\nexact(\"ok\");\nfunction optional(value?: string) {}\noptional(\"ok\");\n", ); @@ -4142,7 +4142,7 @@ unknown.run(); && candidate.constraints.argument_count == Some(1) })); - let private = candidate( + let private = universal_evidence( "src/private.ts", b"class Secret { #run() {} call() { this.#run(); } }\nnew Secret().call();\n", ); @@ -4160,7 +4160,7 @@ unknown.run(); } #[test] -fn javascript_candidate_infers_object_literal_members_and_lexical_calls() { +fn universal_evidence_infers_object_literal_members_and_lexical_calls() { let source = br#"const config = { legacyAgreements: { Stytch: "gold" }, sponsorsToIgnore: ["axios"], @@ -4175,7 +4175,7 @@ config.legacyAgreements; config.sponsorsToIgnore; frozen.active; "#; - let batch = candidate("src/server.js", source); + let batch = universal_evidence("src/server.js", source); validate_evidence(&batch, EvidenceLimits::default()).expect("valid evidence"); let agreements = batch @@ -4235,7 +4235,7 @@ frozen.active; #[test] fn javascript_flow_assignment_object_literal_selects_exact_members() { - let batch = candidate( + let batch = universal_evidence( "src/object-reassignment.js", br#"function read(flag) { let auth = undefined; @@ -4256,7 +4256,7 @@ fn javascript_flow_assignment_object_literal_selects_exact_members() { == Some(username.id.as_str()) })); - let spread = candidate( + let spread = universal_evidence( "src/object-spread-reassignment.js", br#"const base = { username: 'user' }; let auth; @@ -4281,7 +4281,7 @@ auth.username; #[test] fn javascript_flow_assignment_object_literal_branch_fails_closed() { - let batch = candidate( + let batch = universal_evidence( "src/object-branch-reassignment.js", br#"function read(flag) { let auth = undefined; @@ -4301,7 +4301,7 @@ fn javascript_flow_assignment_object_literal_branch_fails_closed() { #[test] fn javascript_object_literal_methods_resolve_this_members() { - let batch = candidate( + let batch = universal_evidence( "src/object-this.js", br#"const api = { write() {}, @@ -4322,7 +4322,7 @@ api.remove(); == Some(write.id.as_str()) })); - let spread = candidate( + let spread = universal_evidence( "src/object-this-spread.js", br#"const base = { write() {} }; const api = { @@ -4341,7 +4341,7 @@ api.remove(); #[test] fn javascript_object_flow_member_reads_and_literal_writes_resolve_exact_targets() { - let batch = candidate( + let batch = universal_evidence( "src/object-flow.js", br#"const response = { request: requestValue, data: null }; const later = () => { @@ -4396,7 +4396,7 @@ new Stream(); 2 ); - let nested = candidate( + let nested = universal_evidence( "src/object-flow-nested.js", br#"function register(transport, data) { transport.request({}, function handleResponse(res) { @@ -4436,7 +4436,7 @@ new Stream(); == Some(nested_request.id.as_str()) })); - let escaped = candidate( + let escaped = universal_evidence( "src/object-flow-escape.js", br#"const response = { request: requestValue }; consume(response); @@ -4455,7 +4455,7 @@ response.request; == Some(escaped_request.id.as_str()) })); - let nested_escape = candidate( + let nested_escape = universal_evidence( "src/object-flow-nested-escape.js", br#"const response = { request: requestValue }; const later = () => consume(response); @@ -4480,7 +4480,7 @@ response.request; #[test] fn javascript_stable_object_property_assignments_resolve_exact_members() { - let batch = candidate( + let batch = universal_evidence( "src/stable-object-assignment.js", br#"const validators = {}; validators.transitional = function transitional() {}; @@ -4520,8 +4520,8 @@ validators.spelling('value'); } #[test] -fn candidate_emits_curated_external_builtin_evidence_and_respects_shadowing() { - let batch = candidate( +fn universal_evidence_emits_curated_external_builtin_evidence_and_respects_shadowing() { + let batch = universal_evidence( "src/builtins.ts", br#"Array.from(items); console.log("ready"); @@ -4578,7 +4578,7 @@ Error("broken"); "Error", "global::Error" )); - let invalid_instance_member = candidate( + let invalid_instance_member = universal_evidence( "src/invalid-builtin-member.ts", b"new ArrayBuffer().add(1);\nnew WeakMap().values();\n", ); @@ -4587,7 +4587,7 @@ Error("broken"); && (candidate.target_spelling == "add" || candidate.target_spelling == "values") })); - let shadowed = candidate( + let shadowed = universal_evidence( "src/shadowed-builtins.ts", b"const Array = { from() {} };\nArray.from(items);\n", ); @@ -4597,8 +4597,8 @@ Error("broken"); } #[test] -fn candidate_declares_for_of_bindings_with_exact_source_anchors() { - let batch = candidate( +fn universal_evidence_declares_for_of_bindings_with_exact_source_anchors() { + let batch = universal_evidence( "src/loops.ts", b"for (const _ of DATA) consume(_);\nfor (const { id } of rows) consume(id);\n", ); @@ -4620,7 +4620,7 @@ fn candidate_declares_for_of_bindings_with_exact_source_anchors() { == b"for (const _ of DATA) consume(_);\nfor (const { ".len() as u64 })); - let callbacks = candidate( + let callbacks = universal_evidence( "src/callbacks.js", b"rows.find((activeSponsor) => activeSponsor.slug === target);\n", ); @@ -4628,7 +4628,7 @@ fn candidate_declares_for_of_bindings_with_exact_source_anchors() { declaration.kind == "parameter" && declaration.name == "activeSponsor" })); - let unparenthesized = candidate( + let unparenthesized = universal_evidence( "src/unparenthesized.js", b"rows.find(activeSponsor => activeSponsor.slug === target);\n", ); @@ -4636,7 +4636,7 @@ fn candidate_declares_for_of_bindings_with_exact_source_anchors() { declaration.kind == "parameter" && declaration.name == "activeSponsor" })); - let caught = candidate( + let caught = universal_evidence( "src/catch.js", b"try { run(); } catch (error) { log(error); }\n", ); @@ -4664,7 +4664,7 @@ consume(nonCallable); const maybe = Math.random() ? onValue : nonCallable; consume(maybe); "#; - let batch = candidate("src/callback-values.js", source); + let batch = universal_evidence("src/callback-values.js", source); validate_evidence(&batch, EvidenceLimits::default()).expect("callback value evidence"); let on_value = batch @@ -4708,8 +4708,8 @@ consume(maybe); } #[test] -fn candidate_resolves_qualified_and_builtin_type_references_without_fallback_guesses() { - let batch = candidate( +fn universal_evidence_resolves_qualified_and_builtin_type_references_without_fallback_guesses() { + let batch = universal_evidence( "src/types.ts", br#"import * as Benchmark from "benchmark"; type Event = Benchmark.Event; @@ -4802,7 +4802,7 @@ const typed: (_params?: string) => void = (_value) => {}; }) ); - let shadowed = candidate( + let shadowed = universal_evidence( "src/shadowed-type.ts", b"type Promise = { then(): void };\ntype Local = Promise;\n", ); @@ -4820,8 +4820,8 @@ const typed: (_params?: string) => void = (_value) => {}; } #[test] -fn candidate_preserves_dynamic_calls_and_proven_super_and_type_member_evidence() { - let batch = candidate( +fn universal_evidence_preserves_dynamic_calls_and_proven_super_and_type_member_evidence() { + let batch = universal_evidence( "src/advanced.ts", br#"import * as schemas from "./schemas"; interface Coerced extends schemas._ZodString {} @@ -4891,8 +4891,8 @@ const dynamicConstructor = new (factory || Base)(); } #[test] -fn javascript_candidate_keeps_block_bindings_distinct_and_hoists_var() { - let batch = candidate( +fn universal_evidence_keeps_block_bindings_distinct_and_hoists_var() { + let batch = universal_evidence( "src/blocks.js", br#"function run(flag) { if (flag) { @@ -4932,7 +4932,7 @@ fn javascript_candidate_keeps_block_bindings_distinct_and_hoists_var() { #[test] fn object_literal_properties_do_not_shadow_unqualified_calls() { - let batch = candidate( + let batch = universal_evidence( "src/object-shadow.js", br#"const object = { run: () => 1 }; run(); @@ -4945,7 +4945,7 @@ run(); } #[test] -fn candidate_tracks_type_alias_enum_and_string_named_members() { +fn universal_evidence_tracks_type_alias_enum_and_string_named_members() { let source = br#"type Config = { name: string }; function makeConfig(): Config { return { name: "ready" }; } const config = makeConfig(); @@ -4961,7 +4961,7 @@ class Box { new Box().read(); "#; - let batch = candidate("src/typed-members.ts", source); + let batch = universal_evidence("src/typed-members.ts", source); validate_evidence(&batch, EvidenceLimits::default()).expect("typed member evidence"); let config_name = batch @@ -5005,8 +5005,8 @@ new Box().read(); } #[test] -fn typescript_candidate_preserves_imported_type_receiver_for_member_evidence() { - let batch = candidate( +fn universal_evidence_preserves_imported_type_receiver_for_member_evidence() { + let batch = universal_evidence( "src/consumer.ts", br#"import type { Config } from "./types"; export function use(config: Config) { config.inspect(); } diff --git a/crates/compass-languages/tests/universal_evidence.rs b/crates/compass-languages/tests/universal_evidence.rs index 03470671..0ee7ceb0 100644 --- a/crates/compass-languages/tests/universal_evidence.rs +++ b/crates/compass-languages/tests/universal_evidence.rs @@ -1,11 +1,12 @@ #![allow(clippy::expect_used)] use compass_languages::{ - AdapterIdentity, AdapterRegistry, BindingFact, BindingKind, CandidateRelation, DeclarationFact, - Engine, EvidenceErrorCode, EvidenceLimits, EvidenceRange, Extraction, HierarchyConstraint, - LanguageCapability, OccurrenceFact, ReceiverDispatchStrategy, RelationshipCandidate, - ResolutionConstraint, ScopeFact, SemanticEvidenceBatch, SemanticRole, - UNIVERSAL_EVIDENCE_SCHEMA, UniversalAdapterProfile, validate_evidence, + BindingFact, BindingKind, CandidateRelation, DeclarationFact, Engine, EvidenceErrorCode, + EvidenceLimits, EvidenceRange, Extraction, HierarchyConstraint, LanguageCapability, + OccurrenceFact, ReceiverDispatchStrategy, RelationshipCandidate, ResolutionConstraint, + ScopeFact, SemanticEvidenceBatch, SemanticRole, UNIVERSAL_EVIDENCE_SCHEMA, + UniversalEvidenceIdentity, UniversalEvidenceQualification, UniversalEvidenceRegistry, + validate_evidence, }; fn range(start: u64, end: u64) -> EvidenceRange { @@ -22,14 +23,14 @@ fn range(start: u64, end: u64) -> EvidenceRange { fn valid_batch() -> SemanticEvidenceBatch { SemanticEvidenceBatch { - adapter: AdapterIdentity { + pipeline: UniversalEvidenceIdentity { id: "compass.python".to_owned(), language: "python".to_owned(), dialect: None, version: 1, evidence_schema: UNIVERSAL_EVIDENCE_SCHEMA.to_owned(), - profile: UniversalAdapterProfile::UniversalCandidate, - producer: "tree-sitter-python".to_owned(), + qualification: UniversalEvidenceQualification::Qualifying, + emitter: "tree-sitter-python".to_owned(), capabilities: vec![ LanguageCapability::Declarations, LanguageCapability::LexicalScopes, @@ -136,7 +137,7 @@ fn evidence_round_trips_with_closed_camel_case_schema() { validate_evidence(&batch, EvidenceLimits::default()).expect("valid fixture"); let encoded = serde_json::to_value(&batch).expect("serialize evidence"); - assert_eq!(encoded["adapter"]["language"], "python"); + assert_eq!(encoded["pipeline"]["language"], "python"); assert_eq!( encoded["occurrences"][0]["ownerDeclarationId"], "decl:caller" @@ -159,7 +160,7 @@ fn evidence_round_trips_with_closed_camel_case_schema() { #[test] fn unknown_fields_are_rejected_at_nested_boundaries() { let mut encoded = serde_json::to_value(valid_batch()).expect("serialize evidence"); - encoded["adapter"]["compatibilityMode"] = serde_json::json!(true); + encoded["pipeline"]["compatibilityMode"] = serde_json::json!(true); let error = serde_json::from_value::(encoded).expect_err("unknown field"); assert!(error.to_string().contains("unknown field")); @@ -206,7 +207,7 @@ fn call_result_chain_references_are_typed_and_acyclic() { let mut missing = valid_batch(); missing - .adapter + .pipeline .capabilities .push(LanguageCapability::TypeReferences); missing.bindings[0].kind = BindingKind::CallResult; @@ -215,7 +216,7 @@ fn call_result_chain_references_are_typed_and_acyclic() { let mut receiver_cycle = valid_batch(); receiver_cycle - .adapter + .pipeline .capabilities .push(LanguageCapability::TypeReferences); receiver_cycle.bindings[0].kind = BindingKind::CallResult; @@ -224,7 +225,7 @@ fn call_result_chain_references_are_typed_and_acyclic() { let mut call_result_fallback = valid_batch(); call_result_fallback - .adapter + .pipeline .capabilities .push(LanguageCapability::TypeReferences); call_result_fallback.bindings[0].kind = BindingKind::CallResult; @@ -296,7 +297,7 @@ fn behavioral_candidates_require_occurrences() { fn capabilities_and_language_constraints_fail_closed() { let mut undeclared = valid_batch(); undeclared - .adapter + .pipeline .capabilities .retain(|capability| *capability != LanguageCapability::Calls); assert_code(&undeclared, EvidenceErrorCode::UndeclaredCapability); @@ -307,7 +308,7 @@ fn capabilities_and_language_constraints_fail_closed() { let mut undeclared_external = valid_batch(); undeclared_external - .adapter + .pipeline .capabilities .retain(|capability| *capability != LanguageCapability::ExternalReferences); assert_code( @@ -331,7 +332,7 @@ fn capabilities_and_language_constraints_fail_closed() { let mut invalid_hierarchy_relation = undeclared_hierarchy; invalid_hierarchy_relation - .adapter + .pipeline .capabilities .push(LanguageCapability::HierarchyDispatch); invalid_hierarchy_relation.candidates[0] @@ -394,10 +395,10 @@ fn complete_direct_base_sets_reject_unsupported_declaration_kinds() { #[test] fn rust_associated_type_constraints_require_an_exact_receiver_identity() { let mut batch = valid_batch(); - batch.adapter.id = "compass.rust".to_owned(); - batch.adapter.language = "rust".to_owned(); - batch.adapter.version = 6; - batch.adapter.capabilities.extend([ + batch.pipeline.id = "compass.rust".to_owned(); + batch.pipeline.language = "rust".to_owned(); + batch.pipeline.version = 6; + batch.pipeline.capabilities.extend([ LanguageCapability::TypeReferences, LanguageCapability::HierarchyDispatch, ]); @@ -463,13 +464,13 @@ fn first_fact_error_is_independent_of_input_order() { } #[test] -fn universal_adapter_profiles_are_unique_sorted_and_truthful() { - AdapterRegistry::validate().expect("production registry must be valid"); - let profiles = AdapterRegistry::universal_profiles(); +fn universal_evidence_pipelines_are_unique_sorted_and_truthful() { + UniversalEvidenceRegistry::validate().expect("production registry must be valid"); + let pipelines = UniversalEvidenceRegistry::pipelines(); assert_eq!( - profiles + pipelines .iter() - .map(|profile| profile.language) + .map(|pipeline| pipeline.producer.language) .collect::>(), [ "csharp", @@ -485,51 +486,52 @@ fn universal_adapter_profiles_are_unique_sorted_and_truthful() { ] ); assert!( - profiles + pipelines .iter() - .all(|profile| !profile.capabilities.is_empty()) + .all(|pipeline| !pipeline.producer.capabilities.is_empty()) ); - assert!(profiles.iter().all(|profile| { - !profile.id.is_empty() - && profile.version > 0 - && profile.evidence_schema == UNIVERSAL_EVIDENCE_SCHEMA + assert!(pipelines.iter().all(|pipeline| { + !pipeline.producer.id.is_empty() + && pipeline.producer.version > 0 + && pipeline.producer.evidence_schema == UNIVERSAL_EVIDENCE_SCHEMA })); assert_eq!( - AdapterRegistry::universal_profile("go").map(|profile| profile.version), + UniversalEvidenceRegistry::pipeline("go").map(|pipeline| pipeline.producer.version), Some(3) ); assert_eq!( - profiles + pipelines .iter() - .map(|profile| profile.id) + .map(|pipeline| pipeline.producer.id) .collect::>() .len(), - profiles.len() + pipelines.len() ); - assert!(profiles.iter().all(|profile| { - profile + assert!(pipelines.iter().all(|pipeline| { + pipeline + .producer .capabilities .windows(2) .all(|pair| pair[0] < pair[1]) })); assert_eq!( - AdapterRegistry::universal_profile("java") - .map(|profile| (profile.version, profile.profile)), - Some((3, UniversalAdapterProfile::UniversalCandidate)) + UniversalEvidenceRegistry::pipeline("java") + .map(|pipeline| (pipeline.producer.version, pipeline.qualification)), + Some((3, UniversalEvidenceQualification::Qualifying)) ); assert_eq!( - AdapterRegistry::universal_profile("rust").map(|profile| profile.version), + UniversalEvidenceRegistry::pipeline("rust").map(|pipeline| pipeline.producer.version), Some(15) ); assert_eq!( - AdapterRegistry::universal_profile("javascript") - .map(|profile| (profile.version, profile.profile)), - Some((5, UniversalAdapterProfile::UniversalCandidate)) + UniversalEvidenceRegistry::pipeline("javascript") + .map(|pipeline| (pipeline.producer.version, pipeline.qualification)), + Some((5, UniversalEvidenceQualification::Qualifying)) ); assert_eq!( - AdapterRegistry::universal_profile("typescript") - .map(|profile| (profile.version, profile.profile)), - Some((5, UniversalAdapterProfile::UniversalCandidate)) + UniversalEvidenceRegistry::pipeline("typescript") + .map(|pipeline| (pipeline.producer.version, pipeline.qualification)), + Some((5, UniversalEvidenceQualification::Qualifying)) ); } @@ -556,7 +558,7 @@ fn empty_hard_cut_sources_emit_zero_width_file_inventory_evidence() { .expect("empty source evidence"); validate_evidence(&evidence, EvidenceLimits::default()).expect("valid empty evidence"); - assert_eq!(evidence.adapter.language, language); + assert_eq!(evidence.pipeline.language, language); assert_eq!(evidence.declarations.len(), 1); assert_eq!(evidence.declarations[0].kind, "file"); assert_eq!(evidence.declarations[0].range.source_file, source_file); @@ -596,7 +598,7 @@ fn trivia_only_hard_cut_sources_anchor_file_inventory_to_source_bytes() { validate_evidence(&evidence, EvidenceLimits::default()) .expect("valid trivia-only evidence"); - assert_eq!(evidence.adapter.language, language); + assert_eq!(evidence.pipeline.language, language); assert_eq!(evidence.declarations.len(), 1); assert_eq!(evidence.declarations[0].range.start_byte, 0); assert_eq!(evidence.declarations[0].range.end_byte, 1); @@ -607,7 +609,7 @@ fn trivia_only_hard_cut_sources_anchor_file_inventory_to_source_bytes() { } #[test] -fn typescript_javascript_candidate_is_wired_into_production_extraction() { +fn typescript_javascript_pipeline_is_wired_into_production_extraction() { for (path, source_file, language, dialect, source) in [ ( "/repo/src/app.ts", @@ -648,13 +650,13 @@ fn typescript_javascript_candidate_is_wired_into_production_extraction() { .semantic_evidence .expect("production universal evidence"); validate_evidence(&evidence, EvidenceLimits::default()).expect("valid ECMAScript evidence"); - assert_eq!(evidence.adapter.language, language); - assert_eq!(evidence.adapter.dialect.as_deref(), Some(dialect)); + assert_eq!(evidence.pipeline.language, language); + assert_eq!(evidence.pipeline.dialect.as_deref(), Some(dialect)); assert_eq!( - evidence.adapter.profile, - UniversalAdapterProfile::UniversalCandidate + evidence.pipeline.qualification, + UniversalEvidenceQualification::Qualifying ); - assert_eq!(evidence.adapter.id, format!("compass.{language}.candidate")); + assert_eq!(evidence.pipeline.id, format!("compass.{language}")); } } @@ -690,7 +692,7 @@ class Derived(Base): .expect("python universal evidence"); validate_evidence(&evidence, EvidenceLimits::default()).expect("valid python evidence"); - assert_eq!(evidence.adapter.language, "python"); + assert_eq!(evidence.pipeline.language, "python"); assert!( evidence .bindings @@ -1501,7 +1503,7 @@ func (d *Derived) Handle(value alias.Input) alias.Output { .expect("go universal evidence"); validate_evidence(&evidence, EvidenceLimits::default()).expect("valid go evidence"); - assert_eq!(evidence.adapter.language, "go"); + assert_eq!(evidence.pipeline.language, "go"); assert!(evidence.bindings.iter().any(|binding| { binding.spelling == "alias" && binding.qualified_target == "example.com/tools" })); @@ -1831,7 +1833,7 @@ func invoke(value interface{}) { } #[test] -fn direct_adapter_ids_and_partial_diagnostics_are_deterministic() { +fn direct_evidence_ids_and_partial_diagnostics_are_deterministic() { let path = std::path::Path::new("/repo/src/example.py"); let source_file = "src/example.py"; let source = b"def broken(value:\n helper(value)\n"; diff --git a/crates/compass-resolve/src/evidence/index/builder.rs b/crates/compass-resolve/src/evidence/index/builder.rs index 00e52646..cc5290bd 100644 --- a/crates/compass-resolve/src/evidence/index/builder.rs +++ b/crates/compass-resolve/src/evidence/index/builder.rs @@ -19,7 +19,7 @@ struct LanguagePresence { impl LanguagePresence { fn detect(batches: &[SemanticEvidenceBatch], inventory_nodes: &[NodeRecord]) -> Self { let mut presence = Self::default(); - for language in batches.iter().map(|batch| batch.adapter.language.as_str()) { + for language in batches.iter().map(|batch| batch.pipeline.language.as_str()) { presence.observe(language); } for node in inventory_nodes { @@ -683,7 +683,7 @@ impl IndexBuilder<'_> { } } // Object-literal members are deliberately collected in their lexical - // binding scope by the TypeScript adapter: unlike a class/interface, + // binding scope by the TypeScript producer: unlike a class/interface, // an object value has no syntax-level scope owner. Recover the // source-qualified object variable as an additional owner so imported // chains such as `api.make(value).inspect()` can traverse the diff --git a/crates/compass-resolve/src/evidence/languages/typescript/members.rs b/crates/compass-resolve/src/evidence/languages/typescript/members.rs index 6d277c09..37bd77f3 100644 --- a/crates/compass-resolve/src/evidence/languages/typescript/members.rs +++ b/crates/compass-resolve/src/evidence/languages/typescript/members.rs @@ -122,7 +122,7 @@ impl ResolutionDb<'_> { } // CommonJS object exports retain the file module as their owner. A // `Member("*")` alias is emitted for that owner only after the - // adapter proves every spread source, so treating a module owner as a + // producer proves every spread source, so treating a module owner as a // structural object here does not widen arbitrary module lookups. if owner.kind == "module" { return true; diff --git a/crates/compass-resolve/src/evidence/mod.rs b/crates/compass-resolve/src/evidence/mod.rs index 5ef29702..b1744460 100644 --- a/crates/compass-resolve/src/evidence/mod.rs +++ b/crates/compass-resolve/src/evidence/mod.rs @@ -121,7 +121,7 @@ struct TypeScriptMemberPath { } /// A source-proven object spread emitted by the TypeScript/JavaScript -/// candidate adapter. The `*` member spelling is an owner alias marker, not a +/// evidence producer. The `*` member spelling is an owner alias marker, not a /// wildcard export: the destination object inherits only members that the /// resolver can prove on this source owner. #[derive(Clone, Debug)] diff --git a/crates/compass-resolve/src/evidence/partition.rs b/crates/compass-resolve/src/evidence/partition.rs index 64ff240d..4ac4b3b1 100644 --- a/crates/compass-resolve/src/evidence/partition.rs +++ b/crates/compass-resolve/src/evidence/partition.rs @@ -186,7 +186,7 @@ pub(crate) fn materialize_bounded_owned( // conservative admission profile even when the caller requested a // richer one, because closed-world inference would be unsound inside // a partial repository view. Every retained candidate carries an - // adapter-proven exact target ID. + // producer-proven exact target ID. let partition_admission = ResolutionAdmission::Low; let index = UniversalResolutionIndex::new_with_prevalidated_project_inventory_owned_at_inference( @@ -279,7 +279,7 @@ fn compact_low_inference_evidence(batches: &mut [SemanticEvidenceBatch]) -> usiz .flat_map(|batch| &batch.scopes) .filter_map(|scope| scope.owner_declaration_id.clone()), ); - // Some adapters intentionally leave lexical targets for the collection + // Some producers intentionally leave lexical targets for the collection // resolver instead of stamping an exact declaration ID. Preserve a leaf // declaration whenever its spelling is used in the same source batch; // otherwise compaction could erase a parameter/property before lexical @@ -437,7 +437,7 @@ fn batch_source_key(batch: &SemanticEvidenceBatch) -> (&str, &str) { .map(|fact| fact.range.source_file.as_str()) }) .unwrap_or_default(); - (source, batch.adapter.language.as_str()) + (source, batch.pipeline.language.as_str()) } #[cfg(test)] @@ -662,7 +662,7 @@ mod tests { .extract_source(Path::new("src/example.py"), b"def run():\n return 1\n")? .semantic_evidence .ok_or("missing evidence")?; - batch.adapter.language.clear(); + batch.pipeline.language.clear(); let mut nodes = Vec::new(); let mut edges = Vec::new(); diff --git a/crates/compass-resolve/src/frameworks/mod.rs b/crates/compass-resolve/src/frameworks/mod.rs index 06187f8c..5aa74ac0 100644 --- a/crates/compass-resolve/src/frameworks/mod.rs +++ b/crates/compass-resolve/src/frameworks/mod.rs @@ -220,7 +220,7 @@ fn universal_framework_targets_are_materialized( ) -> bool { let Some(batch) = extraction.semantic_evidence.as_ref().filter(|batch| { matches!( - batch.adapter.language.as_str(), + batch.pipeline.language.as_str(), "csharp" | "javascript" | "php" | "ruby" | "typescript" ) }) else { @@ -261,7 +261,7 @@ pub(super) fn materialize_universal_framework_targets( ) -> compass_languages::Extraction { let Some(batches) = extraction.semantic_evidence.as_ref().filter(|batch| { matches!( - batch.adapter.language.as_str(), + batch.pipeline.language.as_str(), "csharp" | "javascript" | "php" | "ruby" | "typescript" ) }) else { diff --git a/crates/compass-resolve/src/frameworks/qualification.rs b/crates/compass-resolve/src/frameworks/qualification.rs index 91cf4ff8..682fac62 100644 --- a/crates/compass-resolve/src/frameworks/qualification.rs +++ b/crates/compass-resolve/src/frameworks/qualification.rs @@ -118,7 +118,7 @@ pub fn qualify_framework_case( limits: FrameworkLimits, case: &FrameworkQualificationCase, ) -> Result { - // A universal candidate intentionally keeps the per-file extraction + // A qualifying universal pipeline intentionally keeps the per-file extraction // evidence-only: declaration nodes are projected by the collection // resolver. Qualification fixtures also exercise a single extracted file, // so provide the target index with the same source-backed declaration diff --git a/crates/compass-resolve/src/lib.rs b/crates/compass-resolve/src/lib.rs index d285bf92..b6918e2a 100644 --- a/crates/compass-resolve/src/lib.rs +++ b/crates/compass-resolve/src/lib.rs @@ -538,15 +538,17 @@ fn augment_universal_project_inventory( ) { let mut source_languages = BTreeMap::::new(); let mut source_evidence_lengths = BTreeMap::::new(); - for batch in evidence_batches - .iter() - .filter(|batch| matches!(batch.adapter.language.as_str(), "javascript" | "typescript")) - { + for batch in evidence_batches.iter().filter(|batch| { + matches!( + batch.pipeline.language.as_str(), + "javascript" | "typescript" + ) + }) { for declaration in &batch.declarations { if !declaration.range.source_file.is_empty() { source_languages .entry(declaration.range.source_file.clone()) - .or_insert_with(|| batch.adapter.language.clone()); + .or_insert_with(|| batch.pipeline.language.clone()); let normalized_source = source_key(&declaration.range.source_file, root); let length = source_evidence_lengths .entry(normalized_source) @@ -631,7 +633,12 @@ fn augment_universal_project_inventory( let declaration_ids = evidence_batches .iter() - .filter(|batch| matches!(batch.adapter.language.as_str(), "javascript" | "typescript")) + .filter(|batch| { + matches!( + batch.pipeline.language.as_str(), + "javascript" | "typescript" + ) + }) .flat_map(|batch| { batch .declarations @@ -650,10 +657,12 @@ fn augment_universal_project_inventory( .to_owned() }) .collect::>(); - for batch in evidence_batches - .iter() - .filter(|batch| matches!(batch.adapter.language.as_str(), "javascript" | "typescript")) - { + for batch in evidence_batches.iter().filter(|batch| { + matches!( + batch.pipeline.language.as_str(), + "javascript" | "typescript" + ) + }) { let occurrences_by_id = batch .occurrences .iter() diff --git a/crates/compass-resolve/src/program.rs b/crates/compass-resolve/src/program.rs index 8d8f51f9..773b4188 100644 --- a/crates/compass-resolve/src/program.rs +++ b/crates/compass-resolve/src/program.rs @@ -36,7 +36,7 @@ pub fn collect_program_projection_sites(extractions: &[Extraction]) -> ProgramPr for batch in extractions .iter() .filter_map(|extraction| extraction.semantic_evidence.as_ref()) - .filter(|batch| batch.adapter.language == "java") + .filter(|batch| batch.pipeline.language == "java") { for declaration in &batch.declarations { sites diff --git a/crates/compass-resolve/tests/evidence_resolution_contract.rs b/crates/compass-resolve/tests/evidence_resolution_contract.rs index f5054f41..b36dae91 100644 --- a/crates/compass-resolve/tests/evidence_resolution_contract.rs +++ b/crates/compass-resolve/tests/evidence_resolution_contract.rs @@ -1,9 +1,10 @@ use std::error::Error; use compass_languages::{ - AdapterIdentity, BindingFact, BindingKind, CandidateRelation, DeclarationFact, EvidenceRange, + BindingFact, BindingKind, CandidateRelation, DeclarationFact, EvidenceRange, LanguageCapability, OccurrenceFact, RelationshipCandidate, ResolutionConstraint, ScopeFact, - SemanticEvidenceBatch, SemanticRole, UNIVERSAL_EVIDENCE_SCHEMA, UniversalAdapterProfile, + SemanticEvidenceBatch, SemanticRole, UNIVERSAL_EVIDENCE_SCHEMA, UniversalEvidenceIdentity, + UniversalEvidenceQualification, }; use compass_resolve::evidence::{ ResolutionDecision, ResolutionRule, UniversalResolutionIndex, UniversalResolutionLimits, @@ -78,14 +79,14 @@ fn binding(id: &str, target: &str, target_declaration_id: Option<&str>) -> Bindi fn batch() -> SemanticEvidenceBatch { SemanticEvidenceBatch { - adapter: AdapterIdentity { + pipeline: UniversalEvidenceIdentity { id: "compass.python.contract-test".to_owned(), language: LANGUAGE.to_owned(), dialect: None, version: 1, evidence_schema: UNIVERSAL_EVIDENCE_SCHEMA.to_owned(), - profile: UniversalAdapterProfile::UniversalCandidate, - producer: "contract-test".to_owned(), + qualification: UniversalEvidenceQualification::Qualifying, + emitter: "contract-test".to_owned(), capabilities: vec![ LanguageCapability::Declarations, LanguageCapability::LexicalScopes, diff --git a/crates/compass-resolve/tests/inference_admission.rs b/crates/compass-resolve/tests/inference_admission.rs index d4ed75c1..ce4108a9 100644 --- a/crates/compass-resolve/tests/inference_admission.rs +++ b/crates/compass-resolve/tests/inference_admission.rs @@ -1,10 +1,10 @@ use std::collections::HashMap; use compass_languages::{ - AdapterIdentity, BindingFact, BindingKind, CandidateRelation, DeclarationFact, EvidenceRange, - Extraction, LanguageCapability, OccurrenceFact, RelationshipCandidate, ResolutionConstraint, - ScopeFact, SemanticEvidenceBatch, SemanticRole, UNIVERSAL_EVIDENCE_SCHEMA, - UniversalAdapterProfile, + BindingFact, BindingKind, CandidateRelation, DeclarationFact, EvidenceRange, Extraction, + LanguageCapability, OccurrenceFact, RelationshipCandidate, ResolutionConstraint, ScopeFact, + SemanticEvidenceBatch, SemanticRole, UNIVERSAL_EVIDENCE_SCHEMA, UniversalEvidenceIdentity, + UniversalEvidenceQualification, }; use compass_resolve::{ResolutionAdmission, resolve_prevalidated_owned_with_root_at_inference}; @@ -22,14 +22,14 @@ fn range(start: u64, end: u64) -> EvidenceRange { fn external_call_batch() -> SemanticEvidenceBatch { SemanticEvidenceBatch { - adapter: AdapterIdentity { + pipeline: UniversalEvidenceIdentity { id: "compass.python.inference-admission-test".to_owned(), language: "python".to_owned(), dialect: None, version: 1, evidence_schema: UNIVERSAL_EVIDENCE_SCHEMA.to_owned(), - profile: UniversalAdapterProfile::UniversalCandidate, - producer: "inference-admission-test".to_owned(), + qualification: UniversalEvidenceQualification::Qualifying, + emitter: "inference-admission-test".to_owned(), capabilities: vec![ LanguageCapability::Declarations, LanguageCapability::LexicalScopes, @@ -105,7 +105,7 @@ fn external_call_batch() -> SemanticEvidenceBatch { fn bound_external_import_batch() -> SemanticEvidenceBatch { let mut batch = external_call_batch(); - batch.adapter.capabilities = vec![ + batch.pipeline.capabilities = vec![ LanguageCapability::Declarations, LanguageCapability::LexicalScopes, LanguageCapability::Imports, diff --git a/crates/compass-resolve/tests/universal_evidence.rs b/crates/compass-resolve/tests/universal_evidence.rs index ba315dee..3640a379 100644 --- a/crates/compass-resolve/tests/universal_evidence.rs +++ b/crates/compass-resolve/tests/universal_evidence.rs @@ -831,8 +831,8 @@ class Service { .as_ref() .ok_or("missing Java universal evidence")?; validate_evidence(evidence, EvidenceLimits::default())?; - assert_eq!(evidence.adapter.id, "compass.java"); - assert_eq!(evidence.adapter.version, 3); + assert_eq!(evidence.pipeline.id, "compass.java"); + assert_eq!(evidence.pipeline.version, 3); let declaration_id = |qualified_name: &str| { evidence diff --git a/crates/compass-resolve/tests/universal_resolution/javascript.rs b/crates/compass-resolve/tests/universal_resolution/javascript.rs index 663c0d24..9ee25f4f 100644 --- a/crates/compass-resolve/tests/universal_resolution/javascript.rs +++ b/crates/compass-resolve/tests/universal_resolution/javascript.rs @@ -549,8 +549,8 @@ spread.isNumber(1); .iter() .map(|(relative, source)| { Engine::default() - .extract_source_universal_candidate_evidence(&root.join(relative), relative, source) - .map_err(|error| format!("candidate extraction failed for {relative}: {error}")) + .extract_source_universal_evidence(&root.join(relative), relative, source) + .map_err(|error| format!("universal evidence extraction failed for {relative}: {error}")) }) .collect::, _>>()?; let utils_number = batches[0] @@ -731,8 +731,8 @@ functionSpread.callable(1); .iter() .map(|(relative, source)| { Engine::default() - .extract_source_universal_candidate_evidence(&root.join(relative), relative, source) - .map_err(|error| format!("candidate extraction failed for {relative}: {error}")) + .extract_source_universal_evidence(&root.join(relative), relative, source) + .map_err(|error| format!("universal evidence extraction failed for {relative}: {error}")) }) .collect::, _>>()?; let ambiguous_call = batches[7] @@ -861,8 +861,8 @@ api.run(); .map(|(relative, source)| { let path = root.join(relative); Engine::default() - .extract_source_universal_candidate_evidence(&path, relative, source) - .map_err(|error| format!("candidate extraction failed for {relative}: {error}")) + .extract_source_universal_evidence(&path, relative, source) + .map_err(|error| format!("universal evidence extraction failed for {relative}: {error}")) }) .collect::, _>>()?; let run = batches[0] @@ -969,8 +969,8 @@ api.conflict(); .map(|(relative, source)| { let path = root.join(relative); Engine::default() - .extract_source_universal_candidate_evidence(&path, relative, source) - .map_err(|error| format!("candidate extraction failed for {relative}: {error}")) + .extract_source_universal_evidence(&path, relative, source) + .map_err(|error| format!("universal evidence extraction failed for {relative}: {error}")) }) .collect::, _>>()?; let base = batches[0] @@ -1088,8 +1088,8 @@ nonObject.missing(); .map(|(relative, source)| { let path = root.join(relative); Engine::default() - .extract_source_universal_candidate_evidence(&path, relative, source) - .map_err(|error| format!("candidate extraction failed for {relative}: {error}")) + .extract_source_universal_evidence(&path, relative, source) + .map_err(|error| format!("universal evidence extraction failed for {relative}: {error}")) }) .collect::, _>>()?; let calls = batches[5] @@ -1172,8 +1172,8 @@ api.cjsPrivate(); .map(|(relative, source)| { let path = root.join(relative); Engine::default() - .extract_source_universal_candidate_evidence(&path, relative, source) - .map_err(|error| format!("candidate extraction failed for {relative}: {error}")) + .extract_source_universal_evidence(&path, relative, source) + .map_err(|error| format!("universal evidence extraction failed for {relative}: {error}")) }) .collect::, _>>()?; let esm_published = batches[0] @@ -1291,8 +1291,8 @@ unknown.direct(); .map(|(relative, source)| { let path = root.join(relative); Engine::default() - .extract_source_universal_candidate_evidence(&path, relative, source) - .map_err(|error| format!("candidate extraction failed for {relative}: {error}")) + .extract_source_universal_evidence(&path, relative, source) + .map_err(|error| format!("universal evidence extraction failed for {relative}: {error}")) }) .collect::, _>>()?; let base_inherited = batches[0] @@ -1408,8 +1408,8 @@ api.unknown(); .map(|(relative, source)| { let path = root.join(relative); Engine::default() - .extract_source_universal_candidate_evidence(&path, relative, source) - .map_err(|error| format!("candidate extraction failed for {relative}: {error}")) + .extract_source_universal_evidence(&path, relative, source) + .map_err(|error| format!("universal evidence extraction failed for {relative}: {error}")) }) .collect::, _>>()?; let run = batches[1] @@ -1504,8 +1504,8 @@ api.inherited(); .map(|(relative, source)| { let path = root.join(relative); Engine::default() - .extract_source_universal_candidate_evidence(&path, relative, source) - .map_err(|error| format!("candidate extraction failed for {relative}: {error}")) + .extract_source_universal_evidence(&path, relative, source) + .map_err(|error| format!("universal evidence extraction failed for {relative}: {error}")) }) .collect::, _>>()?; let inherited = batches[0] @@ -1568,12 +1568,12 @@ fn(); fs::write(path, source)?; } let batches = [ - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( &provider, "lib/callable.cjs", provider_source, )?, - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( &consumer, "app/consumer.js", consumer_source, diff --git a/crates/compass-resolve/tests/universal_resolution/ruby.rs b/crates/compass-resolve/tests/universal_resolution/ruby.rs index ecf54456..6e25cf76 100644 --- a/crates/compass-resolve/tests/universal_resolution/ruby.rs +++ b/crates/compass-resolve/tests/universal_resolution/ruby.rs @@ -2,7 +2,7 @@ use compass_resolve::evidence::{ResolutionDecision, ResolutionRule}; fn extract_ruby(path: &str, source: &[u8]) -> compass_languages::SemanticEvidenceBatch { Engine::default() - .extract_source_universal_candidate_evidence(Path::new(path), path, source) + .extract_source_universal_evidence(Path::new(path), path, source) .expect("Ruby universal evidence") } diff --git a/crates/compass-resolve/tests/universal_resolution/typescript.rs b/crates/compass-resolve/tests/universal_resolution/typescript.rs index f58125d3..287c2c73 100644 --- a/crates/compass-resolve/tests/universal_resolution/typescript.rs +++ b/crates/compass-resolve/tests/universal_resolution/typescript.rs @@ -967,7 +967,7 @@ export const value = api; } #[test] -fn typescript_candidate_preserves_dynamic_member_as_unresolved() { +fn typescript_universal_evidence_preserves_dynamic_member_as_unresolved() { let source = br#"class Known { run() {} } const value = getValue(); value.run(); @@ -976,12 +976,12 @@ function exact(value: string) {} exact(); "#; let batch = Engine::default() - .extract_source_universal_candidate_evidence( + .extract_source_universal_evidence( Path::new("src/dynamic.ts"), "src/dynamic.ts", source, ) - .expect("candidate evidence"); + .expect("universal evidence"); let dynamic_id = batch .candidates .iter() @@ -1031,7 +1031,7 @@ exact(); } #[test] -fn typescript_candidate_resolves_typeof_import_query_as_module_dependency() +fn typescript_universal_evidence_resolves_typeof_import_query_as_module_dependency() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -1046,12 +1046,12 @@ type Plain = import("../lib/items").Item; "#; fs::write(&items_path, items_source)?; fs::write(&consumer_path, consumer_source)?; - let items_batch = Engine::default().extract_source_universal_candidate_evidence( + let items_batch = Engine::default().extract_source_universal_evidence( &items_path, "lib/items.ts", items_source, )?; - let consumer_batch = Engine::default().extract_source_universal_candidate_evidence( + let consumer_batch = Engine::default().extract_source_universal_evidence( &consumer_path, "src/types.ts", consumer_source, @@ -1098,7 +1098,7 @@ type Plain = import("../lib/items").Item; } #[test] -fn typescript_candidate_resolves_relative_and_default_imports_across_files() +fn typescript_universal_evidence_resolves_relative_and_default_imports_across_files() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -1138,8 +1138,8 @@ new DefaultWidget().run(); .map(|(relative, source)| { let path = root.join(relative); Engine::default() - .extract_source_universal_candidate_evidence(&path, relative, source) - .map_err(|error| format!("candidate extraction failed for {relative}: {error}")) + .extract_source_universal_evidence(&path, relative, source) + .map_err(|error| format!("universal evidence extraction failed for {relative}: {error}")) }) .collect::, _>>()?; let widget = batches[0] @@ -1314,8 +1314,8 @@ cycleValue(); .map(|(relative, source)| { let path = root.join(relative); Engine::default() - .extract_source_universal_candidate_evidence(&path, relative, source) - .map_err(|error| format!("candidate extraction failed for {relative}: {error}")) + .extract_source_universal_evidence(&path, relative, source) + .map_err(|error| format!("universal evidence extraction failed for {relative}: {error}")) }) .collect::, _>>()?; let run = batches[0] @@ -1426,8 +1426,8 @@ object.run(3); .map(|(relative, source)| { let path = root.join(relative); Engine::default() - .extract_source_universal_candidate_evidence(&path, relative, source) - .map_err(|error| format!("candidate extraction failed for {relative}: {error}")) + .extract_source_universal_evidence(&path, relative, source) + .map_err(|error| format!("universal evidence extraction failed for {relative}: {error}")) }) .collect::, _>>()?; let run = batches[0] @@ -1523,8 +1523,8 @@ tags.sql`select ${42}`; .map(|(relative, source)| { let path = root.join(relative); Engine::default() - .extract_source_universal_candidate_evidence(&path, relative, source) - .map_err(|error| format!("candidate extraction failed for {relative}: {error}")) + .extract_source_universal_evidence(&path, relative, source) + .map_err(|error| format!("universal evidence extraction failed for {relative}: {error}")) }) .collect::, _>>()?; let sql = batches[0] @@ -1610,8 +1610,8 @@ class Admin {} .map(|(relative, source)| { let path = root.join(relative); Engine::default() - .extract_source_universal_candidate_evidence(&path, relative, source) - .map_err(|error| format!("candidate extraction failed for {relative}: {error}")) + .extract_source_universal_evidence(&path, relative, source) + .map_err(|error| format!("universal evidence extraction failed for {relative}: {error}")) }) .collect::, _>>()?; let controller = batches[0] @@ -1665,7 +1665,7 @@ class Admin {} } #[test] -fn typescript_candidate_does_not_use_terminal_name_for_relative_imports() +fn typescript_universal_evidence_does_not_use_terminal_name_for_relative_imports() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -1697,7 +1697,7 @@ new Widget(); fs::write(&path, source)?; batches.push( Engine::default() - .extract_source_universal_candidate_evidence(&path, relative, source)?, + .extract_source_universal_evidence(&path, relative, source)?, ); } let app_widget = batches[0] @@ -1731,7 +1731,7 @@ new Widget(); } #[test] -fn typescript_candidate_resolves_exact_javascript_interop() -> Result<(), Box> +fn typescript_universal_evidence_resolves_exact_javascript_interop() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -1757,7 +1757,7 @@ new Runtime().run(); fs::write(&path, source)?; batches.push( Engine::default() - .extract_source_universal_candidate_evidence(&path, relative, source)?, + .extract_source_universal_evidence(&path, relative, source)?, ); } let runtime = batches[0] @@ -1803,7 +1803,7 @@ new Runtime().run(); } #[test] -fn typescript_candidate_follows_cross_file_reexport_aliases() +fn typescript_universal_evidence_follows_cross_file_reexport_aliases() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -1835,7 +1835,7 @@ new PublicWidget().run(); fs::write(&path, source)?; batches.push( Engine::default() - .extract_source_universal_candidate_evidence(&path, relative, source)?, + .extract_source_universal_evidence(&path, relative, source)?, ); } let widget = batches[0] @@ -1880,7 +1880,7 @@ new PublicWidget().run(); } #[test] -fn typescript_candidate_keeps_duplicate_module_realizations_ambiguous() +fn typescript_universal_evidence_keeps_duplicate_module_realizations_ambiguous() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -1912,7 +1912,7 @@ new Widget(); fs::write(&path, source)?; batches.push( Engine::default() - .extract_source_universal_candidate_evidence(&path, relative, source)?, + .extract_source_universal_evidence(&path, relative, source)?, ); } let construct = batches[2] @@ -1934,7 +1934,7 @@ new Widget(); } #[test] -fn typescript_candidate_consumes_project_path_targets_in_shared_resolution() +fn typescript_universal_evidence_consumes_project_path_targets_in_shared_resolution() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -1974,7 +1974,7 @@ new Widget().run(); let mut extraction = extract(relative, source); if relative.ends_with(".ts") { extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -2026,7 +2026,7 @@ new Widget().run(); } #[test] -fn typescript_candidate_merges_imported_interface_members_across_declarations() +fn typescript_universal_evidence_merges_imported_interface_members_across_declarations() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -2055,7 +2055,7 @@ export function use(config: Config) { config.inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -2089,7 +2089,7 @@ export function use(config: Config) { config.inspect(); } } #[test] -fn typescript_candidate_leaves_duplicate_merged_interface_members_ambiguous() +fn typescript_universal_evidence_leaves_duplicate_merged_interface_members_ambiguous() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -2118,7 +2118,7 @@ export function use(config: Config) { config.inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -2139,7 +2139,7 @@ export function use(config: Config) { config.inspect(); } } #[test] -fn typescript_candidate_resolves_imported_generic_member_chain() +fn typescript_universal_evidence_resolves_imported_generic_member_chain() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -2174,7 +2174,7 @@ export function use(box: Box) { box.item.inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -2203,7 +2203,7 @@ export function use(box: Box) { box.item.inspect(); } } #[test] -fn typescript_candidate_resolves_nested_imported_generic_member_chain() +fn typescript_universal_evidence_resolves_nested_imported_generic_member_chain() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -2239,7 +2239,7 @@ export function use(box: Box>) { box.item.value.inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -2287,7 +2287,7 @@ export function use(box: Box>) { box.item.value.inspect(); } } #[test] -fn typescript_candidate_keeps_nested_generic_member_ambiguity_unresolved() +fn typescript_universal_evidence_keeps_nested_generic_member_ambiguity_unresolved() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -2324,7 +2324,7 @@ export function use(box: Box>) { box.item.value.inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -2345,7 +2345,7 @@ export function use(box: Box>) { box.item.value.inspect(); } } #[test] -fn typescript_candidate_does_not_invent_nested_generic_primitive_members() +fn typescript_universal_evidence_does_not_invent_nested_generic_primitive_members() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -2373,7 +2373,7 @@ export function use(box: Box) { box.item.inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -2396,7 +2396,7 @@ export function use(box: Box) { box.item.inspect(); } } #[test] -fn typescript_candidate_resolves_imported_generic_object_type_alias_members() +fn typescript_universal_evidence_resolves_imported_generic_object_type_alias_members() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -2431,7 +2431,7 @@ export function use(box: Boxed) { box.value.inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -2460,7 +2460,7 @@ export function use(box: Boxed) { box.value.inspect(); } } #[test] -fn typescript_candidate_resolves_imported_nominal_generic_type_alias_members() +fn typescript_universal_evidence_resolves_imported_nominal_generic_type_alias_members() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -2502,7 +2502,7 @@ export function use(box: Alias) { box.value.inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -2531,7 +2531,7 @@ export function use(box: Alias) { box.value.inspect(); } } #[test] -fn typescript_candidate_resolves_imported_homomorphic_mapped_alias_members() +fn typescript_universal_evidence_resolves_imported_homomorphic_mapped_alias_members() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -2566,7 +2566,7 @@ export function use(value: Copy) { value.inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -2595,7 +2595,7 @@ export function use(value: Copy) { value.inspect(); } } #[test] -fn typescript_candidate_publishes_local_conditional_branch_members() +fn typescript_universal_evidence_publishes_local_conditional_branch_members() -> Result<(), Box> { let source = br#"class Item { inspect(): void {} } class Other { other(): void {} } @@ -2609,7 +2609,7 @@ function object(value: ChooseObject) { value.inspect(); } "#; let mut extraction = extract("src/conditional.ts", source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new("src/conditional.ts"), "src/conditional.ts", source, @@ -2649,7 +2649,7 @@ function object(value: ChooseObject) { value.inspect(); } } #[test] -fn typescript_candidate_publishes_literal_indexed_alias_member_calls() +fn typescript_universal_evidence_publishes_literal_indexed_alias_member_calls() -> Result<(), Box> { let source = br#"interface Nested { inspect(): void } interface Item { nested: Nested } @@ -2659,7 +2659,7 @@ export function use(value: NestedAlias) { value.inspect(); } let relative = "src/indexed-alias.ts"; let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -2697,7 +2697,7 @@ export function use(value: NestedAlias) { value.inspect(); } } #[test] -fn typescript_candidate_resolves_imported_generic_indexed_alias_member_calls() +fn typescript_universal_evidence_resolves_imported_generic_indexed_alias_member_calls() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -2733,7 +2733,7 @@ export function use(value: NestedOf) { value.inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -2766,7 +2766,7 @@ export function use(value: NestedOf) { value.inspect(); } } #[test] -fn typescript_candidate_resolves_imported_keyof_identity_alias_members() +fn typescript_universal_evidence_resolves_imported_keyof_identity_alias_members() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -2803,7 +2803,7 @@ export function rejected(value: Empty) { value.inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -2836,7 +2836,7 @@ export function rejected(value: Empty) { value.inspect(); } } #[test] -fn typescript_candidate_resolves_imported_literal_utility_projection_members() +fn typescript_universal_evidence_resolves_imported_literal_utility_projection_members() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -2880,7 +2880,7 @@ export function use(picked: Picked, omitted: Omitted) { sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -2928,7 +2928,7 @@ export function use(picked: Picked, omitted: Omitted) { } #[test] -fn typescript_candidate_resolves_imported_array_and_tuple_member_chains() +fn typescript_universal_evidence_resolves_imported_array_and_tuple_member_chains() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -2976,7 +2976,7 @@ export function use(values: Item[], box: Box) { sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -3018,7 +3018,7 @@ export function use(values: Item[], box: Box) { } #[test] -fn typescript_candidate_resolves_generic_callable_return_member_chain() +fn typescript_universal_evidence_resolves_generic_callable_return_member_chain() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -3032,7 +3032,7 @@ export function use() { identity(new Item()).inspect(); } fs::write(&path, source)?; let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -3069,7 +3069,7 @@ export function use() { identity(new Item()).inspect(); } } #[test] -fn typescript_candidate_resolves_imported_callable_return_member_chains() +fn typescript_universal_evidence_resolves_imported_callable_return_member_chains() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -3114,7 +3114,7 @@ export function use(value: Item) { sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -3152,7 +3152,7 @@ export function use(value: Item) { } #[test] -fn typescript_candidate_keeps_imported_callable_return_ambiguity_unresolved() +fn typescript_universal_evidence_keeps_imported_callable_return_ambiguity_unresolved() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -3189,7 +3189,7 @@ export function use(value: Item) { make(value).inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -3212,7 +3212,7 @@ export function use(value: Item) { make(value).inspect(); } } #[test] -fn typescript_candidate_resolves_imported_callable_member_returns_and_explicit_generics() +fn typescript_universal_evidence_resolves_imported_callable_member_returns_and_explicit_generics() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -3256,7 +3256,7 @@ export function use(value: Item) { sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -3294,7 +3294,7 @@ export function use(value: Item) { } #[test] -fn typescript_candidate_keeps_ambiguous_imported_callable_member_returns_unresolved() +fn typescript_universal_evidence_keeps_ambiguous_imported_callable_member_returns_unresolved() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -3335,7 +3335,7 @@ export function use(value: Item) { Factory.make(value).inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -3358,7 +3358,7 @@ export function use(value: Item) { Factory.make(value).inspect(); } } #[test] -fn typescript_candidate_resolves_imported_callable_properties_and_typed_objects() +fn typescript_universal_evidence_resolves_imported_callable_properties_and_typed_objects() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -3403,7 +3403,7 @@ export function use(value: Item) { sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -3454,7 +3454,7 @@ export function use(value: Item) { } #[test] -fn typescript_candidate_keeps_duplicate_imported_callable_properties_unresolved() +fn typescript_universal_evidence_keeps_duplicate_imported_callable_properties_unresolved() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -3493,7 +3493,7 @@ export function use(value: Item) { api.make(value).inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -3516,7 +3516,7 @@ export function use(value: Item) { api.make(value).inspect(); } } #[test] -fn typescript_candidate_selects_unique_imported_overload_by_argument_type() +fn typescript_universal_evidence_selects_unique_imported_overload_by_argument_type() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -3570,7 +3570,7 @@ export function use(value: Item) { sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -3627,7 +3627,7 @@ export function use(value: Item) { } #[test] -fn typescript_candidate_keeps_imported_overload_ambiguity_and_mismatch_unresolved() +fn typescript_universal_evidence_keeps_imported_overload_ambiguity_and_mismatch_unresolved() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -3678,7 +3678,7 @@ export function use(value: Item) { sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -3701,7 +3701,7 @@ export function use(value: Item) { } #[test] -fn typescript_candidate_resolves_imported_index_signature_member_chain() +fn typescript_universal_evidence_resolves_imported_index_signature_member_chain() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -3736,7 +3736,7 @@ export function use(shape: Shape, key: string) { shape[key].inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -3765,7 +3765,7 @@ export function use(shape: Shape, key: string) { shape[key].inspect(); } } #[test] -fn typescript_candidate_resolves_imported_structural_index_signature_alias_member_chain() +fn typescript_universal_evidence_resolves_imported_structural_index_signature_alias_member_chain() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -3800,7 +3800,7 @@ export function use(shape: Shape, key: string) { shape[key].inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -3829,7 +3829,7 @@ export function use(shape: Shape, key: string) { shape[key].inspect(); } } #[test] -fn typescript_candidate_resolves_inline_structural_index_signature_member_chain() +fn typescript_universal_evidence_resolves_inline_structural_index_signature_member_chain() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -3868,7 +3868,7 @@ export function rejectedAmbiguous(shape: { [key: string]: Item | string }, key: sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -3898,7 +3898,7 @@ export function rejectedAmbiguous(shape: { [key: string]: Item | string }, key: } #[test] -fn typescript_candidate_resolves_imported_generic_index_signature_member_chain() +fn typescript_universal_evidence_resolves_imported_generic_index_signature_member_chain() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -3933,7 +3933,7 @@ export function use(shape: Shape, key: string) { shape[key].inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -3962,7 +3962,7 @@ export function use(shape: Shape, key: string) { shape[key].inspect(); } } #[test] -fn typescript_candidate_keeps_imported_index_signature_ambiguity_unresolved() +fn typescript_universal_evidence_keeps_imported_index_signature_ambiguity_unresolved() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -3998,7 +3998,7 @@ export function use(shape: Shape, key: string) { shape[key].inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -4021,7 +4021,7 @@ export function use(shape: Shape, key: string) { shape[key].inspect(); } } #[test] -fn typescript_candidate_does_not_invent_imported_index_signature_primitive_members() +fn typescript_universal_evidence_does_not_invent_imported_index_signature_primitive_members() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -4049,7 +4049,7 @@ export function use(shape: Shape, key: string) { shape[key].inspect(); } sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -4072,7 +4072,7 @@ export function use(shape: Shape, key: string) { shape[key].inspect(); } } #[test] -fn typescript_candidate_resolves_straight_line_reassignment_to_latest_member() +fn typescript_universal_evidence_resolves_straight_line_reassignment_to_latest_member() -> Result<(), Box> { let directory = tempfile::tempdir()?; let root = directory.path(); @@ -4092,7 +4092,7 @@ export function use() { sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, @@ -4158,7 +4158,7 @@ consume(handlers[0]); sources.insert(relative.to_owned(), String::from_utf8(source.to_vec())?); let mut extraction = extract(relative, source); extraction.semantic_evidence = Some( - Engine::default().extract_source_universal_candidate_evidence( + Engine::default().extract_source_universal_evidence( Path::new(relative), relative, source, diff --git a/docs/design/architecture.md b/docs/design/architecture.md index 965a4c60..68aaaded 100644 --- a/docs/design/architecture.md +++ b/docs/design/architecture.md @@ -114,12 +114,12 @@ The vendored tree-sitter language pack supplies deterministic parser definitions and queries. The [language architecture](language-architecture.md) separates this grammar -substrate from adapter-local semantic policy and universal evidence. Python, -Go, Rust, and Java have hard-cut to the shared evidence and resolution path on -this branch. Rust has passed its Phase 2 qualification; Java is registered as -a version-1 `UniversalCandidate` while its pinned-corpus qualification is -completed. Other registered languages keep their established extraction paths -until their own independently qualified transitions. +substrate from producer-local semantic policy and universal evidence. Python, +Go, Rust, Java, PHP, Kotlin, Ruby, TypeScript, and JavaScript use the shared +evidence and resolution path on this branch. Their registry entries are +`UniversalEvidencePipeline` values with an explicit `Qualifying` or `Qualified` +audit state. Other registered languages keep their established extraction +paths until their own independently qualified transitions. ### `compass-resolve` @@ -127,7 +127,7 @@ Owns cross-file resolution over extraction facts: - import target canonicalization; - universal declaration, scope, binding, occurrence, and candidate indexes; -- shared projection for hard-cut language adapters; +- shared projection for hard-cut language producers; - JavaScript/TypeScript re-export handling; - cross-file calls; - language member-call facts; @@ -142,7 +142,7 @@ making query or rendering decisions. Universal evidence is inside the deterministic construction layer. It is not a parser replacement, a graph database, or a second semantic-provider path. -The vendored package prepares syntax; a language adapter records source-backed +The vendored package prepares syntax; a language producer records source-backed facts; `compass-resolve` selects defensible project-wide targets; and the shared projector creates Code Graph v1 records. @@ -158,17 +158,17 @@ compass-languages: source registry +---------------- established language route ------------------+ | | v v -vendored static Tree-sitter grammar established adapter +vendored static Tree-sitter grammar established extraction | | v v one prepared AST graph facts/call facts | | v | -universal adapter policy | +universal evidence pipeline | Python | Go | Rust | Java | | | v | -SemanticEvidenceBatch v1 | +SemanticEvidenceBatch v2 | | | +--------------------------+-----------------------------------+ v @@ -181,7 +181,7 @@ compass-graph: normalize, deduplicate, cluster, analyze graph.json | cache | history | query surfaces ``` -The registry chooses one publication route per language. A hard-cut adapter +The registry chooses one publication route per language. A hard-cut pipeline does not dual-run its replaced publisher and does not translate old graph records into evidence. The established route remains a first-class production architecture for languages that have not yet made their own transition. diff --git a/docs/design/java-jdt-integration.md b/docs/design/java-jdt-integration.md index 76fc3797..321065bc 100644 --- a/docs/design/java-jdt-integration.md +++ b/docs/design/java-jdt-integration.md @@ -46,7 +46,8 @@ dependencies, or executes Maven or Gradle merely because Java files exist. ### Available now -Java is a version-1 `UniversalCandidate`. Its Tree-sitter adapter emits +Java is a version-3 `Qualifying` universal evidence pipeline. Its Tree-sitter +producer emits source-backed declarations, scopes, packages, imports, annotations, inheritance, type references, method and constructor candidates, receiver spelling, argument counts, ownership, and external-reference evidence. @@ -113,7 +114,7 @@ watch-mode provider, not the first batch implementation. possible. - Publishing JDT AST nodes as a second, competing Java graph. - Adding network access to historical materialization. -- Promoting Java to `UniversalComplete` solely because a provider exists. +- Marking Java `Qualified` solely because a provider exists. ## User experience @@ -1041,7 +1042,7 @@ watch fixtures and local protocol doubles - Introduce `may_dispatch_to` only with a documented consumer and graph contract. - Qualify structural-only, SCIP, JDT Core, and JDT LS profiles independently. -- Re-evaluate Java `UniversalCandidate` status only after all claimed +- Re-evaluate Java `Qualifying` status only after all claimed capabilities pass their gates. **Primary areas** diff --git a/docs/design/language-architecture.md b/docs/design/language-architecture.md index 524df068..940ef4f8 100644 --- a/docs/design/language-architecture.md +++ b/docs/design/language-architecture.md @@ -5,14 +5,14 @@ meta: navLabel: Language Architecture category: Design overview: Conceptual map of the Compass language pipeline and its independently qualified transitions. - goal: Explain the grammar, adapter, evidence, resolution, and graph-publication boundaries. + goal: Explain the grammar, evidence-producer, resolution, and graph-publication boundaries. audience: - Compass contributors - technical evaluators contentPlan: - current and planned status - layer ownership - - direct and universal adapter profiles + - direct and universal evidence pipeline states - language-by-language transition policy - quality and failure boundaries openQuestions: [] @@ -20,7 +20,7 @@ meta: # How Compass turns source into universal evidence -Compass separates parsing from semantic interpretation and graph publication. The vendored language pack supplies pinned parsers, language adapters interpret syntax, and the universal evidence framework gives independently qualified languages one resolution and publication path. +Compass separates parsing from semantic interpretation and graph publication. The vendored language pack supplies pinned parsers, language-specific evidence producers interpret syntax, and the universal evidence pipeline gives transitioned languages one resolution and publication path. ## Current and planned status @@ -29,13 +29,13 @@ This architecture is transitioning one language at a time. The status labels bel | Status | Behavior | | --- | --- | | Available now | The vendored package supplies 37 pinned static Tree-sitter grammars | -| Available now | Python and Go are registered hard-cut adapters: they emit semantic evidence and use shared resolution and projection | -| Available now | Rust is a quality-gated, hard-cut version-15 `UniversalCandidate`; version 15 preserves bounded multi-stage method-result chains across files while retaining source-proven fallbacks when project-wide result evidence is absent, alongside the earlier associated-type, generic-parameter, re-export, and lexical-call safeguards; replaced publisher and collection resolution branches remain removed | -| Available now | Java is a hard-cut version-3 `UniversalCandidate`; its replaced publisher and Java member resolver are removed, and post-cutover pinned-corpus qualification is complete | -| Available now | TypeScript and JavaScript are hard-cut `UniversalCandidate` adapters; TSX uses the TypeScript identity, both share the bounded ECMAScript evidence emitter, and their replaced generic publisher is removed | -| Available now | PHP is a hard-cut version-1 `UniversalCandidate` with explicit case-insensitive type/function/method identity, bounded Composer PSR-4 evidence, conservative trait/inheritance dispatch, and universal Laravel/Drupal source packs; Drupal configuration and Blade template extraction remain available | -| Available now | Kotlin is a hard-cut version-1 `UniversalCandidate` with packages, imports, nominal and companion declarations, constructors, functions and extensions, properties, annotations, generic and nullable types, and named/default argument evidence; its complete quality audit remains open | -| Available now | Ruby is a hard-cut version-1 `UniversalCandidate`; its dedicated evidence emitter, method-space-aware resolver policy, replaced Ruby member publisher, and Rails `rails-ruby` universal pack are active while Plan 019 audit gates remain open | +| Available now | Python, Go, Rust, Java, PHP, Kotlin, Ruby, TypeScript, and JavaScript are registered hard-cut evidence pipelines: they emit semantic evidence and use shared resolution and projection | +| Available now | Rust is a quality-gated, hard-cut version-15 `Qualifying` pipeline; version 15 preserves bounded multi-stage method-result chains across files while retaining source-proven fallbacks when project-wide result evidence is absent, alongside the earlier associated-type, generic-parameter, re-export, and lexical-call safeguards; replaced publisher and collection resolution branches remain removed | +| Available now | Java is a hard-cut version-3 `Qualifying` pipeline; its replaced publisher and Java member resolver are removed, and post-cutover pinned-corpus qualification is complete | +| Available now | TypeScript and JavaScript are hard-cut `Qualifying` pipelines; TSX uses the TypeScript identity, both share the bounded ECMAScript producer, and their replaced generic publisher is removed | +| Available now | PHP is a hard-cut version-1 `Qualifying` pipeline with explicit case-insensitive type/function/method identity, bounded Composer PSR-4 evidence, conservative trait/inheritance dispatch, and universal Laravel/Drupal source packs; Drupal configuration and Blade template extraction remain available | +| Available now | Kotlin is a hard-cut version-1 `Qualifying` pipeline with packages, imports, nominal and companion declarations, constructors, functions and extensions, properties, annotations, generic and nullable types, and named/default argument evidence; its complete quality audit remains open | +| Available now | Ruby is a hard-cut version-1 `Qualifying` pipeline; its dedicated producer, method-space-aware resolver policy, replaced Ruby member publisher, and Rails `rails-ruby` universal pack are active while Plan 019 audit gates remain open | | Available now | The remaining production languages keep their established extraction and resolution paths | | Planned | Later languages transition independently after language-specific qualification | @@ -54,7 +54,7 @@ language. files + project manifests | v - source / adapter registry + source / evidence registry | +--> source-driven producer ------------------------------+ | | @@ -64,16 +64,16 @@ files + project manifests one Tree-sitter AST | | | v | - adapter-local semantics | + producer-local semantics | | | +----------+-----------+ | | | | v v | - hard-cut adapter established adapter | + hard-cut pipeline established extraction | Python / Go / Rust / Java remaining languages | | | | v v | - SemanticEvidenceBatch v1 graph facts + unresolved calls | + SemanticEvidenceBatch v2 graph facts + unresolved calls | | | | v v | universal indexes existing collection resolvers | @@ -97,13 +97,13 @@ files + project manifests Framework packs consume normalized declarations and exact occurrences after language resolution. They do not reinterpret malformed language evidence. -The hard-cut route is selected by `AdapterRegistry`. Presence in the source +The hard-cut route is selected by `UniversalEvidenceRegistry`. Presence in the source registry or availability of a grammar does not select it. On the current -branch, the registry contains `go`, `java`, `python`, and `rust`. Go is at -adapter version 3, Java is at version 3, Python is at version 11, and Rust is at -version 15. Candidate status -describes qualification maturity; it does not re-enable the removed direct -route. Adapter-version changes invalidate cached evidence for only the changed +branch, the registry contains C#, Go, Java, JavaScript, Kotlin, PHP, Python, +Ruby, Rust, and TypeScript. Go and Java are at producer version 3, Python is at +version 11, and Rust is at version 15. `Qualifying` describes audit maturity; +it does not re-enable the removed direct route. Producer-version changes +invalidate cached evidence for only the changed language. Go identities retain the repository-relative directory prefix and use the parsed package clause to distinguish external test packages. @@ -122,7 +122,7 @@ It owns: It does not own: -- Compass adapter profiles +- Compass evidence producers and pipelines - semantic capabilities - declaration or relationship identity - cross-file resolution @@ -147,7 +147,7 @@ implementations. | --- | --- | --- | --- | --- | | Vendored `compass-tree-sitter-language-pack` | Parser and Tree-sitter AST | No | No | Add or update a pinned grammar | | CodeGraph kernel language module | Direct row-buffer nodes, edges, and references | Yes | Only the kernel's direct contracts | Add or modify a complete per-language publisher | -| Compass universal evidence adapter | Declarations, scopes, bindings, occurrences, and candidates | Syntax-to-evidence policy only | No; shared resolver owns it | Add language policy without changing the central publisher | +| Compass universal evidence pipeline | Declarations, scopes, bindings, occurrences, and candidates | Syntax-to-evidence policy only | No; shared resolver owns it | Add a producer without changing the central publisher | The CodeGraph kernel's files such as `python.rs` and `java.rs` are specialized, direct publishers. They parse, walk, normalize, and emit the kernel's graph @@ -159,7 +159,7 @@ language contributes syntax classification and truthful capabilities while reusing target selection, ambiguity policy, graph projection, provenance, limits, and conformance gates. This does not make the vendored grammar pack obsolete: the pack remains the reproducible parser substrate under both the -hard-cut and established adapter routes. +hard-cut and established extraction routes. ## Source and producer registry @@ -169,7 +169,7 @@ A producer descriptor records: - source identity and extractor kind - an optional grammar requirement -- adapter ID, version, and profile +- producer ID, version, and qualification state - declared semantic capabilities - evidence and resolution budgets @@ -180,14 +180,15 @@ The architecture distinguishes five states: 1. grammar available 2. source recognized 3. established extraction available -4. universal candidate -5. universal complete +4. universal evidence pipeline is qualifying +5. universal evidence pipeline is qualified Grammar availability does not imply complete semantic support. -## Adapter-local semantic policy +## Producer-local semantic policy -Each adapter owns the syntax rules and identity normalization unique to its language. +Each language producer owns the syntax rules and identity normalization unique +to its language. Examples include: @@ -211,19 +212,20 @@ renderer support, compatibility review, and qualification in the same change. Until then, unsupported implementation owners remain unresolved rather than being assigned a convenient but false identity. -An AST-backed adapter receives borrowed source bytes and one prepared tree. It does not parse the source again or call the language pack's generic intelligence pipeline. +An AST-backed producer receives borrowed source bytes and one prepared tree. It +does not parse the source again or call the language pack's generic intelligence +pipeline. -After a language enters its universal transition, its adapter emits evidence +After a language enters its universal transition, its producer emits evidence only. The shared projector creates graph nodes and edges for both single-file -and collection builds. `UniversalCandidate` means this route is active while -qualification continues; it is not permission to retain the replaced direct -publisher. +and collection builds. `Qualifying` means this route is active while the audit +continues; it is not permission to retain the replaced direct publisher. ## Universal evidence -Universal evidence records what the adapter can prove before project-wide resolution. +Universal evidence records what the producer can prove before project-wide resolution. -The version-1 contract contains: +The version-2 contract contains: | Evidence | Purpose | | --- | --- | @@ -329,30 +331,30 @@ failure policy. No such analyzer is invoked by the current build pipeline; it requires its own process isolation, limits, freshness contract, and qualification before it can become a shipped provider. -## Established and universal profiles +## Established route and universal pipeline states -Profiles describe publication architecture, not implementation quality. +Pipeline states describe qualification maturity, not a second implementation. -| Profile | Meaning | +| State | Meaning | | --- | --- | -| `Direct` | The established adapter publishes its current graph and unresolved-call records | -| `UniversalCandidate` | The hard-cut adapter publishes universal evidence and is being qualified against candidate gates | -| `UniversalComplete` | The adapter has passed the complete capability and conformance gates | +| `Direct` | The established language-specific extractor publishes its current graph and unresolved-call records | +| `Qualifying` | The hard-cut `UniversalEvidencePipeline` publishes universal evidence while capability and corpus audits run | +| `Qualified` | The same pipeline has passed the complete capability and conformance gates | Documentation uses **established** or `Direct` for the active non-universal route. A historical internal enum variant still uses the name `Legacy`; that implementation detail is not a quality label and must not be used to describe -supported languages. +supported pipelines. ## Language-by-language transitions An established language keeps its direct implementation until its universal -candidate proves the transition is safe. C#, TypeScript, and JavaScript have -completed the route switch and remain candidates while their completion gates -run. +pipeline proves the transition is safe. C#, TypeScript, and JavaScript have +completed the route switch and remain `Qualifying` while their completion +gates run. ```text -established adapter baseline +established extraction baseline | v implement universal evidence policy @@ -360,7 +362,7 @@ implement universal evidence policy v fixture and real-corpus qualification | - +--> gate fails: improve candidate + +--> gate fails: improve the producer | v atomic language transition @@ -428,7 +430,7 @@ Required properties include: - ownership-based corpus merge - separate parse, evidence, resolution, projection, and persistence timings -Rust and Java qualification requires Compass to remain faster than Graphify for comparable cold and warm workloads. Peak resident set size (RSS) remains measured but non-blocking during their candidate phases. +Rust and Java qualification requires Compass to remain faster than Graphify for comparable cold and warm workloads. Peak resident set size (RSS) remains measured but non-blocking during their qualifying audits. ## Related pages @@ -438,4 +440,4 @@ Rust and Java qualification requires Compass to remain faster than Graphify for - [Extending Compass](../implementation/extending-compass.md) - [Rust and Java hard-cutover design](../superpowers/specs/2026-07-30-rust-java-universal-hard-cutover-design.md) -**Next step:** read the [universal evidence implementation architecture](../implementation/universal-evidence.md) before changing the language registry, an adapter, or the resolver. +**Next step:** read the [universal evidence implementation architecture](../implementation/universal-evidence.md) before changing the language registry, a producer, or the resolver. diff --git a/docs/design/managed-language-analyzers.md b/docs/design/managed-language-analyzers.md index c8146c6c..4c493212 100644 --- a/docs/design/managed-language-analyzers.md +++ b/docs/design/managed-language-analyzers.md @@ -60,7 +60,7 @@ decides whether exact structural evidence supports projection. ### Hard-cut universal languages -Python, Go, Rust, and Java are version-1 hard-cut universal adapters. They emit +Python, Go, Rust, and Java are hard-cut universal evidence pipelines. They emit `SemanticEvidenceBatch` facts and use shared resolution and projection. Their replaced direct graph paths are not available as fallbacks. @@ -71,21 +71,21 @@ replaced direct graph paths are not available as fallbacks. | Rust | namespaces, traits, impl ownership, macros, re-exports | trait selection, autoderef, associated items, macro hygiene, `cfg` | | Java | packages, overload signatures, receiver spelling, inheritance | compiler attribution, generics, overload selection, virtual dispatch | -Candidate or hard-cut status does not mean compiler-grade completeness. It -states which publication architecture is active and which qualification has +Qualifying or qualified status does not mean compiler-grade completeness. It +states which evidence pipeline is active and which qualification gates have been completed. -### TypeScript and JavaScript universal candidates +### TypeScript and JavaScript universal evidence pipelines -TypeScript, TSX, JavaScript, and JSX now use the registered universal candidate -route. TypeScript and JavaScript retain distinct adapter identities while +TypeScript, TSX, JavaScript, and JSX now use the registered universal evidence +pipeline. TypeScript and JavaScript retain distinct producer identities while sharing one bounded source-grounded ECMAScript emitter; TSX is the TypeScript parser dialect. Their replaced direct graph publisher is not a fallback. -Candidate status means the universal route is active while the complete +`Qualifying` means the universal route is active while the complete qualification matrix and any compiler-backed enrichment remain future work. Compiler enrichment must not create a permanent third graph route beside the -candidate path. Until a compiler provider is independently bounded and +evidence pipeline. Until a compiler provider is independently bounded and qualified, compiler facts remain Program-only. ### Program and SCIP support @@ -593,10 +593,10 @@ under the same process and network bounds as the Go toolchain. ### TypeScript -TypeScript and TSX use version-1 universal candidate evidence and have their -replaced publication/resolution branches removed. Promotion to a complete -adapter still requires the conformance and corpus gates below; analyzer facts -remain Program-only until a separate provider is qualified. +TypeScript and TSX use the registered universal evidence pipeline and have +their replaced publication/resolution branches removed. A `Qualified` status +still requires the conformance and corpus gates below; analyzer facts remain +Program-only until a separate provider is qualified. Use verified `scip-typescript` first. Build a pinned TypeScript Compiler API bridge for controlled batch analysis and BuilderProgram incrementality. @@ -625,8 +625,9 @@ callbacks, properties, JSX, and external declarations. ### JavaScript -JavaScript/JSX/MJS/CJS use the version-1 JavaScript universal candidate and -share the TypeScript emitter with distinct language policy and identity. +JavaScript/JSX/MJS/CJS use the JavaScript universal evidence pipeline and +share the TypeScript emitter with distinct language policy and producer +identity. Analyzer graph projection remains disabled until a provider is separately qualified. @@ -940,8 +941,8 @@ unrealizable. Published realizations remain immutable. | Milestone | Python | Rust | Go | TypeScript | JavaScript | Java | | --- | --- | --- | --- | --- | --- | --- | -| Native structural path | Available | Available | Available | Universal candidate | Universal candidate | Available | -| Universal hard cut | Available | Available | Available | Candidate route; qualification ongoing | Candidate route; qualification ongoing | Available | +| Native structural path | Available | Available | Available | Evidence pipeline | Evidence pipeline | Available | +| Universal hard cut | Available | Available | Available | Qualifying; audit ongoing | Qualifying; audit ongoing | Available | | Offline SCIP ingestion | Generic | Generic | Generic | Generic | Generic | Calls projected | | Managed artifact runner | Planned | Planned | Not selected | Planned | Planned | Planned | | Native batch analyzer | Optional | Optional | Go types | Compiler API | Compiler API | JDT Core | @@ -959,7 +960,7 @@ This matrix describes architecture status, not release scheduling. focused crates behind one trait. 3. Which tool distributions Compass may redistribute versus only discover. 4. Which SCIP modes can run without repository build execution. -5. How universal profiles divide TS/TSX/JS/JSX/MJS/CJS mixed projects. +5. How producer identities divide TS/TSX/JS/JSX/MJS/CJS mixed projects. 6. Which Program facts or graph relations require new schema majors. 7. Which external dependency identities belong in the graph versus Program. 8. Which metadata commands are safe enough for default use. diff --git a/docs/implementation/extending-compass.md b/docs/implementation/extending-compass.md index 802627fb..447c2b01 100644 --- a/docs/implementation/extending-compass.md +++ b/docs/implementation/extending-compass.md @@ -29,7 +29,7 @@ graph/result verification compass-graph / CLI fixtures ``` Read [Language architecture](../design/language-architecture.md) before -changing parser ownership or adapter profiles. Read +changing parser ownership or evidence pipeline registration. Read [Universal evidence implementation](universal-evidence.md) before starting an independently qualified language transition. Read [Evidence resolution framework technical design](evidence-resolution-framework-technical-design.md) diff --git a/docs/implementation/extraction-pipeline.md b/docs/implementation/extraction-pipeline.md index 12bce4cb..d6da83d5 100644 --- a/docs/implementation/extraction-pipeline.md +++ b/docs/implementation/extraction-pipeline.md @@ -126,9 +126,10 @@ local/project metadata: It should not pick one arbitrary project-wide target when evidence is ambiguous. -Established direct adapters publish their current graph records and unresolved -facts through this boundary. Independently qualified universal adapters emit -the versioned evidence contract and use shared projection instead. See +Established direct publishers publish their current graph records and +unresolved facts through this boundary. Independently qualified universal +evidence producers emit the versioned evidence contract and use shared +projection instead. See [Universal evidence implementation architecture](universal-evidence.md) for the current and planned boundaries. diff --git a/docs/implementation/kotlin-universal-qualification.md b/docs/implementation/kotlin-universal-qualification.md index f8bcd70e..840ac09f 100644 --- a/docs/implementation/kotlin-universal-qualification.md +++ b/docs/implementation/kotlin-universal-qualification.md @@ -1,22 +1,22 @@ --- meta: contentType: Reference - title: Kotlin universal candidate qualification + title: Kotlin universal qualification navLabel: Kotlin Qualification category: Implementation - overview: Reproducible baseline and candidate evidence for the Kotlin hard cut. - goal: Record what the Kotlin candidate has proved and which completion gates remain open. + overview: Reproducible baseline and qualifying evidence for the Kotlin hard cut. + goal: Record what the Kotlin pipeline has proved and which audit gates remain open. audience: - Compass language contributors - release reviewers openQuestions: [] --- -# Kotlin universal candidate qualification +# Kotlin universal qualification This record compares the established Kotlin publisher from Compass commit -`2db60035` with the version-1 universal candidate. It does not promote Kotlin -to `UniversalComplete`. +`2db60035` with the version-1 universal pipeline. It does not promote Kotlin +to `Qualified`. ## Pinned corpus @@ -86,5 +86,5 @@ Spring Kotlin route fixture qualifies the universal framework pack. The independent quality audit is still required. In particular, no claim is made that the minimum 2,000 accepted-relationship pool, precision/recall thresholds, or zero-tolerance critical judgments have passed. Kotlin must -remain `UniversalCandidate` until that audit and the remaining performance work +remain `Qualifying` until that audit and the remaining performance work complete. diff --git a/docs/implementation/ruby-universal-qualification.md b/docs/implementation/ruby-universal-qualification.md index 8c900030..45c20b78 100644 --- a/docs/implementation/ruby-universal-qualification.md +++ b/docs/implementation/ruby-universal-qualification.md @@ -1,9 +1,9 @@ -# Ruby universal-candidate qualification +# Ruby universal qualification Plan 019 is implemented through a single Ruby evidence path and is currently -kept at `UniversalCandidate`. The adapter identity is -`compass.ruby.candidate` (version 1, evidence schema v1); the complete quality -audit has not been claimed or used to promote the profile. +kept at `Qualifying`. The producer identity is +`compass.ruby` (producer version 1, evidence schema v2); the complete quality +audit has not been claimed or used to promote the pipeline to `Qualified`. ## Production contract @@ -103,8 +103,8 @@ and `typescript_workspace_package_exports_follow_nodenext_reexports`); all Ruby and Rails tests in that suite pass. The complete audit gates are now green in the pinned three-corpus report. Ruby -is still intentionally `UniversalCandidate`; passing qualification does not -automatically promote a language profile. +is still intentionally `Qualifying`; passing qualification does not +automatically promote a language pipeline. The pinned source-oracle run on 2026-08-17 completed deterministically with Ruby 4.0.6 / revision `03b6d3f8898a28604fe6cb00eae3226b821168f4`: @@ -125,7 +125,7 @@ The generated audit population contains 89,981 accepted relationships across the three corpora, with 100% observed precision, a 99.9957% Wilson lower bound, 98.5567% source-oracle recall, and zero critical violations. Every fixed qualification gate passes. Ruby is still intentionally kept at -`UniversalCandidate`; promotion is a separate product decision. +`Qualifying`; promotion is a separate product decision. | Capability | Accepted | Recall | Status | | --- | ---: | ---: | --- | @@ -136,7 +136,7 @@ qualification gate passes. Ruby is still intentionally kept at The machine-readable result is produced by `benchmarks/performance/harness.py audit`; no Graphify facts are used as truth. -Ruby remains `UniversalCandidate`. +Ruby remains `Qualifying`. The current real-repository captures (cold, no build time) are: @@ -156,7 +156,7 @@ match. The pinned Discourse Ruby-only five-warm-sample run also restores byte-for-byte (cold 211.296 s, warm median 3.017 s with samples from 2.996–3.214 s, fact-neutral 145.529 s, semantic 226.129 s, restore 226.958 s). RSS remains non-blocking. Ruby therefore remains -`UniversalCandidate`. +`Qualifying`. The fact-neutral delta also preserves unchanged files' extraction status, parser-recovery diagnostics, and per-file coverage while refreshing the edited diff --git a/docs/implementation/universal-evidence.md b/docs/implementation/universal-evidence.md index f5cfa665..579b67fc 100644 --- a/docs/implementation/universal-evidence.md +++ b/docs/implementation/universal-evidence.md @@ -5,7 +5,7 @@ meta: navLabel: Universal Evidence category: Implementation overview: Reference for the contracts that connect source extraction to normalized graph publication. - goal: Define the implementation boundaries and invariants for universal language adapters. + goal: Define the implementation boundaries and invariants for universal evidence pipelines. audience: - Compass language contributors - resolver contributors @@ -29,12 +29,12 @@ future work. | Status | Implementation | | --- | --- | -| Available now | `compass-languages` owns the source registry, parsers, established adapters, and semantic evidence version 1 | -| Available now | C#, Python, Go, Rust, Java, Kotlin, Ruby, TypeScript, and JavaScript are entries in the hard-cut `AdapterRegistry`; C#, Kotlin, and Ruby are at adapter version 1, Go and Java are at version 3, Python is at version 11, Rust is at version 15, and the ECMAScript candidates are at version 5 | -| Available now | `EvidenceBuilder` emits bounded `SemanticEvidenceBatch` values for all registered universal languages; C#, PHP, Kotlin, Ruby, and the ECMAScript family use dedicated source-grounded emitters, while TypeScript and JavaScript retain distinct adapter identities | +| Available now | `compass-languages` owns the source registry, parsers, established extractors, and universal evidence schema version 2 (extraction semantics version 3) | +| Available now | C#, PHP, Python, Go, Rust, Java, Kotlin, Ruby, TypeScript, and JavaScript are entries in the hard-cut `UniversalEvidenceRegistry`; each entry pairs a `UniversalEvidenceProducer` with a `UniversalEvidenceQualification` state | +| Available now | `EvidenceBuilder` emits bounded `SemanticEvidenceBatch` values for all registered universal languages; C#, PHP, Kotlin, Ruby, and the ECMAScript family use dedicated source-grounded producers, while TypeScript and JavaScript retain distinct producer identities | | Available now | `UniversalResolutionIndex` resolves and projects hard-cut evidence without a language-name branch | -| Available now | Rust has passed Phase 2 qualification; C#, Java, Kotlin, Ruby, TypeScript, and JavaScript remain `UniversalCandidate` while their respective completion gates run | -| Planned | `GrammarProvider`, grammar provenance, and producer-registry validation | +| Available now | Rust has passed its Phase 2 quality audit; all registered pipelines remain explicitly `Qualifying` until their complete independent audit gates promote them | +| Planned | `GrammarProvider` and grammar provenance | | Planned | Independently qualified hard cuts for the remaining registered languages | Do not treat a planned interface as a shipped public API until its implementation and qualification commits land. @@ -46,10 +46,10 @@ Each crate has one primary responsibility in the language pipeline. | Crate or package | Responsibility | | --- | --- | | `vendor/compass-tree-sitter-language-pack` | Pinned grammars, static linkage, parser creation, and grammar metadata | -| `compass-languages::registry` | Source recognition, producer selection, adapter descriptors, capabilities, and budgets | +| `compass-languages::registry` | Source recognition, producer selection, evidence pipelines, capabilities, and budgets | | `compass-languages::evidence` | Hard-cut semantic evidence model and bounded AST-backed builder | -| `compass-languages::adapters` | Hard-cut adapter profiles, version-1 capability declarations, and registry validation | -| Adapter modules | Language-specific syntax classification and identity normalization | +| `compass-languages::evidence_pipeline` | Universal evidence producers, qualification states, capability declarations, and registry validation | +| Evidence producers | Language-specific syntax classification and identity normalization | | `compass-resolve::evidence` | Collection indexes, fail-closed target selection, and shared graph projection | | Framework modules | Activation evidence, target constraints, occurrence policy, and framework relations | | `compass-graph` | Normalized graph construction, deduplication, clustering, and analysis | @@ -64,19 +64,19 @@ These files define the current implementation. | --- | --- | | [`vendor/compass-tree-sitter-language-pack/README.md`](../../vendor/compass-tree-sitter-language-pack/README.md) | Compass grammar-pack policy | | [`vendor/compass-tree-sitter-language-pack/build.rs`](../../vendor/compass-tree-sitter-language-pack/build.rs) | Static grammar selection and compilation | -| [`crates/compass-languages/src/registry.rs`](../../crates/compass-languages/src/registry.rs) | Source recognition and adapter profiles | -| [`crates/compass-languages/src/adapters.rs`](../../crates/compass-languages/src/adapters.rs) | Hard-cut language registry and capability declarations | -| [`crates/compass-languages/src/engine.rs`](../../crates/compass-languages/src/engine.rs) | Parsing, mutually exclusive adapter dispatch, and framework detection | +| [`crates/compass-languages/src/registry.rs`](../../crates/compass-languages/src/registry.rs) | Source recognition and evidence-pipeline selection | +| [`crates/compass-languages/src/evidence_pipeline.rs`](../../crates/compass-languages/src/evidence_pipeline.rs) | Hard-cut producer registry, qualification states, and capability declarations | +| [`crates/compass-languages/src/engine.rs`](../../crates/compass-languages/src/engine.rs) | Parsing, mutually exclusive evidence-pipeline dispatch, and framework detection | | [`crates/compass-languages/src/evidence/`](../../crates/compass-languages/src/evidence/) | Hard-cut evidence model, validation, limits, and AST builder | | [`crates/compass-languages/src/evidence/build.rs`](../../crates/compass-languages/src/evidence/build.rs) | AST-to-evidence policy for the hard-cut language set | | [`crates/compass-languages/src/facts.rs`](../../crates/compass-languages/src/facts.rs) | Per-file extraction container | | [`crates/compass-resolve/src/lib.rs`](../../crates/compass-resolve/src/lib.rs) | Collection merge and current resolution sequence | | [`crates/compass-resolve/src/evidence.rs`](../../crates/compass-resolve/src/evidence.rs) | Universal indexes, resolution rules, and shared materialization | -| [`crates/compass-resolve/src/members.rs`](../../crates/compass-resolve/src/members.rs) | Current direct-adapter member-call resolution | +| [`crates/compass-resolve/src/members.rs`](../../crates/compass-resolve/src/members.rs) | Current direct-extractor member-call resolution | ## Grammar provider boundary -The Compass-side grammar provider isolates adapters from the vendored implementation. +The Compass-side grammar provider isolates evidence producers from the vendored implementation. The provider must: @@ -88,7 +88,7 @@ The provider must: The provider returns immutable grammar provenance with the prepared parse tree. Grammar provenance participates in extraction cache identity. -An AST-backed adapter consumes: +An AST-backed producer consumes: ```text source path @@ -98,49 +98,66 @@ immutable grammar provenance producer descriptor ``` -A source-driven adapter consumes the path, source bytes, and producer descriptor without a grammar or tree. +A source-driven producer consumes the path, source bytes, and producer descriptor without a grammar or tree. -## Producer descriptor boundary +## Universal evidence producer and pipeline boundary The producer descriptor binds source recognition to extraction policy without putting semantics into the grammar pack. Its conceptual shape is: ```rust -struct ProducerDescriptor { - source_id: &'static str, - extractor_kind: ExtractorKind, - grammar: Option, - adapter: AdapterDescriptor, - limits: EvidenceLimits, +struct UniversalEvidenceProducer { + id: &'static str, + language: &'static str, + version: u32, + evidence_schema: &'static str, + capabilities: &'static [LanguageCapability], +} + +struct UniversalEvidencePipeline { + producer: UniversalEvidenceProducer, + qualification: UniversalEvidenceQualification, } ``` -This shape is illustrative until the planned registry work lands. The implementation may use equivalent focused types. +This is the shipped registry contract. Language-specific emitters are wired to +the selected pipeline in the same change that registers the language and +removes its replaced publisher or resolver path. Registry validation enforces: - one grammar requirement for every AST-backed producer - no grammar requirement for source-driven producers - static availability for every required grammar -- evidence schema version 1 for universal profiles -- unique source and adapter identities +- evidence schema version 2 for universal pipelines +- unique source and producer identities - bounded resource policies -## Adapter descriptor boundary +The producer owns language-specific metadata. The pipeline is the runtime +selection unit and carries the lifecycle state separately from producer +identity. This separation makes it possible to promote a producer from +`Qualifying` to `Qualified` without changing the extraction API or inventing a +second extractor name. + +## Producer metadata boundary -The adapter descriptor records semantic capability, not parser availability. +The producer metadata records semantic capability, not parser availability. -Version-1 descriptors contain: +Producer metadata contains: -- adapter ID +- producer ID - source language -- optional parser dialect provenance (for example `ts` or `tsx`) -- adapter version +- producer version - evidence schema -- publication profile - capability claims +The emitted pipeline identity may additionally preserve parser dialect +provenance (for example `ts` or `tsx`). + +The enclosing pipeline adds the qualification state (`Qualifying` or +`Qualified`) without changing producer identity. + The shared fact model also has additive optional identity metadata for TypeScript/JavaScript qualification: declarations and bindings may carry a value/type/namespace symbol-space tag, and import/re-export bindings may mark @@ -148,27 +165,29 @@ value/type/namespace symbol-space tag, and import/re-export bindings may mark identity component until a producer explicitly emits one. A type-only binding must use the type or namespace space and is rejected otherwise. -The TypeScript/JavaScript candidate also records exact module-specifier and +The TypeScript/JavaScript producer also records exact module-specifier and binding anchors for imports, resolves JSX member tags through proven namespace receivers, treats `this` and private member identifiers as nominal source evidence, and accepts only literal computed members. Dynamic member keys and unproven `super` receivers remain unresolved; these semantics are production -candidate evidence and remain subject to the completion qualification gates. +evidence and remain subject to the independent audit gates. -The architectural profile names are `Direct`, `UniversalCandidate`, and -`UniversalComplete`. A historical compatibility evidence type still serializes -an internal `legacy` variant. Treat it as an old wire identifier, not the name -or quality status of an established adapter. +The architecture names are `Direct` (the established language-specific route) +and `UniversalEvidencePipeline` (the shared route). The pipeline lifecycle +states are `Qualifying` and `Qualified`; neither state creates a second +extractor. A historical compatibility evidence type still serializes an +internal `legacy` variant. Treat it as an old wire identifier, not a current +pipeline state. -Go and Java are at adapter version 3, Python is at version 11, and Rust is at -version 15. Adapter versions advance when a semantic evidence change +Go and Java are at producer version 3, Python is at version 11, and Rust is at +version 15. Producer versions advance when a semantic evidence change requires language-local cache invalidation; grammar provenance and the extraction-semantics identity remain independent cache inputs. ## Evidence contract Each successfully prepared hard-cut source produces one -`SemanticEvidenceBatch`. Hard cutover alone does not increment an adapter +`SemanticEvidenceBatch`. Hard cutover alone does not increment a producer version, but later evidence-contract changes may do so to invalidate only that language's cached facts. @@ -191,7 +210,8 @@ The builder must: - deduplicate exact facts - mark incomplete batches -Adapters do not create graph nodes, graph edges, or `RawCall` records after their universal transition. +Universal evidence producers do not create graph nodes, graph edges, or +`RawCall` records after their pipeline is selected. ## End-to-end ownership map @@ -206,22 +226,22 @@ compass-files discovers and fingerprints source v compass-languages::Registry selects LanguageSpec | - +--> AdapterRegistry miss ------------------------------+ + +--> UniversalEvidenceRegistry miss ------------------------------+ | | | v | established extractor | | v v -AdapterRegistry hit graph/raw/language facts +UniversalEvidenceRegistry hit graph/raw/language facts | v vendored grammar -> one Tree-sitter AST | v -adapter-local AST policy +producer-local AST policy | v -EvidenceBuilder -> SemanticEvidenceBatch v1 +EvidenceBuilder -> SemanticEvidenceBatch v2 | +--------------------------+----------------------------+ v @@ -242,7 +262,6 @@ EvidenceBuilder -> SemanticEvidenceBatch v1 v compass-graph ``` - The two routes meet only after each has produced project-resolvable facts. The universal route never reconstructs evidence from graph rows, and the established route is not forced through a compatibility translator. @@ -261,7 +280,7 @@ validate ProducerDescriptor GrammarProvider::parse once | v -adapter policy emits evidence +producer policy emits evidence | v EvidenceBuilder::finish @@ -272,7 +291,9 @@ EvidenceBuilder::finish retain batch for collection resolution ``` -Adapters may make bounded subpasses over the prepared tree. They may not reparse source, invoke the language pack's `process()` pipeline, or derive evidence from direct graph records. +Evidence producers may make bounded subpasses over the prepared tree. They may +not reparse source, invoke the language pack's `process()` pipeline, or derive +evidence from direct graph records. ## Collection lifecycle @@ -297,7 +318,7 @@ project normalized graph records resolve framework facts ``` -Established adapters continue through their current resolution paths until +Established extractors continue through their current resolution paths until their own transition. The universal resolver ignores language names and operates only on evidence fields. The merge discards replaced code relations for evidence-backed extractions, retains source inventory and framework facts, @@ -328,12 +349,13 @@ It must: - map declaration kinds to stable normalized symbol kinds - derive containment from ownership and scope parentage - preserve exact occurrence source anchors -- stamp adapter and grammar provenance +- stamp producer and grammar provenance - avoid edges for ambiguous candidates - avoid terminal-name fallback - produce deterministic record ordering -Single-file and collection APIs use the same projection rules. A language adapter cannot maintain a second private publisher. +Single-file and collection APIs use the same projection rules. A language +producer cannot maintain a second private publisher. ## Failure classes @@ -365,34 +387,37 @@ Qualification records parse, evidence, resolution, projection, persistence, tota ## Language transition contract -A language transition starts from its established direct adapter as the production baseline. +A language transition starts from its established direct extractor as the +production baseline. The transition sequence is: 1. capture deterministic fixture and pinned-corpus baseline evidence -2. implement adapter-local universal evidence without production dual-running +2. implement producer-local universal evidence without production dual-running 3. add post-implementation contract and language conformance tests 4. compare normalized graph output and relation-family coverage 5. measure cold, warm, incremental, restore, and RSS behavior 6. hard-transition only after every blocking gate passes 7. remove only that language's replaced direct publisher and resolver branches -A failed gate returns work to the candidate implementation. It does not weaken the direct adapter's baseline or force other languages to transition. +A failed gate leaves the pipeline `Qualifying`. It does not weaken the direct +extractor baseline or force other languages to transition. -## Current language profiles +## Current language pipeline states This table describes the current branch. -| Language | Profile | Publication path | +| Language | Pipeline state | Publication path | | --- | --- | --- | | Python | Hard-cut universal | `SemanticEvidenceBatch` plus shared resolution and projection; no replaced collection resolver | | Go | Hard-cut universal | `SemanticEvidenceBatch` plus shared resolution and projection; no replaced Go collection resolver | -| Rust | Hard-cut `UniversalCandidate` | Version-15 adapter evidence plus shared resolution and projection; bounded method-result chains, impl-scoped associated types, exact `Self::Type` returns, scoped generic parameters, and nested lexical calls are preserved, Phase 2 is qualified, and replaced Rust paths are removed | -| Java | Hard-cut `UniversalCandidate` | Version-3 evidence plus shared resolution and projection; exact callable ownership, proven conversions, replaced Java paths removed, and post-cutover corpus qualification complete | -| Kotlin | Hard-cut `UniversalCandidate` | Version-1 evidence plus shared resolution and projection; exact Kotlin-only source resolution, named/default arguments and extensions, replaced Kotlin paths removed, and complete quality-audit gates still pending | -| TypeScript | Hard-cut `UniversalCandidate` | Version-5 evidence plus shared resolution and projection; TSX aliases this identity and the replaced generic publisher is removed | -| JavaScript | Hard-cut `UniversalCandidate` | Version-5 evidence plus shared resolution and projection; CJS/ESM and package decisions retain source and provenance bounds | -| Remaining registered languages | Established direct adapters | Current language-specific or generic extraction paths | +| Rust | Hard-cut `Qualifying` | Version-15 producer evidence plus shared resolution and projection; bounded method-result chains, impl-scoped associated types, exact `Self::Type` returns, scoped generic parameters, and nested lexical calls are preserved, Phase 2 is qualified, and replaced Rust paths are removed | +| Java | Hard-cut `Qualifying` | Version-3 producer evidence plus shared resolution and projection; exact callable ownership, proven conversions, replaced Java paths removed, and post-cutover corpus qualification complete | +| Kotlin | Hard-cut `Qualifying` | Version-1 producer evidence plus shared resolution and projection; exact Kotlin-only source resolution, named/default arguments and extensions, replaced Kotlin paths removed, and complete quality-audit gates still pending | +| Ruby | Hard-cut `Qualifying` | Version-1 producer evidence plus shared resolution and projection; method-space-aware dispatch and Rails pack use the same pipeline while audit gates remain open | +| TypeScript | Hard-cut `Qualifying` | Version-5 producer evidence plus shared resolution and projection; TSX aliases this identity and the replaced generic publisher is removed | +| JavaScript | Hard-cut `Qualifying` | Version-5 producer evidence plus shared resolution and projection; CJS/ESM and package decisions retain source and provenance bounds | +| Remaining registered languages | Established direct extractors | Current language-specific or generic extraction paths | Python, Go, Rust, Java, Kotlin, Ruby, TypeScript, and JavaScript are hard-cut on this branch. Each later language @@ -401,10 +426,10 @@ without adding language cases to the central publisher. A language's transition does not alter the publication route of any other language. The pinned Kotlin baseline, coverage deltas, performance results, and open audit gates are recorded in -[Kotlin universal candidate qualification](kotlin-universal-qualification.md). +[Kotlin universal qualification](kotlin-universal-qualification.md). Ruby's pinned three-corpus baseline, independent Ripper oracle, performance -samples, and candidate-only audit boundary are recorded in -[Ruby universal candidate qualification](ruby-universal-qualification.md). +samples, and qualifying-only audit boundary are recorded in +[Ruby universal qualification](ruby-universal-qualification.md). ## Framework-pack status @@ -444,14 +469,14 @@ Every universal transition verifies: - resource limits - malformed-source behavior - declared capability coverage -- absence of adapter-emitted graph records +- absence of producer-emitted graph records - full language and resolver regression suites - framework targeting - repeated fixture determinism - pinned real-corpus graph quality - cold and warm performance -Run `graphify update .` after code changes so the repository knowledge graph reflects the final architecture. +Run `compass update .` after code changes so the repository knowledge graph reflects the final architecture. ## Related pages diff --git a/docs/reference/universal-semantic-evidence.md b/docs/reference/universal-semantic-evidence.md index 6ea31d87..3fc533a2 100644 --- a/docs/reference/universal-semantic-evidence.md +++ b/docs/reference/universal-semantic-evidence.md @@ -1,27 +1,33 @@ # Universal semantic evidence Compass resolves source relationships through a language-neutral evidence -contract. Python, Go, Rust, and Java use this contract in production, and the -TypeScript and JavaScript candidate adapters now use the same production route. -The ECMAScript adapters remain candidates while their complete qualification -matrix is finished; they do not retain a second direct graph publisher. +contract. Every hard-cut language uses the same production route. C#, PHP, +Kotlin, Ruby, TypeScript, and JavaScript remain `Qualifying` while their +independent audit matrices run; they do not retain a second direct graph +publisher. This is a hard-cutover interface. It has no raw-fact translation layer, shadow mode, terminal-name fallback, or runtime dependency on Graphify. ## Evidence contract +The serialized evidence schema is `compass.languages.evidence/2`. The +extraction cache identity is `compass.languages.extraction/3`; changing either +major version invalidates older artifacts instead of attempting an implicit +translation. + `SemanticEvidenceBatch` is the unit stored with one source extraction. Its -`AdapterIdentity` names one language and producer and lists only capabilities -the adapter actually emits. The batch contains six bounded collections: +`UniversalEvidenceIdentity` names one language and emitter and lists only the +capabilities the producer actually emits. The batch contains six bounded +collections: - `DeclarationFact` identifies a source-backed declaration and its existing graph node, kind, name, qualified name, optional module/package, lexical scope, signature, optional bounded canonical parameter-type vector, optional complete-direct-base marker for Java types, and exact range. TypeScript and - JavaScript candidate evidence may additionally identify the declaration's + JavaScript evidence may additionally identify the declaration's `namespace` as `value`, `type`, `namespace`, or `value_and_type`; legacy - adapters omit this optional field. + producers omit this optional field. - `ScopeFact` identifies a lexical scope, optional owning declaration, parent scope, and exact range. - `BindingFact` records an import, import alias, re-export, local alias, @@ -45,7 +51,7 @@ the adapter actually emits. The batch contains six bounded collections: resolves to an exact source declaration or an explicitly qualified external identity. An unresolved prelude or imported spelling must not be reinterpreted as a repository-local type. - The TypeScript/JavaScript candidate adapter may use a member binding + The TypeScript/JavaScript producer may use a member binding with spelling `*` as a source-range-backed object-owner alias, including a CommonJS file-module owner for a proven `module.exports = { ...source }` shape, including a source-anchored namespace import, static `require()` @@ -79,7 +85,7 @@ the adapter actually emits. The batch contains six bounded collections: conversions, but only when one applicable vector is more specific than all other applicable vectors. Unknown hierarchy or a competing conversion remains unresolved. - The TypeScript/JavaScript candidate adapter represents a tagged + The TypeScript/JavaScript producer represents a tagged template (``tag`text ${value}``) as a call with occurrence context `tagged_template` (or `tagged_member` for a member tag). Its bounded argument vector starts with an explicit unknown slot for the runtime @@ -103,7 +109,7 @@ the adapter actually emits. The batch contains six bounded collections: identity; contextual or malformed non-statement assignments remain unresolved rather than being promoted into declarations. JSX attribute values, spread attributes, and child expressions are ordinary - value-reference contexts. The TypeScript/JavaScript candidate adapter emits + value-reference contexts. The TypeScript/JavaScript producer emits exact `jsx_value`, `jsx_spread`, and `jsx_child` occurrences for local, imported, and unresolved values, while excluding prop names, member-property spellings, and call callees. This preserves callback @@ -157,15 +163,15 @@ rather than becoming crate-qualified placeholders. `validate_evidence` rejects a batch when any of these conditions is false: -- the adapter language and producer are non-empty; +- the pipeline language and emitter are non-empty; - fact IDs are unique and all references resolve; - source paths are normalized, relative, and remain inside the corpus; - ranges are non-empty, byte ordered, and line/column ordered; -- every fact and exact-language constraint agrees with the adapter language; +- every fact and exact-language constraint agrees with the pipeline language; - behavioral candidates have an exact occurrence; - each binding, role, and relationship is covered by an advertised `LanguageCapability`; -- external resolution is used only by an adapter advertising +- external resolution is used only by a producer advertising `ExternalReferences`; and - declarations, scopes, bindings, occurrences, candidates, diagnostics, and diagnostic messages remain within `EvidenceLimits`. @@ -182,16 +188,16 @@ visited declaration proves that its complete direct-base set was emitted. Validation traverses each collection by stable fact ID, so equivalent input orders return the same first error. -## Adapter registration +## Evidence pipeline registration -`AdapterRegistry::universal_profile(language)` is the authority for universal -cutover. A returned `AdapterProfile` means universal evidence is mandatory. +`UniversalEvidenceRegistry::pipeline(language)` is the authority for universal +cutover. A returned `UniversalEvidencePipeline` means universal evidence is mandatory. Python, Go, Rust, Java, Kotlin, Ruby, TypeScript, and JavaScript are currently -registered. TSX resolves to the canonical TypeScript profile. +registered. TSX resolves to the canonical TypeScript pipeline. An unregistered language does not silently claim universal behavior. -An adapter profile must: +An evidence pipeline must: 1. use the same normalized language name as `Registry`; 2. advertise a non-empty, sorted, duplicate-free capability list; @@ -200,22 +206,22 @@ An adapter profile must: 4. validate every completed batch; and 5. stay within the shared evidence limits. -Tree-sitter and source-driven adapters produce the same records. Tree-sitter -adapters normally obtain byte and line coordinates with `range_for_node`. +Tree-sitter and source-driven producers produce the same records. Tree-sitter +producers normally obtain byte and line coordinates with `range_for_node`. A source-driven parser constructs the identical `EvidenceRange` from its own token offsets. Downstream resolution does not branch on parser technology. -### Minimal in-tree adapter +### Minimal in-tree producer This abbreviated example shows the production API shape. The language must -also have a real extractor and registry case; registering a profile without +also have a real extractor and registry case; registering a pipeline without direct emission is invalid. ```rust use compass_languages::{ - AdapterProfile, AdapterRegistry, CandidateRelation, EvidenceBuilder, - EvidenceLimits, EvidenceRange, LanguageCapability, ResolutionConstraint, - SemanticRole, + CandidateRelation, EvidenceBuilder, EvidenceLimits, EvidenceRange, + LanguageCapability, ResolutionConstraint, SemanticRole, + UniversalEvidencePipeline, UniversalEvidenceProducer, UniversalEvidenceQualification, }; const WREN_CAPABILITIES: &[LanguageCapability] = &[ @@ -224,9 +230,15 @@ const WREN_CAPABILITIES: &[LanguageCapability] = &[ LanguageCapability::Calls, ]; -static WREN_PROFILE: AdapterProfile = AdapterProfile { - language: "wren", - capabilities: WREN_CAPABILITIES, +static WREN_PIPELINE: UniversalEvidencePipeline = UniversalEvidencePipeline { + producer: UniversalEvidenceProducer { + id: "compass.wren", + language: "wren", + version: 1, + evidence_schema: "compass.languages.evidence/2", + capabilities: WREN_CAPABILITIES, + }, + qualification: UniversalEvidenceQualification::Qualifying, }; let function_range = EvidenceRange { @@ -249,7 +261,7 @@ let call_range = EvidenceRange { }; let mut builder = EvidenceBuilder::new( - &WREN_PROFILE, + &WREN_PIPELINE, "compass.languages.wren", "src/main.wren", EvidenceLimits::default(), @@ -289,10 +301,9 @@ builder.relate( let batch = builder.finish()?; ``` -Inside `adapters.rs`, add the profile to the sorted -`UNIVERSAL_ADAPTERS` table only in the same change that connects extraction -and removes the replaced resolver path. `AdapterRegistry::validate()` must -then pass. +Inside `evidence_pipeline.rs`, add the pipeline to the sorted registry table +only in the same change that connects extraction and removes the replaced +resolver path. `UniversalEvidenceRegistry::validate()` must then pass. ## Resolution @@ -377,7 +388,7 @@ every non-primitive nested trait argument. Lookup uses the implementation scope rather than the implementer's declaration scope, preserving imported types and implementation-local type parameters. The outer trait remains represented only by the `implements` candidate, so the same source token is not also -published as a generic reference. If the adapter cannot prove the implementer +published as a generic reference. If the producer cannot prove the implementer declaration, the parser recovered through an error, or an argument has competing bindings, Compass does not invent a reference endpoint. @@ -452,12 +463,12 @@ Python callable-value occurrences publish a reference candidate for the exact value use. They do not publish an indirect call merely because that value resolves to a function or method: target identity and callability do not prove invocation. An indirect call requires separate source or contract evidence that -the receiving API invokes the value. The current Python adapter does not infer +the receiving API invokes the value. The current Python producer does not infer such contracts, so uninvoked argument, collection, assignment, and return uses remain references. Python call syntax does not prove whether its target is a function or class, -and capitalization is not semantic evidence. The adapter therefore emits a +and capitalization is not semantic evidence. The producer therefore emits a call candidate that permits either callable declaration kind. Publication normalizes a uniquely resolved class target to `instantiates`; an unresolved target remains a call-shaped external or unresolved endpoint rather than an @@ -482,7 +493,7 @@ language-neutral candidate contract: `DirectBase { base_set_complete }`; - a member use carries `ReceiverDispatch` with an exact receiver identity and a registered linearization strategy; and -- the adapter must advertise `HierarchyDispatch`, which also makes cached +- the producer must advertise `HierarchyDispatch`, which also makes cached evidence lacking these facts ineligible for reuse. The shared resolver builds bounded direct-base and directly-owned-member @@ -537,7 +548,7 @@ possible target. Nested sibling bases use their enclosing class identity. A receiver-dispatch candidate cannot also carry a qualified target and cannot fall through to a same-named local or imported symbol. -Future adapters reuse the typed boundary with evidence appropriate to their +Future producers reuse the typed boundary with evidence appropriate to their language: compiler-selected overloads, trait or interface order, typed receivers, or statically resolved superclass members. A new language semantic must add and qualify its own explicit strategy; it must not reinterpret C3 or @@ -549,15 +560,15 @@ changes for this extension. Offline SCIP batches are Program evidence, not `SemanticEvidenceBatch` records. For Java calls, Compass can join their symbol identities to this contract without weakening its invariants: the compiler -reference must match an adapter-emitted call occurrence by normalized source +reference must match a producer-emitted call occurrence by normalized source path and exact half-open byte range, and the compiler definition must match an -adapter-emitted declaration the same way. Only fresh, unanimous, locally +producer-emitted declaration the same way. Only fresh, unanimous, locally defined targets are projected. Non-call references, stale or unverified documents, ambiguous definition anchors, and provider conflicts do not create or retarget an edge. The resulting graph provenance has artifact origin and the -`compiler-exact-anchor` rule while retaining the adapter's exact occurrence as +`compiler-exact-anchor` rule while retaining the producer's exact occurrence as the relationship site. Tree-sitter-only builds and `--no-program` builds keep the existing structural behavior. @@ -628,7 +639,7 @@ that removes the old pack entry. One language or framework cutover is complete only when: 1. production extraction emits typed evidence directly; -2. the capability profile states exactly what is emitted; +2. the producer capability declaration states exactly what is emitted; 3. malformed or oversized evidence fails closed with a bounded diagnostic; 4. cached extraction without valid required evidence is treated as a cache miss while all existing version values remain unchanged; @@ -653,9 +664,9 @@ python3 benchmarks/performance/harness.py audit \ --corpus path/to/pinned/corpus ``` -The manifest schema is `compass.quality-audit`. It records pinned corpus +The manifest schema is `compass.quality-audit/2`. It records pinned corpus commits and graph hashes, exact source-oracle provider identities and inventory -digests, advertised adapter/framework capabilities, required relations, and +digests, advertised producer/framework capabilities, required relations, and records from three independent pools: - `accepted` audits Compass-published edges for precision; @@ -664,7 +675,7 @@ records from three independent pools: - `graphify_hypothesis` classifies Graphify-only facts without treating them as truth. -Each record includes corpus, adapter, optional framework pack, capability, +Each record includes corpus, producer, optional framework pack, capability, language, relation, confidence, target cluster, source and target expectations, exact occurrence range, normalized snippet SHA-256, judgment, and reason. `represented_elsewhere` also names the actual graph fact. @@ -699,13 +710,14 @@ Python or Go has met the production qualification gates. ## Current qualification boundary -Python and Go are hard-cut universal language adapters. C#, PHP, Rust, Java, -Kotlin, Ruby, TypeScript, and JavaScript remain `UniversalCandidate`; the latter two share a -bounded ECMAScript emitter but retain distinct adapter identities. TSX uses the -TypeScript candidate profile. C# and PHP use dedicated bounded AST emitters and -no longer publish or resolve through their replaced raw extraction paths. -Candidate status means the universal route is active while complete capability -and corpus qualification remain in progress. `spring-java`, `spring-kotlin`, +Python, Go, Rust, Java, PHP, C#, Kotlin, Ruby, TypeScript, and JavaScript are +hard-cut universal pipelines. C#, PHP, Kotlin, Ruby, TypeScript, and JavaScript +remain `Qualifying`; TypeScript and JavaScript share a bounded ECMAScript +producer but retain distinct producer identities. TSX uses the TypeScript +pipeline. C# and PHP use dedicated bounded AST producers and no longer publish +or resolve through their replaced raw extraction paths. `Qualifying` means the +universal route is active while complete capability and corpus audit gates are +in progress. `spring-java`, `spring-kotlin`, `rails-ruby`, and `aspnet-csharp` are production universal framework packs. The `php-frameworks` pack consumes exact PHP call/import/ownership evidence for Laravel routes and Drupal hooks while configuration and template extraction diff --git a/scripts/build_ruby_quality_audit.py b/scripts/build_ruby_quality_audit.py index c78a2aa7..e1f06ec1 100644 --- a/scripts/build_ruby_quality_audit.py +++ b/scripts/build_ruby_quality_audit.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Build a deterministic Ruby universal-candidate quality-audit manifest. +"""Build a deterministic Ruby universal-evidence quality-audit manifest. The manifest is qualification data, never product input. It joins exact Tree-sitter graph anchors with the independently pinned Ripper inventory and @@ -33,7 +33,7 @@ ) -ADAPTER = "ruby" +PRODUCER = "ruby" PROVIDER = "ruby_ripper_4_0_6" CAPABILITY_BY_RELATION = { "aliases": "aliases", @@ -122,7 +122,7 @@ def _graph_edges(path: Path, nodes: dict[str, dict[str, Any]]) -> tuple[list[dic target_node = nodes.get(target) if source_node is None or target_node is None: continue - if source_node["language"] != ADAPTER or target_node["language"] != ADAPTER: + if source_node["language"] != PRODUCER or target_node["language"] != PRODUCER: continue anchor = _edge_anchor(raw) if anchor is None: @@ -354,14 +354,14 @@ def _candidate( "id": _record_id(kind, corpus, construct, target_id), "corpus": corpus, "pool": "accepted" if kind == "accepted" else "source_oracle", - "adapter": ADAPTER, + "producer": PRODUCER, "capability": capability, - "language": ADAPTER, + "language": PRODUCER, "relation": construct.relation, "confidence": confidence, "targetCluster": _target_cluster(target_label, target_id), - "source": {"nodeId": source_id, "language": ADAPTER}, - "target": {"nodeId": target_id, "language": ADAPTER}, + "source": {"nodeId": source_id, "language": PRODUCER}, + "target": {"nodeId": target_id, "language": PRODUCER}, "occurrence": { "file": construct.source_file, "startByte": construct.start_byte, @@ -481,8 +481,8 @@ def build_corpus(name: str, root: Path, graph: Path, *, max_accepted: int, max_s graph = graph.resolve() nodes, node_count = _graph_nodes(graph) edges, edge_count = _graph_edges(graph, nodes) - inventory = independent_source_inventory(root, ADAPTER) - if independent_source_provider_identity(ADAPTER) != PROVIDER: + inventory = independent_source_inventory(root, PRODUCER) + if independent_source_provider_identity(PRODUCER) != PROVIDER: raise RuntimeError("the pinned Ruby source provider identity changed") by_anchor: dict[tuple[str, str, int, int], list[dict[str, Any]]] = defaultdict(list) for edge in edges: @@ -491,12 +491,12 @@ def build_corpus(name: str, root: Path, graph: Path, *, max_accepted: int, max_s file_nodes = { node["sourceFile"]: node for node in nodes.values() - if node["language"] == ADAPTER and node["kind"] == "file" and node["sourceFile"] + if node["language"] == PRODUCER and node["kind"] == "file" and node["sourceFile"] } qualified_nodes: dict[str, list[dict[str, Any]]] = defaultdict(list) source_nodes: dict[str, list[dict[str, Any]]] = defaultdict(list) for node in nodes.values(): - if node["language"] != ADAPTER: + if node["language"] != PRODUCER: continue qualified_nodes[node["qualifiedName"]].append(node) if node["sourceFile"]: @@ -657,11 +657,11 @@ def build_corpus(name: str, root: Path, graph: Path, *, max_accepted: int, max_s } coverage = { "corpus": name, - "adapter": ADAPTER, + "producer": PRODUCER, "provider": PROVIDER, "scannedFiles": inventory.scanned_files, "parsedFiles": inventory.parsed_files, - "inventorySha256": source_construct_inventory_sha256(ADAPTER, inventory), + "inventorySha256": source_construct_inventory_sha256(PRODUCER, inventory), "graphNodes": node_count, "graphEdges": edge_count, "acceptedBeforeSampling": sum(capability_counts.values()), @@ -722,13 +722,13 @@ def main() -> int: capability_counts = Counter(record["capability"] for record in accepted) accepted_capabilities = sorted(capability_counts) advertised = [ - {"adapter": ADAPTER, "capability": capability} + {"producer": PRODUCER, "capability": capability} for capability in accepted_capabilities if capability_counts[capability] >= 100 ] - advertised_keys = {(entry["adapter"], entry["capability"]) for entry in advertised} - accepted = [record for record in accepted if (record["adapter"], record["capability"]) in advertised_keys] - source_records = [record for record in source_records if (record["adapter"], record["capability"]) in advertised_keys] + advertised_keys = {(entry["producer"], entry["capability"]) for entry in advertised} + accepted = [record for record in accepted if (record["producer"], record["capability"]) in advertised_keys] + source_records = [record for record in source_records if (record["producer"], record["capability"]) in advertised_keys] relation_counts = Counter(record["relation"] for record in accepted) relations = sorted( relation for relation, count in relation_counts.items() if count >= 100 @@ -738,14 +738,14 @@ def main() -> int: source_records = [record for record in source_records if record["relation"] in allowed_relations] records = sorted(accepted + source_records, key=lambda item: item["id"]) manifest = { - "schema": "compass.quality-audit", + "schema": "compass.quality-audit/2", "mode": "qualification", "corpora": sorted(corpora, key=lambda item: item["name"]), "sourceOracles": sorted( [ { "corpus": item["corpus"], - "adapter": ADAPTER, + "producer": PRODUCER, "provider": PROVIDER, "scannedFiles": item["scannedFiles"], "parsedFiles": item["parsedFiles"], @@ -753,16 +753,16 @@ def main() -> int: } for item in coverage ], - key=lambda item: (item["corpus"], item["adapter"]), + key=lambda item: (item["corpus"], item["producer"]), ), - "advertisedCapabilities": sorted(advertised, key=lambda item: (item["adapter"], item["capability"])), + "advertisedCapabilities": sorted(advertised, key=lambda item: (item["producer"], item["capability"])), "requiredRelations": relations, "records": records, } args.output.parent.mkdir(parents=True, exist_ok=True) args.output.write_text(json.dumps(manifest, sort_keys=True, separators=(",", ":"), ensure_ascii=False) + "\n", encoding="utf-8") report = { - "schema": "compass.ruby-quality-audit-build/1", + "schema": "compass.ruby-quality-audit-build/2", "manifest": str(args.output), "corpora": coverage, "advertisedCapabilities": advertised, diff --git a/scripts/qualify_ruby_universal.py b/scripts/qualify_ruby_universal.py index 4423f9fc..2daa2f6e 100755 --- a/scripts/qualify_ruby_universal.py +++ b/scripts/qualify_ruby_universal.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Run the bounded, independent Ruby universal-candidate qualification. +"""Run the bounded, independent Ruby universal-evidence qualification. This entry point deliberately keeps the Ripper oracle and Compass production build separate. Fixture mode needs only Ruby and the standard library; @@ -376,7 +376,7 @@ def quality_audit_mode( raise QualificationError( f"shared quality-audit evaluator emitted invalid JSON: {error}" ) from error - if result.get("schema") != "compass.quality-audit-result": + if result.get("schema") != "compass.quality-audit-result/2": raise QualificationError( f"unexpected quality-audit result schema: {result.get('schema')!r}" ) @@ -397,7 +397,7 @@ def parser() -> argparse.ArgumentParser: result.add_argument("--root", type=Path, default=ROOT / "fixtures" / "code-graph" / "qualification") result.add_argument("--oracle", type=Path, default=DEFAULT_ORACLE) result.add_argument("--manifest", type=Path, default=DEFAULT_MANIFEST) - result.add_argument("--audit-manifest", type=Path, help="compass.quality-audit JSON") + result.add_argument("--audit-manifest", type=Path, help="compass.quality-audit/2 JSON") result.add_argument("--graph", type=Path, help="published graph for quality-audit mode") result.add_argument("--corpus", type=Path, help="pinned source corpus for quality-audit mode") result.add_argument("--repository", action="append", default=[], metavar="NAME=PATH") diff --git a/tests/qualification/ruby-universal-baseline.json b/tests/qualification/ruby-universal-baseline.json index 0412a6bb..4bb3c4b4 100644 --- a/tests/qualification/ruby-universal-baseline.json +++ b/tests/qualification/ruby-universal-baseline.json @@ -1,9 +1,9 @@ { "schema": "compass.ruby-universal-baseline/1", "capturedAt": "2026-08-17", - "adapter": "compass.ruby.candidate", - "adapterVersion": 1, - "evidenceSchema": 1, + "producer": "compass.ruby", + "producerVersion": 1, + "evidenceSchema": "compass.languages.evidence/2", "buildTimeExcluded": true, "rssBlocking": false, "corpora": [ @@ -104,6 +104,6 @@ ], "limitations": [ "Discourse and RuboCop graph projections exclude non-Ruby files because their full mixed-language trees exceed the bounded Markdown parser resource envelope.", - "The quality audit now passes the fixed precision/recall thresholds, but Ruby remains a UniversalCandidate until a separate promotion decision is made." + "The quality audit now passes the fixed precision/recall thresholds, but Ruby remains Qualifying until a separate promotion decision is made." ] }