Semantic Inspector

The Semantic Inspector is Sage's interactive code-review surface. Running cargo sage inspect opens a local web application which browses the selected crate's symbols, requests semantic products on demand, and shows the dynamic Salsa and metadata work which produced each result. It is an observer of Sage; the Oracle Test Harness remains the independent exact conformance check.

Inputs and outputs

The input is one selected Cargo target, its represented Rust source files, and the reachable dependency metadata needed by Sage. The service retains that workspace across requests and ordinary source edits. Its externally visible outputs are:

  • one complete, detail-free directory of represented local symbols;
  • a selected local or external symbol with a positive list of available products;
  • an owned generic render tree for one requested product;
  • a bounded continuation for an omitted reflected subtree;
  • the dynamic query/metadata tree for one inspection run; and
  • retained revisions, exact input deltas, demanded runs, and comparisons.

The browser does not interpret Rust symbol kinds or product identifiers. It interprets these generic resources and uses canonical paths supplied by the server for navigation.

Runtime architecture

flowchart TD
    Browser[React browser] -->|JSON and SSE| Axum[Axum loopback server]
    Axum --> Client[InspectionClient]
    Watcher[Debounced file watcher] --> Client
    Client -->|bounded typed mailbox| Actor[Database actor]
    Actor --> Host[Persistent SageHost]
    Host --> Db[(Salsa database)]
    Host --> Metadata[rustc metadata provider]
    Db --> Recorder[Run and revision recorder]
    Actor --> Reflect[Bounded structural reflection]
    Reflect --> Recorder
    Actor -->|owned response| Client

Axum owns transport and embedded assets. It has no database handle and performs no selection, semantic analysis, or reflection. A single actor serializes typed requests, source edit batches, and workspace reloads. Ordinary .rs changes update existing SourceFile inputs; Cargo or toolchain changes rebuild the host and are reported as an explicit workspace-reload revision. The actor then publishes Cargo metadata's authoritative workspace root together with the selected package and source roots to the watcher. The watcher replaces its non-recursive workspace/package and .cargo/config{,.toml} directory watches and recursive source-tree watch before waiting for the next edit. Both rust-toolchain filenames are configuration inputs. The command reports ready only after these initial watches are installed; it does not infer either Cargo root from a possibly nested target source path.

After reconstruction, replacement roots are installed before obsolete roots are removed. A transient replacement-installation failure retains the prior complete set, reports a structured degraded-watcher event, and retries. A failed obsolete-root cleanup retains both old and replacement watches and reports that harmless degraded state; it never sacrifices coverage of the new source tree.

A live session owns exactly one Cargo library target. Multi-target workspaces must select it with --package; the session preserves distinct Cargo package and target names. Dependency preparation builds only that selected library and collects all of its normal direct dependencies by Cargo package identity and extern name, including dependencies which are also workspace members; an unrelated broken member cannot block inspection. Reconstruction builds a replacement before swapping it into the actor. The replacement's rustc metadata thread must reach after_expansion without diagnostics and report readiness before the host can be published. A malformed manifest, failed build, or metadata-stub failure therefore returns a structured reload error while the prior database, revision, and watch set remain usable.

semantic_inspector_service_boundary

crates.sage_inspector.inspection.workspace_actor

Verification scenarios: Same-host cold/warm/relevant/unrelated edit tests distinguish ordinary input revisions from database reloads and never expose a partially applied batch.

crates.sage_inspector.inspection.typed_service

Verification scenarios: Service tests call the live provider directly; black-box router tests start from a checked-in Rust project and cross the actor and HTTP boundary. No handler accesses Sage directly.

The executable entry point constructs the live provider, starts the actor and watcher, binds loopback port 2442 by default, and serves assets embedded with rust-embed:

semantic_inspector_command_entry

Expected startup failures are reported as CLI errors rather than panics. A bind conflict points to --port; failure to open the default browser is only a warning because the printed loopback URL and server remain useful.

Protocol and generic browser

The normative /api/v1 schema is recorded in the accepted Semantic Inspector RFD. Every success and error carries the process-wide revision ID and a request ID; resources which do not exist are structured 400 or 404 responses. The server returns the only valid products for a selected symbol, so an absent body is omission rather than a synthetic “unavailable” product.

crates.sage_inspector.inspection.source_evidence

Verification scenarios: Backend and browser integration tests launch the real command over the same sample project, drive production routes, and snapshot the returned values, visible UI, and actor/query demand. A source edit must change results only through the live database; no snapshot is read as an API response.

crates.sage_inspector.inspection.generic_browser

Verification scenarios: Generic rendering component tests cover every protocol node without semantic product IDs, source-driven browser tests cover local and external navigation, and a static audit forbids product- or symbol-kind dispatch in the client. Omitted products make no request.

crates.sage_inspector.inspection.durable_navigation

Verification scenarios: Direct-load, Back/Forward, replace-versus-push, missing-target, event-reconnect, and revision-event tests inspect both the restored view and API demand.

The local symbol directory is fetched eagerly because it is small identity and presentation data needed for useful browser-local search. No signature, body, field type, associated value, impl candidate, or callee body is part of that resource. External metadata may be read only when macro resolution or expansion requires it to determine generated local membership. If that expansion cannot account for every possible child, the directory retains the safe represented children and marks their parent explicitly incomplete.

crates.sage_inspector.inspection.detail_free_index

Verification scenarios: A real-workspace trace snapshots the closed set of operation families used by directory construction, including the permitted macro metadata families, and explicitly forbids signatures, bodies, associated values, and impl discovery.

crates.sage_inspector.inspection.product_availability

Verification scenarios: Local/external catalog tests assert the exact positive lists and forbid product-detail reads while constructing them.

Canonical navigation

Local paths encode the selected crate and typed ownership segments. Named segments include namespace/kind information; anonymous impl segments use a stable header hash which excludes body, member, and span detail. External roots include rustc's crate disambiguator and every structured definition-path segment includes its kind, name, and disambiguator. Display labels are never parsed to recover identity.

Links through anonymous entities are best effort across edits and inspector restarts. Content-derived segments often preserve a link, but cannot reliably distinguish identical occurrences across revisions: deleting one duplicate may make its old link select another. This also affects descendants reached through an anonymous ownership segment. The stronger stability guarantee applies to paths with entirely named ownership segments. These navigation limitations do not change the semantic identity of a definition reached through imports.

Inspector navigation deliberately has its own external-path projection. The existing conformance projection omits anonymous rustc ownership segments where both emitters omit them; canonical navigation retains those segments so, for example, a variant and its constructor do not share an address. Adding navigation detail must not change oracle output.

semantic_inspector_external_paths

crates.sage_inspector.inspection.canonical_ownership

Verification scenarios: Path tests cover local association, anonymous and generated ownership, reordered siblings, rename failure, reconstruction, external parent/children, and crate disambiguation. The full exact oracle suite separately proves that navigation-only path detail does not enter the conformance representation.

Semantic products and reflection

The current local function products are identity, source, expanded Concrete IR, checked signature, diagnostics, and completed Typed IR. External products are limited to independently available metadata such as identity, signature, and children. Each product is requested independently and frozen into owned protocol values before the actor replies.

A local product refreshes its symbol handle and any newly introduced reflected cross-link by walking the symbol's explicit owner chain and enumerating only siblings at those owner boundaries. It does not rebuild the recursive directory or observe membership in unrelated descendant modules. This keeps edited product requests correct without turning the eager search index into an incremental dependency of every semantic product.

Ordinary structs and enums derive Reflect; the derive recursively preserves every field and enum payload. Explicit implementations supply semantic names, symbol cross-links, spans, stashed roots, and transparent arena-handle projection. The context limits depth and node count. Repeated DAG edges are expanded inline; only reentry into an entry currently on the reflection stack produces an explicit cycle marker. A limit produces a frozen continuation rather than silently dropping detail.

semantic_inspector_reflect_derive

crates.sage_inspector.inspection.owned_reflection

Verification scenarios: Derive-shape, repeated-edge inlining, cycle-guard, semantic-link, bounded wide-sequence traversal, representative real IR, and continuation tests cover these cases.

crates.sage_inspector.inspection.derive_reflection

Verification scenarios: Compile/shape tests cover named, tuple, and unit forms, real products retain their wrappers and nested type trees, and continuation limits retain an explicit resumable boundary.

Execution evidence

The workspace uses a temporary Salsa 0.26.1 fork. A tracked-function fetch emits WillRequest before memo lookup and a balanced DidRequest with Executed, Validated, Reused, or Cancelled. An RAII guard balances unwinding. Sage database events and external-metadata operations are correlated with that dynamic stack without per-query annotations.

Consecutive identical leaf requests are losslessly coalesced into one trace node with an observation count. This preserves the millions of cache lookups a solver run can perform without making the JSON tree itself millions of nodes.

semantic_inspector_salsa_events
semantic_inspector_salsa_request_guard

crates.sage_inspector.inspection.balanced_traces

Verification scenarios: Cold, warm, edited, nested, panicking, and pre-fetch revision-cancellation tests assert balanced entry/exit and all four terminal dispositions. Accumulator traversal separately proves that its internal memo refreshes use the same lifecycle.

crates.sage_inspector.inspection.unknown_work

Verification scenarios: Real traces include all balanced Salsa requests and metadata calls, and forbidden-demand tests cannot pass by filtering an unknown node.

Analysis, reflection, and pure view assembly are explicit trace phases. Reflection begins only after the requested semantic value is available, and the run freezes before Axum serialization. The browser can filter, resize, grow, collapse, and expand this already-owned tree without causing semantic work.

crates.sage_inspector.inspection.semantic_phase_boundary

Verification scenarios: Phase tests inspect real products; generic component tests interact with already-owned trees; and a source-driven browser demand trace proves filtering, resizing, growth, collapse, and expansion perform no semantic request.

crates.sage_inspector.inspection.deterministic_trace

Verification scenarios: Producer-authored sequential and unordered groups, cold/warm/edit traces, and forbidden fallback assertions establish the checked projection while raw debug payload remains available on failure. Async group spans are poll-scoped so interleaved futures cannot corrupt dynamic parentage.

Revisions and edit experiments

A revision record contains input deltas and zero or more inspection run handles. Applying an edit creates a revision but does not itself claim that a semantic query ran. A run is added only when the browser or another client demands work. Product comparisons report value change and observed differences between executed and reused operation identities; they do not invent causal invalidation edges Salsa did not report.

Within one live database, revision IDs contain Salsa's actual revision number. A small database-generation component distinguishes a reconstructed host, whose Salsa counter begins again, from the database it replaced. Each retained summary also carries an explicit cause: initial workspace state, an identified input-edit batch, or a workspace reload linked to the previous generation's last revision and its structured reason.

crates.sage_inspector.inspection.revision_and_demand

Verification scenarios: Cold/warm/relevant/unrelated/reload tests inspect revision details before and after demand and compare retained products and traces across revisions.

Implementation and test evidence

The generated anchor pages report implementation entries, annotated tests, explicit TODO/N/A gaps, and saved claim/test reviews. These associations do not themselves establish that a test ran.

The earlier coverage snapshot preserves the former capability descriptions, edit experiments, and limitations as historical evidence. Current coverage comes from the generated entries; implementation plans and decision rationale belong in the relevant RFDs.