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.

Input

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.

The incident as opened — demo workspace text
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.

Method

The five phases at a glance

The incident page shows these as a timeline. Each phase leaves a record you can open.

  1. 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. 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. 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. 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. 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.

Phase 1

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 result text
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.

Phase 2

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.

Step sweep — Nightly ledger load text
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.

How each step can end
Result Meaning What happens to it
looked fineLogs were read and the step behaved like its baselineNarrows the search; the step stays on the record as checked
found somethingLogs show behaviour that differs from successful runsBecomes an observation with cited lines, input to the hypotheses
no logsThe source returned nothing for this step and windowRecorded as a gap, not as evidence that the step was healthy
not enough logsSomething was returned, but too little to judge the stepRecorded as a gap; often the first instrumentation to add
collection unavailableThe log source or agent could not be reachedRecorded as a gap; the investigation says what it could not read
Phase 3

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.

Revision 1 — three explanations for missing EUR totals
Hypothesis Confidence Status
AA change released on Tuesday excludes EUR rows in staging61%active
BThe FX rates file arrived late and currency conversion dropped EUR48%active
CThe core banking export stopped delivering EUR35%active
One hypothesis, opened text
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.

Phase 4

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 — EUR rows in staging, by day sql
-- 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:

Check result, submitted by the team text
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.

Revision 2 — after the check result
What the evidence shows Confidence Status
ACommit 9f3c2e1 replaced the hard-coded list ('USD','GBP','EUR') with a parameter @Currencies defaulting to 'USD,GBP'; the scheduled job calls the procedure without it92%confirmed
BThe FX file landed before conversion started, and every currency pair had a rate18%rejected
CThe export still carries about 41,000 EUR rows a night9%rejected
Phase 5

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.

The four outcomes
Outcome When What you get
Cause foundA hypothesis is confirmed and corroborated by evidence from more than one directionA diagnosis: the cause, the evidence chain, and the alternatives ruled out
Likely causeOne explanation leads, but the corroboration a diagnosis needs is not yet inA provisional answer, labelled as such, with the check that would confirm it
Cause unclearThe evidence collected does not separate the remaining explanationsThe competing hypotheses, what each still needs, and what was ruled out
Needs log accessThe steps that matter produced no usable logs, or could not be reachedAn 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.

Disproof

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.

Feedback

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?
A model does the reading and proposes explanations, and it is well suited to that. What it cannot do is make an uncited claim stick. Every observation in phase 2 has to cite lines that exist in the logs Decim collected, and a hypothesis can only be confirmed when independent evidence corroborates it. Reasoning is generated; the findings point at evidence you can open.
Does Decim run queries against our database?
No. Decim does not connect to databases today. When the deciding evidence is in a database, Decim writes a check: a read-only SQL query, validated against a read-only allowlist, with a note on what result would support, refute or leave the question open. Someone on your team runs it and puts the result back. Database connectors are planned.
What stops it from producing a confident wrong answer?
The confidence policy. A hypothesis at 90% with nothing corroborating it is reported as a likely cause, not a diagnosis. Phase 2 also records gaps explicitly — no logs, not enough logs, collection unavailable — so a step that could not be read is never treated as a step that looked fine.
Can we see the hypotheses it rejected?
Yes. Rejected hypotheses stay on the incident with the evidence that ruled them out and the revision in which that happened. In the worked example above, a late FX rates file and a broken core banking export were both reasonable first suspects, and both are kept with the reasons they lost.
Where do the logs come from?
From the log sources you connect: Google Cloud Logging through named filters you define, or files and Windows Event Logs read through the optional Windows agent. You can also attach log exports to the incident directly, up to 500 MB per file. AWS CloudWatch Logs and Azure Monitor are planned.
What if the pipeline is not in our repository?
Locate ranks pipelines discovered from the GitHub repositories you selected, so a job defined only inside a scheduler or a cloud console is not found yet. In that case Locate asks for more context instead of guessing, and you can still attach logs and describe the job in the incident.
Can we disagree with the conclusion?
Yes, at any point. Challenge a conclusion with an explanation, or add files and check results. Each submission triggers a new revision, and the earlier revisions stay visible so you can see what changed and why.
Does it need to have seen this failure before?
No. There is no library of known failure signatures. The method is the same for a new failure as for a familiar one: find the pipeline, read what each step did, compare it with runs that worked, and test each explanation against the evidence.

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.