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.

How a claim renders in a diagnosis text
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
The record

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.

Evidence record — query result json
{
  "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"]
}
The fields that make a result into evidence
Field What it is Why it matters
idStable identifierWhat a diagnosis cites; survives re-investigation
kindWhich of the six typesDetermines how it renders and what it can support
collected_byWhich agent, which siteMulti-segment estates need this to be unambiguous
sourceCatalogue id, server, parametersExactly what ran, where — reproducible by you
statement_sha256Hash of the pinned statementProves which version of the query produced this
truncatedWhether the row cap was hitA capped result cannot support a total
redactionRules applied, fields droppedYou can see what was removed before it left
cited_byHypotheses and diagnoses using itReverse 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.

Evidence kinds and their scope
Kind What it holds Constraint
Log linesMatched lines, with surrounding contextNever whole files — the window and pattern select what ships
Query resultsRows from a catalogue statementRow-capped, with the cap recorded in the record
Database rowsReference and mapping tablesWhat the pipeline actually read, not what the repository says it reads
Source codeA file at a commit, a line rangeFetched on demand for a path already implicated
DeploymentsReleases in the incident windowChanged paths and the pull request, correlated by time
Metrics and manifestsThroughput against baseline; expected vs received filesEstablishes 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

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.

Evidence record — absence json
{
  "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?
Yes, and that is the point of recording the catalogue id, server, database and bound parameters. The record contains everything needed to reproduce it against your own instance. A citation you cannot independently execute is an assertion with extra formatting.
What if the result was truncated by the row cap?
The record says so, and a truncated result cannot support a claim about a total. This is a common way to reach a wrong number confidently — an aggregate over the first 5,000 rows looks exactly like an aggregate over all of them once it has been copied into a summary.
Do you store our data indefinitely?
No. Evidence is scoped to an incident and expires with it. Retention periods and the deletion path are on the security page; the short version is that evidence is a working artefact of one investigation, not a dataset we accumulate.
How is this different from just attaching logs to a ticket?
Addressability. A log excerpt pasted into a ticket has no identity, so nothing can reference it, nothing knows when it was collected, and nobody can tell whether it was the whole file or the part that supported the argument. An evidence record has an id, a provenance and a reverse index of everything resting on it.
What happens when evidence contradicts the diagnosis?
The hypothesis it supported changes state, and the diagnosis is recomputed. That is the mechanism working — the investigation is a set of claims held up by artefacts, so removing an artefact has to move something.
Can evidence be collected without an incident?
Topology discovery runs on its own schedule, because a graph built during an incident is a graph built too late. Evidence collection proper is incident-scoped — there is no continuous tap on your production database.

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.