Public claim level: demo. A C# dependency graph can look tidy and still be wrong. Two assemblies can declare the same qualified type name. Overloads can share a display label. A local can shadow a field. A missing reference can leave an invocation that looks bindable but is not. TraceMap’s checked-in synthetic fixture matrix treats those cases as graph-integrity tests, not formatting details.
What makes the evidence stronger than a display-name graph?
The short answer is compiler-selected identity plus explicit failure behavior. When Roslyn resolves a symbol, TraceMap retains a canonical ID, the declared relationship direction, the exact call-site span, the source and target assembly context, the rule ID, evidence tier, commit SHA, extractor ID/version, coverage label, and limitation. The label shown to a reviewer is not the join key.
When compiler evidence is unavailable, TraceMap does not silently recreate the same confidence from text. It keeps syntax evidence and emits a gap. That makes a reduced graph less attractive than a guessed graph, but much more useful for responsible review.
Canonical identity keeps same-looking symbols apart.
csharp.semantic.symbolidentity.v1 and
csharp.semantic.declarations.v1 preserve compiler-selected
identity across namespaces, assemblies, overload signatures, generic
substitutions, aliases, nested types, and partial declarations. A local
type and a framework type can share a simple name without becoming one
node. Same-named methods remain distinct when parameter or return types
differ.
Partial declarations in separate files can retain one canonical type identity while each declaration and call keeps its real span. An alias is resolved to the original symbol rather than promoted into a competing identity. Constructed generic endpoints preserve definition identity while substituted type keys keep incompatible constructions separate.
The call site records what the compiler selected.
csharp.semantic.callgraph.v1 treats the caller as the source and
the selected member as the target. Field, parameter, property, local,
inline declaration, pattern variable, static, extension, and
interface-typed receiver forms are scoped through compiler evidence. If a
local shadows a field, the local call and explicitly qualified field call
can point to different canonical targets.
Direction is preserved through extraction, machine-readable facts, the standard index, and reverse traversal. Reverse analysis walks from target to source; it does not rewrite the stored edge. This matters because a reversed relationship can produce paths that look completely plausible.
Semantic failure becomes syntax plus a gap.
If a receiver or reference cannot be resolved, TraceMap refuses to invent a
Tier 1 endpoint. It can retain a Tier 3 invocation or declaration under
rules such as csharp.syntax.invocation.v1 and
csharp.syntax.callgraph.v1, then attach a Tier 4 workspace or
compiler gap under csharp.semantic.workspace.v1. Coverage is
labeled reduced or partial rather than clean.
The fallback is deliberately asymmetric: syntax can preserve what was written, but it cannot claim what the compiler would have selected. A source declaration with a matching display name does not absorb an unresolved global symbol. Error-type identities do not become durable semantic nodes merely because they are convenient.
Several tempting joins are explicitly refused.
TraceMap does not join by display name, rebind unresolved symbols to nearby declarations, choose an arbitrary receiver, or guess a member from the receiver type alone. It does not merge partial types across assembly boundaries. Noncanonical route, query, table, message, and project labels are not promoted into semantic call edges.
Optional HTTP and database relationship families participate in reverse traversal only when the caller and framework-member endpoints carry the documented Tier 1 semantic identity and provenance. Syntax-only observations remain observations with identity gaps, not substitute endpoints.
Snapshot truthfulness protects the graph around the extractor.
The scan manifest binds repository and commit identity, selected scope, source inventory, and coverage. Source-byte identity is checked so a file that changes during a scan cannot quietly support evidence from two different states. Missing inventory and explicit exclusion are different conditions: only declared scope/exclusion authority may describe a source as intentionally excluded.
Current C# graph construction rebuilds immutable full snapshots. A file span says where evidence was observed; it is not ownership or deletion authority. Moving, deleting, or excluding a target in a later full scan must remove stale semantic evidence and degrade unresolved sites to syntax plus gaps without pruning unrelated evidence.
Ambiguity and fan-out remain review-tier.
Canonical identity prevents many false joins, but it does not make static dispatch equal runtime behavior. Interface and override candidates can fan out. DI registration context may narrow compatible candidates without selecting the runtime implementation, order, or lifetime. Bounded results, truncation state, omitted counts, cycles, and identity/member/schema gaps remain visible downstream.
Paths, route-flow reports, reverse queries, impact reports, portfolios, review packets, and evidence exports must carry the weakest relationship tier and gap state forward. They cannot strengthen an ambiguous candidate merely because several reports repeat it. The dedicated static-dispatch article tracked by issue #617 will own the deeper candidate-composition walkthrough when that route is published.
Legacy and non-compiling inputs remain useful—but partial.
An old project with an unavailable reference can still yield structural build diagnostics, syntax declarations, and syntax invocations. The honest result also includes workspace gaps and reduced coverage. A failed build or unavailable project load is not a clean repository, and an empty semantic edge set does not prove no dependency exists.
Referenced projects may support compilation without placing out-of-scope facts into the selected inventory. Linked compile items that belong to the selected project remain inventoried. Out-of-root compiler identities can be retained under bounded synthetic provenance without exposing host-local paths.
Known gaps stay named.
TraceMap does not currently expose an in-place incremental replacement contract. It does not combine multiple build configurations into universal semantics. Nested partial and generated/source partial coverage still have planned fixture work. Runtime dispatch, branch feasibility, reflection, dynamic loading, selected DI implementation, registration order, and production reachability require evidence outside this static contract.
Those limits belong in the handoff. Use the companion history article, The Bugs Hiding in Graph History, evidence model, gap guide, static-versus-runtime boundary, manager proof path, capability index, change-review use case, validation guide, and limitations before repeating a graph conclusion.
What this evidence does not establish.
It does not establish complete language coverage, runtime dispatch or reachability, a selected DI implementation, branch feasibility, production correctness, severity, required testing, release approval, or operational safety. It does not replace compilation review, runtime observation, system owners, or human approval.
The public article includes no source snippet, analyzer output, raw index, machine-local path, private identity, credential, or private validation detail. No LLM, embedding, vector, or prompt classification creates the graph.
A truthful partial graph beats a complete-looking guess.
TraceMap’s C# contract is strongest where the compiler selects exact identities and weakest where evidence is missing or ambiguous. The value is not a promise that every edge is known. It is the ability to distinguish a canonical relationship from syntax evidence, review-tier candidates, and explicit unknowns without hiding the difference.