Evidence
Every claim, one click from its proof
Decim shows the query it ran and the rows that came back, the log lines it matched, and the commit it correlated. If the evidence does not support a conclusion, it says so.
A claim without a citation is a guess
The product principle everything else follows from: every conclusion must be traceable to a specific artefact.
There is a reason academic work carries citations and incident reports usually do not. In a paper, an uncited claim is a defect anyone can point at. In an incident report, prose is the norm — "we believe the mapping table was stale" sits comfortably next to "the mapping table was stale", and a reader six weeks later cannot tell which was established and which was inferred at two in the morning by someone who wanted to go back to bed.
That ambiguity is expensive twice over. It costs you during the incident, when a plausible claim gets acted on before it is checked. And it costs you afterwards, when the report becomes the institutional memory of what happened and nobody can reconstruct which parts were solid.
So a diagnosis here is not written and then supported. It is assembled from evidence records, each with an identity, and a claim that cannot cite one does not appear.
The mapping table had no entry for CONTACTLESS [ev_01J9Z4K2QX]
└─ query result, 3 rows
reject-profile-by-reason
sql-prod-02 / Sales
2026-08-05 08:14:22Z What an evidence record contains
The artefact itself is the least interesting part. The provenance around it is what makes it usable.
Here is the record behind the finding in the worked investigation — the one that confirmed rows were being diverted rather than lost.
{
"id": "ev_01J9Z4K2QX",
"incident": "INC-1042",
"kind": "query_result",
"collected_at": "2026-08-05T08:14:22Z",
"collected_by": "agent/sql-prod-weu",
"source": {
"catalogue_id": "reject-profile-by-reason",
"engine": "mssql",
"server": "sql-prod-02",
"database": "Sales",
"parameters": {
"schema": "dbo",
"table": "RejectedTransactions",
"since_utc": "2026-08-05T00:00:00Z",
"until_utc": "2026-08-06T00:00:00Z"
},
"statement_sha256": "9f2c…a41d"
},
"result": {
"row_count": 3,
"truncated": false,
"duration_ms": 412,
"rows": [
{ "Reason": "unmapped PaymentType", "RejectedValue": "CONTACTLESS", "Rows": 455382 },
{ "Reason": "unmapped PaymentType", "RejectedValue": "GIFTCARD_V2", "Rows": 118 },
{ "Reason": "failed FK: StoreId", "RejectedValue": null, "Rows": 41 }
]
},
"redaction": { "rules_applied": ["pan"], "fields_dropped": [] },
"cited_by": ["hyp_04:confirmed", "diag_01:primary"]
} | Field | What it is | Why it matters |
|---|---|---|
id | Stable identifier | What a diagnosis cites; survives re-investigation |
kind | Which of the six types | Determines how it renders and what it can support |
collected_by | Which agent, which site | Multi-segment estates need this to be unambiguous |
source | Catalogue id, server, parameters | Exactly what ran, where — reproducible by you |
statement_sha256 | Hash of the pinned statement | Proves which version of the query produced this |
truncated | Whether the row cap was hit | A capped result cannot support a total |
redaction | Rules applied, fields dropped | You can see what was removed before it left |
cited_by | Hypotheses and diagnoses using it | Reverse index — what rests on this |
The six kinds
Different questions need different artefacts, and the kind determines what a record can legitimately support. A log match can establish that something happened at a time; it cannot establish a total. A capped query result can establish a shape; it cannot establish a sum.
| Kind | What it holds | Constraint |
|---|---|---|
| Log lines | Matched lines, with surrounding context | Never whole files — the window and pattern select what ships |
| Query results | Rows from a catalogue statement | Row-capped, with the cap recorded in the record |
| Database rows | Reference and mapping tables | What the pipeline actually read, not what the repository says it reads |
| Source code | A file at a commit, a line range | Fetched on demand for a path already implicated |
| Deployments | Releases in the incident window | Changed paths and the pull request, correlated by time |
| Metrics and manifests | Throughput against baseline; expected vs received files | Establishes the denominator a reconciliation needs |
Scoped, capped, and not streamed
Evidence is gathered around one incident and window. There is no continuous collection.
This is a design decision with both a privacy and a correctness rationale. The privacy one is obvious: a system that continuously ingests your production data is a system with a copy of your production data, whatever its retention policy says.
The correctness rationale is less obvious and matters more day to day. Evidence collected speculatively, in advance, is evidence collected without knowing the question — so it is either too narrow to answer the one that eventually arrives, or so broad that finding the relevant part becomes its own problem. Collection driven by a specific hypothesis produces artefacts that are precisely scoped, because the hypothesis said what to look for.
Topology discovery is the exception, and deliberately so: a pipeline graph assembled during an incident is a graph assembled too late to be trusted. That runs on its own schedule and reads metadata rather than data — see pipeline topology.
Absence is evidence too
Half the hypotheses in a real investigation are killed by something not being there. That needs a record of its own.
"There were no connection timeouts after 02:12" is a finding, and it refuted a hypothesis in the worked example. But it is a fundamentally weaker statement than a positive result, because it depends entirely on having looked in the right place with the right pattern over the right interval. A negative result with no record of the search is unfalsifiable.
{
"id": "ev_01J9Z4M7BB",
"incident": "INC-1042",
"kind": "absence",
"collected_at": "2026-08-05T08:15:03Z",
"sought": "connection pool timeout errors, 02:12Z–23:59Z",
"searched": {
"log_paths": ["/var/log/etl/loader-2026-08-05.log"],
"pattern": "Timeout expired.*connection from the pool",
"lines_scanned": 1284502
},
"found": 0,
"supports": ["hyp_06:refuted"],
"caveat": "Log retention is 7 days; this window is fully covered."
}
Note the caveat. Log retention is seven days and the window is fully covered, so
this absence is meaningful. Had the window extended past retention, the same search would
produce the same zero and mean nothing at all — and the record would say so.
This is the single most common way an investigation reaches a confident wrong answer: treating
"we found nothing" as equivalent to "nothing happened", when the truthful statement is "we
looked in a place that could not have contained it". Recording lines_scanned and
the paths searched turns an absence into something a reader can audit.
Evidence goes stale
Some of the most important artefacts describe state that is actively being overwritten.
A mapping table that is truncated and reloaded nightly destroys its own history. A log file
rotates. sys.dm_db_index_usage_stats resets when the service restarts. The
configuration deployed today is not the one that was deployed when the incident began.
Which is why collected_at is a first-class field rather than metadata, and why
collecting early matters. An investigation started three days after the symptom is often
working with evidence that no longer exists — not because anyone deleted it, but because the
system that held it is designed to move on.
Where a record describes volatile state, that is noted on the record. A diagnosis that rests on a mapping table read after a nightly reload is a weaker diagnosis than one that read it before, and the difference should be visible to the reader rather than buried.
Common questions
Can I re-run the query myself?
What if the result was truncated by the row cap?
Do you store our data indefinitely?
How is this different from just attaching logs to a ticket?
What happens when evidence contradicts the diagnosis?
Can evidence be collected without an incident?
Related: investigations for how evidence settles hypotheses; the agent for how it is collected and redacted; security for retention and access.
Get started
Bring us a pipeline that broke last week
The fastest way to evaluate this is a real incident you already know the answer to. If Decim gets it wrong, that is a far more useful demo than one where it doesn't.