Investigations
Five phases from a report to a cause you can check
Something already told you a pipeline is wrong. Decim finds out why: it locates the pipeline, reads its logs step by step, weighs competing explanations, settles them with evidence, and concludes only when the evidence agrees.
Why an explanation is not a diagnosis
Ask any capable model why a pipeline lost rows and it will tell you. That is the problem.
You will get a fluent list: schema drift, a late upstream file, a mapping gap, a failed connection. Each item is a real cause of real incidents. The list is also the same list you would get for a pipeline that failed for a completely different reason, because nothing in it came from looking at yours.
Plausibility is not evidence, and prose cannot tell the two apart. "The currency filter was
probably changed" reads exactly like "commit 9f3c2e1 changed the currency
filter, and here is the log line from the first run without EUR in it." Only the second one
survives someone checking.
So a Decim investigation is not a paragraph. It is a sequence of phases, each of which leaves a record: which pipeline was chosen and why, what each step's logs showed, which explanations were considered, how they were tested, and what the evidence finally supported. The rest of this page walks through those phases on one incident.
What an investigation starts from
A plain-language description of what is wrong. Everything else is optional, and each optional input becomes evidence.
Decim is not monitoring. Something else noticed the problem: an alert, a failed check, or a finance analyst asking where the numbers went. You open an incident by describing it in your own words. You can add an affected period, narrow the scope to a repository, pipeline or step, link a Jira ticket, and attach files up to 500 MB each: log exports, data extracts, screenshots.
A linked Jira ticket is imported as an immutable snapshot: its description, comments and attachments are copied at the moment you link it, and the investigation reads that copy. If someone edits the ticket later, the evidence the investigation used does not change under it.
The running example on this page comes from Decim's demo workspace. It is sample data, not a customer case.
Incident Transaction volume dropped by 38% in the nightly ledger load.
Finance says the EUR totals have been missing since Tuesday.
Jira ticket FIN-2291 "EUR totals missing from the daily finance dashboard since Tuesday"
description, 4 comments, 1 attachment (dashboard screenshot)
imported as an immutable snapshot
Repository github.com/acme/ledger-etl
Log sources Ledger load jobs (Google Cloud Logging, named filter)
Affected period not set
Scope not set
The pipeline behind it, Nightly ledger load, was discovered from
github.com/acme/ledger-etl. It runs nightly at 02:00 UTC and moves about 111,000
ledger entries: roughly 41,000 EUR, 58,000 USD and 12,000 GBP. Its steps are the core banking
ledger (a REST export), Extract ledger, stg_transactions (the stored
procedure stg.usp_load_transactions), Currency conversion (a stored
procedure that joins the ECB daily FX rates feed), the warehouse table
fact_transactions, and the finance daily report. Losing every EUR row would
remove about 37–38% of a night's volume, which is the first clue that the 38% in the
description and the EUR totals in the ticket are the same problem.
The five phases at a glance
The incident page shows these as a timeline. Each phase leaves a record you can open.
- 1
Locate
Rank the pipelines discovered in your repository against the description and the ticket, pick the most likely one and the step to start from, and cite the code that made the match. If nothing matches well enough, ask for more context.
- 2
Read the logs, step by step
Walk the pipeline's steps most-suspicious first, pull each step's logs from Google Cloud Logging or through the agent, compare the failing run with successful runs up to 30 days back, and cite the exact lines behind every finding.
- 3
Weigh explanations
Hold up to five competing hypotheses, each with a confidence, evidence for and against, the code involved and the tests that would settle it. An independent critique pass challenges them. Every change is a numbered revision.
- 4
Settle it
Test the hypotheses against everything collected. Where the deciding evidence lives somewhere Decim cannot read, write a check for your team to run: a read-only SQL query, a file to fetch, or guided steps.
- 5
Conclude
Confirm a cause only when other evidence corroborates it, never on confidence alone. Otherwise say so: a likely cause, an unclear one, or a gap in log access.
Locate: find the pipeline, cite the code
Before reading a single log line, Decim decides which pipeline the report is about, and shows why.
Decim reads the GitHub repositories you selected and discovers the pipelines in them, each defined by its trigger (a schedule, a queue consumer, a file arrival, a batch command) and broken into steps with the code behind each one. Locate ranks those pipelines and their steps against the incident description and the ticket snapshot.
A ranking alone would be a guess with a number attached, so every match cites code: the file
and line range that connect the report to the pipeline. Here, the report mentions EUR totals
and the finance dashboard. Nightly ledger load is the only pipeline that writes
fact_transactions, which feeds that dashboard, and its staging procedure is where
currencies are filtered. Locate starts the next phase at stg_transactions.
Locate
Nightly ledger load 91%
trigger scheduled, nightly at 02:00 UTC
why the only pipeline writing fact_transactions; the report
names EUR totals and the finance dashboard, both downstream
cites jobs/ledger-nightly.yml L12–L31 schedule and step order
sql/stg/usp_load_transactions.sql L1–L48 staging load, currency filter
sql/fact/load_fact_transactions.sql L1–L22 writes fact_transactions
start at stg_transactions (stored procedure stg.usp_load_transactions) If no pipeline matches well enough, Locate does not pick the least-bad one. It stops and asks for more context: a pipeline name, a table, a job, a time window. This matters most when the pipeline is not in a connected repository at all — a job defined only inside a scheduler or a cloud console is not discovered today — and a confident match against the wrong pipeline would send the rest of the investigation in the wrong direction.
Read the logs, step by step
Decim walks the pipeline the way an engineer would: most suspicious step first, comparing the bad run with runs that worked.
For each step, Decim pulls the relevant logs itself: from Google Cloud Logging through the named log sources you defined (for example "Ledger load jobs"), or through the optional Windows agent for log files and Windows Event Logs on your own servers. Files attached to the incident are read too. You do not paste logs into a chat.
Collection is bounded. Each investigation has a limited number of log requests, a cap on bytes read and a budget of model calls, so a noisy source cannot run away with the investigation, and when a limit cuts a read short the record says so.
A failing run on its own says little; most log lines look the same on good nights and bad ones. So for each step Decim also reads successful runs up to 30 days back and uses them as a baseline. The finding is the difference: a line that should be there and is not, a count that changed, an error that never appeared before.
Read the logs ledger-nightly / failing run 2026-10-13
stg_transactions found something
EUR rows stop arriving in staging on Tuesday (daily count ~41k -> 0)
cited 2026-10-13 02:14:09 usp_load_transactions Loaded 57911 rows for USD
2026-10-13 02:14:09 usp_load_transactions Loaded 12206 rows for GBP
2026-10-13 02:14:10 usp_load_transactions Step completed successfully (0 errors)
(no EUR line in this run)
baseline, previous successful run
2026-10-12 02:13:52 usp_load_transactions Loaded 40903 rows for EUR
Extract ledger looked fine
export still carries EUR 40,887 of 111,004 entries
Currency conversion looked fine
converted 70,117 rows, 0 unmatched rates The decisive observation is an absence. On Tuesday the staging procedure logged USD and GBP and then reported success with zero errors. There is no EUR line. The night before, the same procedure logged 40,903 EUR rows. Nothing failed, nothing alerted, and the step's own status was green.
Every cited line is pinned to the exact place it came from: a byte offset in a log file, or a record ID in Cloud Logging or the Event Log, plus the excerpt and a hash of what was collected. Decim checks that each excerpt matches the collected bytes before the finding is saved, so a citation cannot point at a line that does not exist. The evidence page explains the record in detail.
The two other steps ended "looked fine", and that is useful too. The export still carried 40,887 EUR entries, so the rows were leaving the core banking system. Currency conversion converted 70,117 rows with no unmatched rates, which is exactly the USD and GBP total, so it was not discarding anything it received.
| Result | Meaning | What happens to it |
|---|---|---|
| looked fine | Logs were read and the step behaved like its baseline | Narrows the search; the step stays on the record as checked |
| found something | Logs show behaviour that differs from successful runs | Becomes an observation with cited lines, input to the hypotheses |
| no logs | The source returned nothing for this step and window | Recorded as a gap, not as evidence that the step was healthy |
| not enough logs | Something was returned, but too little to judge the step | Recorded as a gap; often the first instrumentation to add |
| collection unavailable | The log source or agent could not be reached | Recorded as a gap; the investigation says what it could not read |
Weigh competing explanations
Up to five hypotheses at once, each an object with evidence on both sides, not a sentence in a summary.
With the observations in hand, Decim proposes competing explanations: never more than five, so each one can be tested properly. Every hypothesis carries a confidence and the reason for it, the evidence that supports it, the evidence that contradicts it, the assumptions it depends on, the code involved (file and line range), and the tests that would settle it.
| Hypothesis | Confidence | Status | |
|---|---|---|---|
| A | A change released on Tuesday excludes EUR rows in staging | 61% | active |
| B | The FX rates file arrived late and currency conversion dropped EUR | 48% | active |
| C | The core banking export stopped delivering EUR | 35% | active |
Hypothesis A revision 1 61% active
Claim A change released on Tuesday excludes EUR rows in stg_transactions.
For stg_transactions log: USD and GBP lines on 2026-10-13, no EUR line
baseline: "Loaded 40903 rows for EUR" on 2026-10-12
commit 9f3c2e1 touches sql/stg/usp_load_transactions.sql
Against none yet
Assumes the scheduled job runs the procedure from this repository
Code sql/stg/usp_load_transactions.sql L4–L9 parameter list
jobs/ledger-nightly.yml L24–L26 procedure call
Would settle
EUR row counts in staging per day, before and after the release
(requires the database; Decim cannot read it -> check) A is ahead, but 61% is not a diagnosis, and B and C are still reasonable: a late FX file or an upstream export change would also produce a night without EUR. At this point Decim knows which evidence would separate them. It does not yet have it.
The critique pass
Before the hypotheses are used, an independent critique pass reviews every one of them. It re-reads the cited excerpts against each claim, challenges each cause, and asks whether the evidence actually justifies each confidence score, correcting scores that are unsupported in either direction and recording why. It also reviews the proposed checks, so a check cannot promise a conclusion stronger than its result could show. What the critique cannot do is swap a cause for a different one to dodge a refutation: the hypotheses it reviews are the ones that face the evidence.
Numbered revisions
Hypotheses are never edited in place. When new evidence arrives — a check result, an attached file, a comment from your team — Decim produces a new numbered revision, and the earlier revision stays readable. You can see what the investigation believed before the evidence came in, what it believed after, and which piece of evidence moved it.
Settle it with checks
Decim tests explanations against everything it has collected. When the deciding evidence is somewhere it cannot read, it asks.
First, Decim tests each hypothesis against what it already holds: the cited log lines, the
baselines, the code at the commits involved, the ticket snapshot, the attached files. Here,
that reveals the commit. 9f3c2e1, Tuesday's release, changed
stg.usp_load_transactions: the hard-coded list ('USD','GBP','EUR')
became a parameter @Currencies with a default of 'USD,GBP', and the
scheduled job step still calls the procedure without passing it.
That is a strong lead, but it is a reading of code. To confirm it you would want the data: did EUR actually stop arriving in staging on Tuesday, while the other currencies carried on? That answer lives in a database, and Decim does not connect to databases. So it writes a check.
What a check is
A check is a precise request for evidence that a person on your team carries out. It names the hypotheses it tests, where to run it, the parameters, and what result would support, refute or leave the question open. There are three kinds:
- A read-only SQL query. Validated against a read-only allowlist before it is shown, so it cannot write, alter or delete. Decim never executes it: your team runs it with their own access, in their own tools, and pastes or uploads the result.
- A file to fetch. A specific log, export or configuration file, with the reason it is needed and where it is likely to be.
- Guided steps. Things to look at in a console or a system Decim has no access to, in order, with what to record.
-- Check proposed by Decim for hypotheses A, B and C. Read-only.
-- Run against the ledger warehouse. Decim does not execute this.
-- Supports A if: EUR drops to 0 from 2026-10-13 while USD and GBP stay steady.
-- Refutes A if: EUR counts after 2026-10-13 are in line with the days before.
-- Inconclusive if: all three currencies drop together (points upstream instead).
SELECT
CAST(t.loaded_at_utc AS date) AS load_date,
t.currency_code,
COUNT(*) AS row_count
FROM dbo.stg_transactions AS t
WHERE t.loaded_at_utc >= '2026-10-09'
AND t.loaded_at_utc < '2026-10-16'
GROUP BY CAST(t.loaded_at_utc AS date), t.currency_code
ORDER BY load_date, t.currency_code; The team ran it and put the result back on the incident:
load_date currency_code row_count
----------- ------------- ---------
2026-10-11 EUR 41,118
2026-10-11 GBP 12,094
2026-10-11 USD 58,203
2026-10-12 EUR 40,903
2026-10-12 GBP 12,151
2026-10-12 USD 57,640
2026-10-13 GBP 12,206
2026-10-13 USD 57,911
2026-10-14 GBP 12,188
2026-10-14 USD 58,026 EUR is present every night until the 12th and absent from the 13th, while USD and GBP hold steady. A submitted result triggers a new revision, and the result itself becomes evidence with its own reference, so the conclusion can point at it.
| What the evidence shows | Confidence | Status | |
|---|---|---|---|
| A | Commit 9f3c2e1 replaced the hard-coded list ('USD','GBP','EUR') with a parameter @Currencies defaulting to 'USD,GBP'; the scheduled job calls the procedure without it | 92% | confirmed |
| B | The FX file landed before conversion started, and every currency pair had a rate | 18% | rejected |
| C | The export still carries about 41,000 EUR rows a night | 9% | rejected |
Conclude only with corroboration
A cause is confirmed when independent evidence agrees, never on confidence alone.
Hypothesis A now rests on three independent kinds of evidence. The logs show the first run without an EUR line, cited to the byte. The commit diff shows the change that removed EUR from the default. The check result shows EUR row counts falling to zero on exactly that night while the other currencies are unchanged. Each would be suggestive on its own; together they corroborate one another, and the conclusion is Cause found.
The diagnosis also explains why nothing alerted: the procedure filtered EUR out as designed by its new default, loaded what remained, and reported success with zero errors. Every monitor watching for failures was correct that nothing failed.
The confidence policy is deliberately strict. A hypothesis can reach a high confidence on reasoning alone — a persuasive reading of code, a neat fit with the symptom — and still be wrong. So confidence decides ranking; corroboration decides conclusions. When the evidence is not there, Decim says which of four outcomes applies instead of rounding up.
| Outcome | When | What you get |
|---|---|---|
| Cause found | A hypothesis is confirmed and corroborated by evidence from more than one direction | A diagnosis: the cause, the evidence chain, and the alternatives ruled out |
| Likely cause | One explanation leads, but the corroboration a diagnosis needs is not yet in | A provisional answer, labelled as such, with the check that would confirm it |
| Cause unclear | The evidence collected does not separate the remaining explanations | The competing hypotheses, what each still needs, and what was ruled out |
| Needs log access | The steps that matter produced no usable logs, or could not be reached | An instrumentation gap: which steps, which sources, what to enable |
From a diagnosis, Decim can propose a fix: here, a patch that restores
'USD,GBP,EUR' as the default and passes the list explicitly from the job step,
plus SQL to find and reload the affected nights. It opens as a draft pull request; nothing is
merged or run automatically. Remediations covers that in
detail. The whole investigation, including the rejected hypotheses, also exports as a PDF
report.
Why the rejected ones are kept
Two of the three explanations above were ruled out. For many readers that is the more useful half.
Hypothesis B, a late FX file, is the first thing anyone on a finance data team would check, and checking it properly means finding the file's arrival time and the conversion step's start time and comparing them. Hypothesis C, a broken upstream export, would make the incident someone else's problem, which is exactly why it needs to be ruled out with evidence rather than hope. When the investigation hands you both already settled, with the export count and the conversion log behind them, it has removed the two most likely wrong turns.
A conclusion with its alternatives visible can also be argued with. You can look at the rejection of B and say: conversion matched every rate, but our month-end run uses a different rates feed. That is a real gap in the reasoning, found at a glance. A conclusion presented without its alternatives gives you nothing to push against except the conclusion itself.
So a rejected hypothesis keeps its evidence, its final confidence, and the revision in which it was ruled out. "We ruled out the FX feed" is an assertion. "We ruled it out in revision 2 because the conversion step matched a rate for every currency pair, here is the log line" is something you can check and, if warranted, overturn.
Challenging a conclusion
You know things the logs do not. The investigation is built to take them in at any point.
You can challenge a conclusion, or any hypothesis, with a written explanation: the job was paused that night, the parameter is set elsewhere in production, the figure the report uses is a different table. You can add files at any time, and submit check results as soon as you have them. Each submission produces a new revision rather than overwriting the old one.
A challenge is weighed like any other evidence, not obeyed as an instruction to change the answer. The hypotheses are re-assessed with it alongside everything already on record, and the conclusion may hold or move. If it introduces something the investigation could not see, such as a production setting that is not in the repository, that is exactly the kind of input that should move it. Either way, the change and its reason are visible in the timeline.
Common questions
Is this just a language model summarising our logs?
Does Decim run queries against our database?
What stops it from producing a confident wrong answer?
Can we see the hypotheses it rejected?
Where do the logs come from?
What if the pipeline is not in our repository?
Can we disagree with the conclusion?
Does it need to have seen this failure before?
Related: evidence for how each citation is recorded and checked; pipeline topology for how pipelines are discovered from code; remediations for what happens after a diagnosis; and the write-up of the "succeeded but rows missing" failure mode in longer form.
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.