When a coding agent has to change a function in a repository it did not write, the first thing it does is try to find out who depends on it. It searches for the name as text, opens a few files, reads whatever fits in the context, and from that it builds a picture of the structure for itself. That picture never shows up as an artifact. It lives inside the model’s reasoning, mixed with the change plan, and reaches the user already converted into a decision: “I can change this, there are only two callers.” If there was a third caller in a module the search did not reach, or a reference through a re-export the text did not reveal, the mistake does not appear as a reading error. It appears as a bug, later. To me, this is the blind spot that justifies TCG: the problem is not that the model reasons badly, it is that the model reasons over facts it improvised along the way, and nobody can tell what was observed from what was assumed.

TCG (Traceability-Guided Change Governance) is my attempt to pull that picture out of the reasoning and turn it into infrastructure. It analyzes the repository deterministically and builds a queryable map of files, symbols, definitions, references, calls (when supported), dependencies, provenance, and possible structural impacts. The consumer of that map is an LLM-based agent. Today I posted on LinkedIn a short delimitation of the project; here I want to work through what it implies, because the delimitation is not presentational modesty. It is the architectural decision.

Diagram of TCG v0.1.0, "structural code map for AI agents" (labels in Portuguese). A cycle links six boxes: the Repository (source code and project state) is mapped by TCG (indexes, maps, relates, queries, deterministically), which structures a Structured map (files, symbols, references, calls, dependencies, provenance, and impacts); the LLM queries the map (interprets, plans, changes, verifies) and makes the Code change; the New TCG analysis reindexes, updates the map, records the new snapshot, and loops back to TCG. A banner reads: "TCG provides structural facts and their provenance. The LLM reasons over those facts and carries out the change." Below are Rust, Python, and TypeScript as experimental support, a badge "Stabilizing: 252 tests passing, 5 being fixed," and the footer "Facts with provenance. Absence of a relation ≠ guarantee of nonexistence."

Facts on one side, reasoning on the other

The obvious temptation, once you have a map of the code, is to make it act. If the system knows who calls whom, why not suggest the patch? If it knows the impact, why not pick the strategy? I rejected that path on purpose. TCG does not write or modify code, does not generate or apply patches, does not choose an implementation strategy, and is not a coding agent. The flow is different: the repository goes through TCG, TCG delivers a structured map, the LLM queries that map, reasons, and changes the code, and the change leads to a new analysis that records a new snapshot. The boundary between the two halves is not an implementation detail. It defines which kind of error can exist on each side. If the map is wrong, the defect belongs to TCG and can be reproduced, because the analysis is deterministic. If the change is wrong over a correct map, the defect belongs to the reasoning. When both live in the same component, that distinction disappears, and diagnosis turns into prompt archaeology.

Separating fact from reasoning is, at bottom, separating what can carry a contract from what cannot. A deterministic component can promise that the same input yields the same output, that every fact carries its origin, and that nothing was reindexed without cause. An LLM cannot promise any of that, and should not be held to it. I prefer the probabilistic part of the system to sit entirely where uncertainty is expected, and the part that feeds that uncertainty to be as auditable as a compiler. For the same reason, TCG is not an extension of ESAA, which I have discussed at length here. They are independent projects. A structural map of the code has to answer about the code, whatever process the agent follows to change it, and coupling it to an execution protocol would repeat, at another scale, the mixture TCG exists to avoid: whoever informs would come to depend on whoever decides.

Declaring what is unknown is part of the invariant

The decision that cost me the most was about silence. A structural map always has holes. In Python and TypeScript, some relations depend on dynamic dispatch or on constructs that static analysis cannot resolve safely. In any language, call support may not cover a case, and a provider may be missing from the environment. The question is what the map does with those holes. The easy answer is to omit: list what was found and leave the rest blank. The problem is that, for an agent, a list of three callers reads as “there are three callers.” The omission becomes a negative claim without anyone having claimed anything.

That is why TCG’s output shows where each fact comes from, which snapshot it belongs to, what is resolved, and what could not be determined. The absence of an identified relation is not a guarantee of nonexistence, and the contract says so explicitly instead of letting the consumer infer the opposite. An impact query delivers relations and provenance, not an absolute prediction of behavior. It may sound like legal fine print. To me it is the central invariant: a fact without origin and without snapshot is not a fact, it is structured rumor, and an undeclared gap is worse than a declared one, because it hands the LLM the decision to treat the unknown as empty. The agent may still decide badly with an honest map. What it should not do is decide badly because the map went quiet.

The snapshot deserves a paragraph of its own. Because the LLM changes the code and TCG analyzes again afterwards, every fact has a dated validity. A reference that existed before the change may not exist after it, and one that did not exist may have appeared. Binding each answer to a snapshot is what keeps the agent from reasoning about the repository of ten minutes ago as if it were the current one, and it is what makes it possible to ask, after a change, what changed in the structure and not only in the text.

What the stabilization round showed

v0.1.0 is experimental and supports Rust, Python, and TypeScript. C and PHP are left for the future. In the stabilization round, the candidate passed on the three real applications it was tested against: three equivalent rebuilds per application, two approved impact queries per application, and a warm no-op that did no reindexing at all. I read those three criteria as contract tests, not performance metrics. Equivalent rebuilds test determinism, which is the condition for an error in the map to be reproducible. Approved impact queries test whether the map answers what the agent needs to ask. The warm no-op tests a less flashy property: if nothing changed in the repository, nothing should be recomputed. A system that reindexes without cause is not just spending time; it is signaling that it does not know exactly what depends on what, which is precisely what it promises to know.

The full regression came out at 252 PASS, 5 FAIL, and 0 ERROR. Four of those failures come from providers missing in the environment. The fifth is a real regression, in test_nonpropagating. The official result is RELEASE_BLOCKED. The version has not been released and there is no public repository.

I could describe this as “almost ready,” and the number invites it: 252 against 5 looks like a comfortable margin. But the release gate does not measure margin. It measures whether any invariant was violated, and one was. Four environment failures are information about the environment; one real failure is information about the system, and folding both into the same count would commit, in the release process, the error the map refuses to commit in a query. A project whose whole argument is not turning the unknown into absence cannot, at the moment of shipping, treat a known regression as statistical noise. If I relaxed the criterion here, I would be teaching TCG itself the lie it was built not to tell. The block is the system working.

Why embryonic is the right word

I consider TCG embryonic, and not out of rhetorical caution. Three languages, three real applications, and one open regression describe a project that already has a shape but has not yet proven its contract outside the place where it was born. What I have is a well-drawn boundary and early evidence that it holds. What I do not have is broad use, a diversity of repositories, or a track record showing how the map behaves in front of code I have never seen.

The boundary, though, seems to me the part that will not change. Coding agents will keep getting better at reasoning, and precisely for that reason they need a floor that does not reason: something that says what exists in the code, where that information came from, when it was observed, and where it ends. The quality of a change made by an LLM depends less on how much the model knows about programming than on how well it can tell, in the repository in front of it, what was seen from what was assumed.