Every issue traces back to
the prompt that wrote it.
A defect in production names a file and a line. RepoOps names the developer, the session, the prompt cycle, and what that session cost. Security, performance and configuration all walk the same chain. This page is that chain, hop by hop, with the module behind each one and the confidence stated at every step.
Runs on your machine · the prompt text stays there · every answer carries its confidence band
The gap
Blame names a commit. It does not name an actor.
When a person wrote every line, the commit was the actor: one author, one intent, one head to ask. That identity is what git blame hands you, and for thirty years it was enough.
It stops being enough when an agent writes most of the lines. The commit still carries a human name, and that name now means the person who accepted the change, which is a different fact from the one an incident review needs. The session that produced the line, the prompt that asked for it, the model that answered, and the money that turn spent are all real, all recorded somewhere, and none of them are in the commit.
So RepoOps writes the link at the moment the evidence exists, then walks it back on demand. Two trailers on the commit, a session record on your disk, and a set of joins that each say how sure they are. The rest of this page is those joins.
The chain
Eight hops, and the evidence each one leaves.
The first three run while the work happens. The rest run when something goes wrong, over evidence that already exists.
- 01Capture
- 02Stamp
- 03Sign
- 04Sense
- 05Join
- 06Band
- 07Answer
- 08Learn
- 01Capture
The session record: the prompts, the edits, the tool calls, the model, and the tokens each turn spent. Written on your machine as the agent works.
lib/cc-telemetry.mjs - 02Stamp
A prepare-commit-msg hook writes a Session-Id trailer onto the commit, and a second trailer carries the agent and human line counts. It goes in at setup, because a trailer that starts appearing in month three cannot attribute month one.
lib/attribution/install-trailer-hook.mjs - 03Sign
On merge, a signed authorship record goes to the transparency log with the merge receipt. Off until you switch attestation on, and honest-empty rather than fabricated when it cannot read the merged pull request.
lib/attestation/emit-on-merge.mjs - 04Sense
Something arrives: an error or a slow route from production, a finding from the detectors on this machine, or a configuration change between two baselines.
lib/events/envelope.mjs - 05Join
Walk it back. Each kind of signal has its own hops, and each hop is evidence rather than a guess: a trailer, a blame walk, a deploy record, a file overlap.
lib/causal/prod-attribute.mjs - 06Band
State how sure the join is, in one vocabulary shared by every leg: exact, strong, weak or none. The rules live in the code that assigns them.
lib/causal-link.mjs - 07Answer
The developer, the session, the prompt cycle, and what that session cost. The answer always arrives wearing its band.
lib/prompt-for-line.mjs - 08Learn
The lesson, the guard that blocks the recurrence at the merge gate, and a receipt an auditor can check without taking our word for the catch.
lib/causal/prevention-receipt.mjs
One chain, four signals
Production, performance, security, configuration.
Each leg differs exactly where the evidence differs, and each one says what it refuses to conclude.
A production error
incident → deploy → commit → session → prompt
The error names a culprit file. The nearest preceding deploy names a commit, the commit names a session, and the prompt cycle that touched that file names the turn. This is the leg a runtime-observability vendor cannot build, because it needs the coding-agent record and the git history in the same place.
Refuses No introducing deploy means the chain reads none. It does not reach for a session anyway.
lib/causal/prod-attribute.mjsA slow route
metric → deploy → commit
A latency metric is correlated to a recent deploy on three axes, strongest first: the metric's own commit reference matches the deploy's, the tables the slow path touches intersect what the deploy changed, or the metric simply lands in the window after it. The third is the weakest and is banded as such.
Refuses A match on timing alone never reads as more than the weak band it is.
lib/causal/prod-correlate.mjsA security finding
finding → commit → developer → prompt
No deploy hop, and no clock. A secret committed six months ago is detected today, so closeness in time is not evidence here at all. Correlation is by commit trailer or by blame, and the developer comes from the commit rather than from whoever owns the machine.
Refuses The machine's git identity bands weak and names itself as the source. It says who usually commits on this box, which is not a statement about this finding.
lib/causal/security-attribute.mjsA configuration change
symptom → config change
Four symptoms are watched (cost up, quality down, refusals up, latency up) and five kinds of change can explain one: a model swapped, a prompt changed, a tool added or removed, a parameter changed. The symptom is linked to the change that coincided with it, under the same band rules as every other leg.
Refuses No coincident change is recorded as unexplained rather than pinned on the nearest edit. A dimension missing on either side reads unmeasured.
lib/model-drift.mjsThe vocabulary
Four bands, and two of them count.
A chain that cannot say how sure it is gets trusted for the wrong reasons. Every leg above lands in one of these.
| Band | When it is assigned | Weight |
|---|---|---|
exact | The defect's file and line sit inside a hunk of a commit that carries a session id, and that session is the only candidate. | Counts as proof |
strong | Blame traces the line to a commit whose window overlaps a session that touched the same file. Sole match, no trailer. | Counts as proof |
weak | More than one session matched, or the blame walk crossed a squash with no recovered trailer, or the file was renamed inside the window. | Reads as possibly. Never counts |
none | No session window overlapped at all. | Not written down |
Only exact and strong count toward the attribution figure RepoOps publishes about itself. Weak renders as "possibly caused by" wherever it appears, and none is never written down at all.
The refusals
What the chain will not tell you.
A system that always answers is a system that guesses, and a guess with a name attached does real damage in an incident review. Four refusals are built in.
It will not name a developer from the machine. The git identity configured on a box says who usually commits there. It is wrong the moment a machine is shared, a pairing session happens, or a CI account is configured, so it bands weak and names itself as the source rather than borrowing the weight of the stronger bands.
It will not invent the missing hop. No introducing deploy, no chain. The result reads none, and none is not persisted, so nothing downstream can quietly promote it later.
It will not claim a prompt wrote a line it only shares a file with. When a captured edit covers the blamed line the band is strong, and exact when the commit carries a matching session id. File-level overlap alone is the common case, and it returns the real prompt text with a weak band and the word possibly.
It will not sign a claim it has not checked. A signature makes a claim attributable, not true, so signing an unverified one produces a signed unverified claim that now looks audited. A prevention receipt is built only from a row already in the append-only ledger, and it carries the band it came from along with that band's measured status, including unmeasured.
Two things riding the same chain
The dollars, and the line the transcript never crosses.
The money is on the same key
Cost is summed per session from the recorded token counts, so a finding arrives with the spend of the session that produced it rather than a day-level average. A session whose turns all ran on a model the rate card does not price reports that it is unpriced. It still appears, because it still tripped the signal. No stand-in price is ever shown in place of a real one.
lib/security-cost-fusion.mjsThe prompt text stays on your machine
A case stores the key to a prompt cycle, never the text. The prompt that leaked a secret may hold one, so the text is read locally, on demand. When someone working an incident in another repo needs to read a transcript, that takes a per-incident consent record: six states, a request that lapses in 72 hours, a grant that closes in 14 days, and no hosted code path that can write its own approval.
lib/transcript-grant.mjsWhat it is for
The chain is what makes the rest of it work.
An attributed defect is not the point. It is the input. Once an issue resolves to a session and a prompt, the lesson written from it can be aimed at what actually caused it, and the guard armed from that lesson blocks the recurrence at the merge gate instead of reminding somebody in a document nobody reads.
That is the loop on the auto-remediation page, the evidence under the security claims, and the reason the figures on the spend page can be tied to outcomes at all. Each of those pages says what it does with the chain. This one is the chain.
Every hop, in full
Where each part is documented.
- Causal trailthe defect-to-session edge, end to end
- Attribution breadthhow much of a repo the chain reaches
- Incidentsproduction signals joined to changes
- AI incident responsea finding becomes a case with an owner
- Session signalsthe detectors that open a finding
- Agent code scanthe scan over the agent's own diff
- Spend to outcomethe dollars beside the result
- Memory integritythe signed record and how it verifies
- Run forensicsone run rebuilt from local evidence
- Granted transcriptthe consent record that moves a transcript