Failure Intelligence

The full account
of why it failed.

A red Salesforce Apex test tells you something broke. nimbus explain tells you what the engine actually saw: the exception, the line, the expected and actual values, and the SOQL and DML that ran just before. nimbus triage does the same job for a whole failing suite. Both are Free.

Nimbus executes your Salesforce Apex tests, triggers, SOQL, DML, and Flows on your machine, with no org involved.

Because the tests run in-process, the engine already knows why each one failed at the moment it failed. Failure intelligence is the decision to hand that over instead of throwing it away and leaving you to reconstruct it from a stack trace.

nimbus test output stays terse. These are explicit expansions - nothing about a normal run changes.

One failure, in full

nimbus explain <TestClass.method> assembles one test's outcome into a single report: the exception type and message, the source location that produced it, the assertion's expected and actual values side by side, and the SOQL and DML executed immediately before the failure, in order, each with its own file and line.

That last part is the difference between "assertion failed: expected 999, got 1" and knowing that the insert ran, the query ran after it, and the query is what came back short.

When history holds a run in which the same test passed, the report says when - and whether the Apex source has changed since. An unchanged fingerprint means the code is not what differs, which separates a regression from non-determinism.

bash
# Explain a specific failure
nimbus explain AccountServiceTest.testInsertAccount

# Structured output for scripts and agents
nimbus explain AccountServiceTest.testInsertAccount --json

# Mask runtime values before pasting into an issue
nimbus explain AccountServiceTest.testInsertAccount --redact

# What does this command keep, and where?
nimbus explain --retention-policy
bash
ZZDiagFailTest.assertionFailsWithOperationTail — FAILED (8ms)

  System.AssertException
  Assertion failed: expected 999, got 1. diagnostics fixture: expected 999 accounts

  at force-app/main/default/classes/ZZDiagFailTest.cls:23

  Assertion
    expected  999
    actual    1

  Last operations before the failure (most recent last)
    dml   insert Account  (1 row)  ZZDiagFailTest.cls:18
    soql  SELECT Id, Name FROM Account WHERE Name = 'Diagnostics Fixture Co'  ZZDiagFailTest.cls:20

  Not established by this explanation
    - Explanation is derived from local execution only; the org remains the final authority.
    - Record-level mutation provenance is not recorded.
    - No comparison against a previous passing run is available.
--json

A versioned contract

Emits the explanation as JSON under the schema nimbus.explain/v1. Versioned so a script or an agent can depend on the shape instead of parsing prose.

--redact

Safe to paste elsewhere

Masks assertion operands and SOQL string literals, and their occurrences in the exception message. It deliberately over-masks and keeps structure - object, fields, exception type, location - so a redacted report stays diagnosable.

--retention-policy

What is kept, and where

Prints exactly what this command retains. Explanations are computed in-process: not written to disk, not cached, not uploaded. The command will tell you that itself rather than asking you to trust a page.

It states what it does not know

Every explanation ends with a list of what it could not establish. That section is not a disclaimer - it is the part that makes the rest of the report usable.

Nimbus runs Apex locally at high fidelity, but it is not the Salesforce platform. An absent record in the evidence means not observed, not did not happen. The report says so rather than letting you assume otherwise.

So it also names the boundaries of the feature itself: an empty operation tail is never mistaken for a test that ran no queries, and a missing comparison against a previous passing run is stated as a missing comparison, not implied to be an absence of change.

A debugging tool that quietly presents partial evidence as complete evidence sends you to fix the wrong thing. This one refuses to.

bash
  Not established by this explanation
    - Explanation is derived from local execution only;
      the org remains the final authority.
    - Record-level mutation provenance is not recorded.
    - No comparison against a previous passing run is
      available.
Why it matters: the same discipline runs through the triage report below. Nimbus reports the cause the engine recorded, or reports that it has none - it never guesses a plausible one to fill the gap.

A wall of red, grouped by cause

nimbus triage runs your tests and groups the failures by the cause the engine recorded at the moment each one failed. Built for the first run on an unfamiliar codebase, where the output is a wall of failures and the real question is how many distinct problems this actually is.

It is not a text classifier. Every group corresponds to a cause some part of the engine assigned while the failure was happening - a Test.loadData resource that was absent, a @testSetup that threw, an assertion that did not hold. Nothing inspects message text to decide where a failure belongs.

Failures the engine could not classify are listed individually, never folded into whichever group looks plausible. A null-pointer exception is a symptom whose cause could be a missing stub, an unpopulated field, or a genuine bug - no pattern match tells those apart, and a wrong grouping hides the failure inside a count.

bash
# Triage the whole suite
nimbus triage

# Triage one class
nimbus triage "AccountTest.*"

# Structured output (schema: nimbus.triage/v1)
nimbus triage --json

# Show at most 3 tests per group
nimbus triage --limit 3
bash
Triage — 133 failed of 2357 tests (*)

  Grouped by engine-assigned cause

    assertion.failed             93 test(s)
      An assertion in the test did not hold

    loaddata.resource-not-found  2 test(s)
      Test.loadData referenced a static resource the project does not contain
      fix: Retrieve the static resource, or check the resource name passed to Test.loadData.

  No cause recorded (40) — listed individually, not grouped
    ACCT_IndividualAccounts_TEST.testOwnerLastNameUpdate — Invalid field Owner for Account

  Run-level evidence (2) — not attributed to any failure
    Apex Classes             ABadClass
[pattern]

Scope the run

Bare nimbus triage runs the whole suite. Pass a pattern - nimbus triage "AccountTest.*" - to triage one class while you work through it.

--json

nimbus.triage/v1

The full grouped report as structured JSON, including the failures the engine could not classify and the run-level evidence, each kept in its own place.

--limit

Trim the listings

Max tests listed per group, default 5. Pass 0 to list every test in every group. Group counts are unaffected - only the sample shown under each group.

Missing metadata is reported, not attributed

Missing metadata appears in its own section as run-level evidence, and is deliberately not attributed to individual failures.

Nimbus degrades an absent definition to null or zero rows instead of throwing. The run continues, and the failure surfaces later - somewhere that no longer knows a schema gap caused it. The gap is real and worth fixing. Which test it broke is not something the engine can honestly claim, so it does not claim it.

nimbus doctor --suite runs the same triage and appends a ranked "fix in this order" list to doctor's setup checks - largest cause first, so a red suite becomes a short ordered worklist. It is opt-in because it runs your tests; plain nimbus doctor stays fast enough for a pre-flight check.

Agents get the same account

The MCP tool explain_failure returns the same versioned payload as the CLI, built from the same code path - so a human and an agent never read divergent accounts of one failure. It answers from the previous run when it can, and otherwise runs just that test.

This matters more than it sounds. When an agent debugging your suite reads a summarised or reconstructed version of a failure, its conclusions drift from yours and you spend the session reconciling two stories. One construction path removes that failure mode entirely.

Triage is exposed the same way, through the same construction path, including the "no cause recorded" list. An agent inherits the honesty section too: it is told what the evidence does not establish, in the payload, rather than being left to infer completeness.

bash
# Start the MCP server for your agent
nimbus mcp

# Tools an agent can call:
#   explain_failure   one test, full evidence
#   triage_failures   a suite, grouped by cause

# Same schemas as the CLI:
#   nimbus.explain/v1
#   nimbus.triage/v1

Across runs, not just this one

Explain and triage answer questions about a single run. For the shape of your suite over time, nimbus history opens an interactive browser over past runs, and nimbus history --flaky reports flake suspects - backed by the conditions each run recorded, so "same test, same conditions, different outcome" can be distinguished from an environment difference that explains everything. See traces and analytics for trends and execution traces.

Stop reconstructing failures by hand.

nimbus explain and nimbus triage are Free commands. The engine already knows why your test failed - all these do is tell you.