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
Every reported finding must clear all of these before it's allowed in the ledger:
- Stated as a falsifiable condition ("duplicate refund requests are prevented" — not "the code looks idempotent").
- Backed by a file path and line range, or
path — symbolif a line range isn't available. No line number is ever guessed from a summary or search snippet. - A concrete consequence if the assumption is false.
- Existing safeguards — or an explicit "none found in
X,Y;Zwas not inspected." - A falsification test or verification step.
- A status:
Protected/Partially protected/Unprotected/Unknown. - An evidence confidence:
High/Medium/Low. - A priority:
P0–P3.
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
Then, in any agent session:
Use Assumptions to review the current diff.
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
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.
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
| 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 });
});
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-7 — stripe.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)
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)