Appendix A - 14. Caused-by provenance (agent-side change traceability)
The Structure of Caused-by provenance (agent-side change traceability) — its shape at a glance:
The cause is minted where the change is decided (the dispatch), travels with it, and is asserted at the commit gate; an inferred cause carries an honest proxy label.
flowchart LR
Dispatch[/Dispatch mints cause-id/] -->|carries cause| Agent[Agent works]
Agent --> Gate{Commit gate}
Taxonomy[(Closed cause taxonomy)] --> Gate
Gate -->|field present + valid value| Land([Commit lands])
Gate -->|absent or off-taxonomy| Reject([Rejected])
Accessible description: a dispatch mints a cause-id and carries it to the agent; at the commit gate the typed caused-by field is checked against a closed taxonomy; the commit lands when the field is present and names a known value and is rejected otherwise, with inferred causes labelled as proxies.
Projected from the catalogue entry lifecycle-and-observability / Caused-by provenance (agent-side change traceability).
On this page: Intent · Motivation · Applicability · Structure · Sample Code · Consequences · Example use within DocAble · Related Patterns
Intent
Intent — When an agent-driven change alters system state, you must be able to trace it back to the reason that caused it, drawn from a closed taxonomy of causes, enforced by asserting a typed caused-by field at the commit gate. Upgrade correlation to causation wherever the cause is known at the moment of action (carry it with the dispatch); accept a labeled _proxy only where no artifact can hold the cause. Never reward a particular rate; the trace exists to explain, not to be optimized (our instance: a caused-by: epic:<id> | reflection:<fire_id> | ad-hoc field asserted by the code-agent pre-commit hook).
Motivation
The failure is the un-warranted change: a landed diff whose reason survives only as tribal knowledge. When the fleet does something surprising, RCA of "why did we do this?" becomes guesswork: the diff shows what changed, never what caused the fleet to change it. Two costs follow:
- Behavioral RCA has no ground truth. You cannot debug the fleet's decisions if each decision's trigger is lost the moment the commit lands.
- No causal claim is defensible. Ask whether a nudge, a gate, or a policy actually changes behavior, and all you can offer is co-occurrence, which Goodharts the instant anyone optimizes it.
Applicability
- A commit gate to assert at, here the code-agent pre-commit hook.
- A closed taxonomy of causes small enough to be exhaustive and typed.
- A dispatch that carries the cause so the known-cause path is deterministic, not reconstructed.
- An honesty discipline, the
_proxylabeling and the no-reward-for-a-rate stance, without which the trace drifts toward a flattering number.
Structure
The Structure diagram appears at the top of this page.
Sample Code
The cause travels with the dispatch, so the field "comes out for free" at commit — correlation upgraded to causation because the cause was known when the change was made. A commit gate asserts the field names a value in the closed taxonomy; an inferred cause is labelled _proxy, never hidden.
CAUSES = {"epic", "reflection", "ad-hoc"} # closed taxonomy — a cause outside it fails loud
def assert_caused_by(commit_msg: str) -> int:
line = next((l for l in commit_msg.splitlines() if l.startswith("caused-by:")), None)
if line is None:
print("REJECT: commit has no caused-by field"); return 1
value = line.split(":", 1)[1].strip()
base = value[:-6] if value.endswith("_proxy") else value # _proxy = the cause had to be inferred
if base not in CAUSES:
print(f"REJECT: caused-by '{value}' is not in the taxonomy {sorted(CAUSES)}"); return 1
return 0
# A telemetry read — NEVER a target. Optimizing the deterministic-vs-proxy ratio would only teach lying.
def caused_by_mix(commits) -> dict[str, int]:
mix = {}
for c in commits:
v = c.caused_by
mix[v] = mix.get(v, 0) + 1
return mix
Consequences
- Some causes are proxy-only. A change provoked by the agent's own self-check facet has no external artifact holding its cause, so it is essentially inferred; label it
_proxyand be honest. A heavier mechanism does not remove that uncertainty; it relocates it. - The taxonomy is a coordination point. A genuinely new cause needs a declared taxonomy value, not a local string; deliberate, but it makes the cause set a shared surface.
- The signal must not become a target. The moment anyone rewards the deterministic-vs-proxy ratio, the field starts lying; the discipline is to read it, not to steer by it.
Example use within DocAble
- The code-agent pre-commit hook asserting a
caused-byfield from the closed taxonomy before a commit lands. - A telemetry query reporting the caused-by mix per class (how much of the fleet's change is traced deterministically versus
_proxy-labeled), read by a self-operations runbook that queries the caused-by mix.
Related Patterns
- Specializes — reflection-facet-substrate (and its lifecycle-hooks measured leash): the leash joins firings to actions by session key; this threads an explicit cause-id from cause to effect, a deterministic join where an artifact exists.
- Counterpart — mutator-stamps: product-side provenance (a mutation inside the artifact) versus agent-side provenance (the fleet's change to the repo).
- Realizes — the catalogue's claim that a traceability matrix can be a queried model behind a drift gate rather than a filed spreadsheet: caused-by is that matrix emitted one row per commit.
- See also — ddt-pin-trailers: a sibling typed-trailer discipline asserted at the commit gate.