DEV Community

Cover image for Assumptions: Turn Any Diff Into an Evidence-Backed Risk Ledger
Teycir Ben Soltane
Teycir Ben Soltane

Posted on

Assumptions: Turn Any Diff Into an Evidence-Backed Risk Ledger

The gap this closes

Most production incidents aren't caused by code that's obviously broken. They're caused by code that silently assumes something stays true:

  • "This request is only processed once."
  • "The migration deploys before the worker."
  • "That API field is never absent."
  • "Events always arrive in order."
  • "This record belongs to the current tenant."

You can ask any AI coding assistant "what could go wrong with this diff?" and get an answer. The problem is that a free-form answer is easy to skim, easy to hand-wave past, and different every time you ask. There's no standard forcing the model to show its work — no requirement that "unprotected" actually point at a file and line, or that "critical" mean something more specific than "this sounds scary."

Assumptions closes that gap. It's a SKILL.md file — no build step, no dependencies, no server, no account — that constrains an agent's review into a fixed, evidence-backed format:

Generic "review this" prompt Assumptions
Output shape Prose, varies every run Fixed table: Assumption, Evidence, If false, Status, Falsification test, Action, Confidence
Evidence Often asserted, not shown Required — every entry cites a file/line locator, or the status is downgraded to Unknown
Severity "This seems risky" Explicit P0–P3, argued from consequence
Protection vs. certainty Conflated into one impression Two separate labels: Status and Evidence confidence
Actionability You invent the test yourself Every finding ships with a concrete falsification test
False positives Generic "edge case" lists pad the output Nothing reported without evidence — unclear cases are Unknown, not flagged as bugs

It doesn't replace your agent's judgment. It constrains that judgment to a format that's fast to review and hard to talk your way around.


How it works

flowchart TD
    subgraph Input ["1. Trigger & Scope"]
        A["Git Diff / PR Changes"] --> B["Invoke Assumptions"]
        A2["Mode flag: --deploy / --security / --concurrency / --failure"] --> B
    end

    subgraph Analysis ["2. Evidence-Backed Risk Scanning"]
        B --> C{"High-Risk Path Analysis"}
        C -->|"Auth & Scope"| D1["Tenant Isolation Check"]
        C -->|"Idempotency"| D2["Retry & Concurrency Check"]
        C -->|"Migrations"| D3["Schema & Rollout Order"]
        C -->|"Messaging"| D4["Event Redelivery & Ordering"]
    end

    subgraph Verification ["3. Status & Confidence"]
        D1 & D2 & D3 & D4 --> E{"Inspect Repo Evidence"}
        E -->|"Verified in Code"| F1["Protected"]
        E -->|"Partial Safeguard"| F2["Partially Protected"]
        E -->|"Missing Safeguard"| F3["Unprotected"]
        E -->|"Uninspected"| F4["Unknown"]
    end

    subgraph Output ["4. Ledger"]
        F1 & F2 & F3 & F4 --> G["Assumption Ledger"]
        G --> H1["P0-P3 Findings"]
        G --> H2["File & Line Locators"]
        G --> H3["Falsification Tests"]
    end
Enter fullscreen mode Exit fullscreen mode

Every reported finding must clear all of these before it's allowed in the ledger:

  1. Stated as a falsifiable condition ("duplicate refund requests are prevented" — not "the code looks idempotent").
  2. Backed by a file path and line range, or path — symbol if a line range isn't available. No line number is ever guessed from a summary or search snippet.
  3. A concrete consequence if the assumption is false.
  4. Existing safeguards — or an explicit "none found in X, Y; Z was not inspected."
  5. A falsification test or verification step.
  6. A status: Protected / Partially protected / Unprotected / Unknown.
  7. An evidence confidence: High / Medium / Low.
  8. A priority: P0P3.

The status/confidence split is the part most reviews skip. "Unprotected" answers does a safeguard exist in the repo. "Evidence confidence" answers how sure am I about that call. A missing idempotency key you can see directly in the handler is Unprotected, High. A missing validation check where you never opened the middleware file is Unknown, not Unprotected — because you didn't actually look.


Quick start

mkdir -p .claude/skills/assumptions
cp SKILL.md .claude/skills/assumptions/SKILL.md
Enter fullscreen mode Exit fullscreen mode

Then, in any agent session:

Use Assumptions to review the current diff.
Enter fullscreen mode Exit fullscreen mode

That's the entire install and the entire invocation.

User-level install (available in every project):

mkdir -p ~/.claude/skills/assumptions
cp /path/to/Assumptions/SKILL.md ~/.claude/skills/assumptions/SKILL.md
Enter fullscreen mode Exit fullscreen mode

Any other agent host: clone the repo and point your agent's skill/instruction config at SKILL.md. It's plain Markdown with no vendor-specific syntax — Claude Code is the reference integration, but anything that can read a Markdown instructions file and inspect your repo can use it.

If your host doesn't auto-discover skills, paste SKILL.md's contents directly into a system prompt or custom instructions field.


Usage

Use Assumptions to review the current diff.
Use Assumptions to review src/billing/create-refund.ts.
Use Assumptions in deploy mode: "Add a nullable organization_id column, backfill it, then require it."
Use Assumptions in concurrency mode: "Can two people redeem the same invite?"
Use Assumptions in failure mode: "What happens if Stripe times out after charging the customer?"
Use Assumptions to produce falsification tests for src/billing/create-refund.ts.
Use Assumptions in compact mode for the current diff.
Enter fullscreen mode Exit fullscreen mode

If your host registers slash commands, the same modes map to:

/assumptions-scan
/assumptions-scan src/billing/create-refund.ts
/assumptions-scan --deploy "Add a nullable organization_id column, backfill it, then require it"
/assumptions-scan --concurrency "Can two people redeem the same invite?"
/assumptions-scan --failure "What happens if Stripe times out after charging the customer?"
/assumptions-scan --tests src/billing/create-refund.ts
/assumptions-scan --compact
Enter fullscreen mode Exit fullscreen mode
Mode Focus Categories
(none) Current diff or requested scope All
--deploy Migrations, rollouts, flags, rollback, version overlap [deploy]
--failure Retries, partial failure, dependencies, timeouts, recovery [failure]
--concurrency Races, duplicate delivery, locking, idempotency [concurrency]
--security Identity, authorization, tenancy, secrets, trust boundaries [security]
--tests Falsification tests only, same investigation underneath All
--compact Short PR-ready ledger (P0/P1 only) All

A real example

Input: a new endpoint that retries a Stripe refund.

// src/refunds/retry.ts
app.post("/refunds/:id/retry", async (req, res) => {
  const refund = await stripe.refunds.create({
    charge: req.body.chargeId,
    amount: req.body.amount,
  });

  return res.json({ status: "ok", refundId: refund.id });
});
Enter fullscreen mode Exit fullscreen mode

No idempotency key. No local record written before the call. One test, covering only the happy path.

Output ledger (trimmed to the two P0/P1 rows):

Priority Assumption Evidence If false Status Falsification test Recommended action Confidence
P0 Duplicate refund requests are prevented or safely deduplicated. src/refunds/retry.ts:4-7stripe.refunds.create() called with no idempotencyKey, no DB write before the call. A retry issues a second refund for the same charge. Unprotected — none found in src/refunds/retry.ts or tests/refunds/retry.test.ts; gateway retry behavior not inspected. Simulate a timeout after the provider accepts the request, then retry the same call. Persist a request key before calling the provider; pass it as Stripe's idempotency key. High
P1 A caller can reliably determine the refund's outcome after a response interruption. src/refunds/retry.ts:9 — single synchronous response, no reconciliation path found. A dropped response after a successful call leaves the client unsure whether to retry. Unprotected — none found in reviewed scope. Kill the response after the provider call succeeds; check refund state afterward. Add a durable refund-request record the client or a background job can poll. Medium

Notice what's not here: no vague "consider adding error handling," no invented business rule, no claim about what the API gateway does since that file was never opened. That gap gets named explicitly in the ledger's "Unknowns and boundaries" section instead of getting rounded up into false confidence.

Six more full ledgers — cross-tenant access, unsafe migrations, webhook ordering, cache staleness — are in the repo's examples/ folder.


Where it earns its keep

Scenario Without Assumptions With Assumptions
Payment/checkout PR Idempotency goes unchecked unless someone thinks to ask Surfaces "processed exactly once?" as P0, with a falsification test
Schema migration + app code together Ships together, "probably" deploys in the right order --deploy checks NOT NULL columns with no default, missing backfills, code assuming migration is done
New authenticated endpoint Auth confirmed, tenant scoping assumed --security flags queries scoped only by primary key, proposes a cross-tenant test
Queue consumer / webhook handler Duplicate/out-of-order delivery is an untested edge case --concurrency / --failure finds missing idempotency keys and ordering gaps
Writing the PR description "Tested locally," no structure --compact gives a short table of P0/P1 risks to act on
Turning a review comment into a test Left as a follow-up that never happens --tests outputs ready-to-write falsification tests, ordered by priority
Unfamiliar / oversized diff Shallow pass, too much to hold in your head Picks the highest-risk subset, states what was excluded and why

What it deliberately isn't

  • Not a generic AI code reviewer
  • Not a static analyzer
  • Not a list of imaginary edge cases
  • Not an autonomous code modifier — it never changes code unless you ask

And it has real limits worth knowing before you rely on it: it can only see what your agent host can inspect. Queue delivery semantics, gateway retry policy, and a provider's actual guarantees stay Unknown unless they're captured in your repo's code, tests, config, or docs. Treat Unknown as a verification task, not a confirmed absence of risk.


Benchmarked, not just vibes-approved

The repo ships a small eval suite: fixtures with known, hidden assumptions, graded blind against an expected-findings file.

Run Model Recall Precision Notes
v0.1 baseline Manual (Claude) 1.00 1.00 Confirms the procedure is followable and outputs the right shape
v0.2 blind Sisyphus (DeepSeek V4 Flash) 0.65 0.96 Unattended run exposed confidence over-calibration and priority misranking
v0.3 blind Sisyphus (DeepSeek V4 Flash) 1.00 1.00 After targeted fixes: observed-vs-inferred test, clearer P0 ranking, a completeness pass

The most fragile part turned out to be evidence-confidence calibration — telling apart what the code confirms from what a reviewer infers about the outside world. That's why the "observed-vs-inferred" check exists as an explicit step before any finding gets written down, rather than being left to the model's judgment in the moment.


Installation, in full

Assumptions/
├── SKILL.md          Core skill definition and output contract
├── CLAUDE.md          When to run it, for Claude Code
├── AGENTS.md          Same convention, host-neutral
├── llms.txt           Repo map for LLM agents
├── examples/          Full sample ledgers
├── fixtures/          Small repos with known hidden assumptions
├── evals/             Benchmark cases, rubric, run archive
└── scripts/           Optional pre-commit reminder (non-blocking)
Enter fullscreen mode Exit fullscreen mode

It's local and open-source: no hosted backend, no telemetry, no account. You run it inside your own agent environment, against your own repo.

Repo: github.com/Teycir/Assumptions
License: MIT

If this is useful, a star helps other people find it — and the highest-leverage contribution isn't code, it's a new example or fixture: a realistic diff with a hidden assumption the skill should catch.

Top comments (0)