What it does
Carries one question from a raw ask to a reviewed answer: clarify, inspect, plan, calculate under checks, write, challenge, and stop with a reason when the evidence runs out. Portable analysis is the default. An Engine Finding is a separate route you ask for.
It is the only orchestrator. Craft belongs to the specialist skills; /analyze does not redo their work.
When to reach for it
You start it: /analyze. The agent will not start it on its own.
Use it when you have an ask and want the whole practice applied, including an honest “inconclusive” or “not enough data.” Use grill-question when you only want the interview. Use a single craft skill when you already have a query, a chart, or a draft. Use revise-finding for feedback on a finished artifact.
Follow the evidence, then try to break it
Read designated prior lessons before planning. Name the checks that could invalidate the result before the main query, and say what each should show. Reproduce the headline before explaining it. For a rate, inspect numerator and denominator separately. For an aggregate change, separate mix from within-group change. Label cuts made after seeing results as exploratory. Keep a log as the work happens: what each step tested, what it showed, and what it decided. Stop when the decision-relevant uncertainty is resolved, the useful evidence is exhausted, or the next step needs something you do not have.
The answer leads with what was observed, for whom and when, with the decision-changing caveat beside it. Four review lenses (method, question, reader, materiality) try to disprove the draft. Serial self-review is still self-review and must be described as such.
Common questions
Do I need the aftergrid CLI? No. Start with a CSV, notebook, pasted results, or an existing connector. The Engine route needs the complete toolkit.
Why doesn’t it call /grill-question? User-invoked skills do not call each other. Both read the same clarification procedure.
Is “inconclusive” a failed run? No. Insufficient data, inconclusive, and needs-reframing are valid completed answers. A check that errored is a run that did not happen; a check that failed as pre-specified can be the answer.
Where did checked-analysis go? It is part of this skill now. The checks named before the main query, the analysis log, and the line between checks that ran and checks only proposed all live here. The rules about rates, joins, and causal language live in references/rigor.md, which several skills share.
What if I cannot run the queries? It delivers a runnable plan and marks every result in it unexecuted. It never fills a result with the value it expected.
Does a clean run mean the finding is approved? No. Agent review is not human publication approval. Portable output has no Engine verification merely because this skill produced it.
It's working if
- The original ask is still visible next to the sharpened question.
- Material numbers point at a source, saved result, or calculation that actually ran.
- Validity checks are named before the main query, with expected behavior taken from the question or policy, not from a convenient observed value.
- Dead ends that constrain the conclusion are kept, in a log written as the work happened.
- Another person can tell whether they can rerun the calculation, only inspect saved results, or neither.
- The closing recommendation matches the claim type the design earned (descriptive, associational, or causal) and names what would change it.
Where it fits
User-invoked Analyze-stage orchestrator. Neighbors: diagnose-change for a movement-only job, plan-analysis for a test sequence before execution, analysis-review for a draft you already have.
Optional Engine reference
Existing Engine checks and approval requirements still apply.
What it does
/analyze runs one Question from a raw ask to a reviewed Finding draft, and stops with a reason when any gate
fails. It is the only orchestrator in the Engine: it owns the order of the stages and the decision to halt,
and nothing else runs them.
The order is fixed: clarify → the checked-analysis stage → /write-finding → /iterate-visual → the /write-finding narrative pass →
/analysis-review → aftergrid check → aftergrid review status. Each stage's craft belongs to the skill
that owns it; /analyze does not redo that work, which is what keeps a three-pass visual loop from quietly
becoming a six-pass one.
The last two commands of every run are aftergrid check and then aftergrid review status, and nothing
runs after them. A review binds to a content digest, so an edit made after a review leaves the reviews
describing content nobody read; an edit after review status sends the run back to /analysis-review and
both commands run again before the run may report the Finding as reviewed.
Normal completion is an evidence-valid draft reviewed by agents, awaiting human publication readiness. Not
approved, not published. A human APPROVED review at the analyzed commit is a separate gate
(docs/contracts/publication.md).
It is user-invoked (disable-model-invocation: true, policy.allow_implicit_invocation: false). It writes
a Finding directory, reads a warehouse and spends real model time, so a human asks for it — and being the only
orchestrator, a model reaching for it mid-task would nest one run inside another.
When to reach for it
- You have a raw ask in plain words and want a Finding out of it.
/analyzecreates the Finding and clarifies the ask first; you are not required to already know the metric, population, window or falsifier. - You have a Finding directory whose
question.stateis alreadyresolvedand want the analysis run against it. - A previous run halted and you have supplied what it asked for.
/analyzereadsanalysis-progress.yamland resumes from the stage that stopped.
Not for: presentation feedback on a finished Finding (/revise-finding), sharpening a Question on its own
(/grill-question), or recording a decision (aftergrid decide).
Common questions
Why does it not call /grill-question to clarify? Because /grill-question is user-invoked, and no
user-invoked skill invokes another. Both ship an identical copy of the clarification procedure,
references/clarification.md, so the interview is the same one either way.
What is the difference between the three halts? needs_input means a person owes the run something — a
clarification, an approved Metric definition for a published decision metric, a provisional-read sign-off.
needs_attention means the run produced something a person must look at — a blocking review finding, an error
from aftergrid check, or a chart /iterate-visual handed back after its third pass. permission_denied
means the harness refused a write inside the Finding directory: not the data, not the method, not the Operator,
but the tooling. All three leave every artifact in place and all three are resumable. Those three lists are the
whole set; anything not on them, a missing adapter included, is reported rather than halted on.
What happens when the harness refuses a write? The run stops at the refusal and does not route around it —
no shell redirect, no copy through /tmp, no second tool that happens not to be refused. It writes
analysis-progress.yaml with status: permission_denied only if that file is writable, which it often is
not, because it lives in the directory that just refused a write. So the halt is also printed as the last line
of the final message, as one JSON object: {"aftergrid": "halt", "status": "permission_denied", "stage": …, "paths": [… every refused path …], "reason": "… the harness's denial text …"}. A headless harness reads that
line from stdout when no file could be written; the nightly eval records it as infrastructure with the
cause harness_permission_denied — zero assertions, never an analytical failure
(docs/contracts/eval.md).
Is a missing adapter a halt? No, and it never was one. The default data path is the recorded one (ADR 0010,
docs/contracts/record.md): your harness runs the SQL and aftergrid record writes
down what it ran. The orchestration continues unchanged, the draft carries snapshot.guarantees: [artifact_replay] only, its Check outcomes are agent-reported, and check --mode rerun and Revisit are
unavailable until inputs are captured. The run reports all of that; it does not stop for it.
Is "insufficient data" a halt? No. insufficient_data, inconclusive and needs_reframing are outcomes a
completed Analysis reaches, and they run all the way through review and check like any other. The line that
matters: a minimum_data Check that failed is the answer; a Check that errored is a run that did not
happen, and that is a halt.
Is a failed falsifier a halt? No. A pre-registered falsifier that records the outcome it did not expect is
the Analysis working: it said in advance what would show the Answer wrong, and that happened. The outcome
becomes inconclusive (or needs_reframing) and the run writes the Finding, reviews it and renders it like
any other. check reports a falsifier_failed warning, not an error. Halting there would leave the honest
Finding unwritten, which is the failure the rule exists to prevent.
What does the run report about the reviews? The two lines aftergrid review status printed, quoted
verbatim — its counts line (reviews: 3 current, 3 superseded, 0 stale) and its verdict line
(verdict: continue (…)) — plus the exit code, never a summary in the run's own words and never the
reviewers' text in place of the verdict. review status exits 1 when a required kind's newest review is
stale or missing, so a headless run cannot report a Finding as reviewed over reviews nobody redid
(docs/contracts/analysis-directory.md).
Is a stale review the same thing as a superseded one? No, and conflating them costs a run. A
stale count means the newest review of some kind is bound to other content: re-run that review and
record it at the current digest — never re-pin a review, which would make a reviewer say they read bytes they
never saw. A superseded count means an earlier review of a kind that is reviewed at the current
digest: history, not staleness, and no reason to review anything again (aftergrid review record dedupes on (kind, reviewer,
digest), so the re-review would write nothing). Citi Bike runs 2 and 3 in
examples/nyc-open-data/docs/run-log.md are the case that put this rule here.
What stops it from inventing a falsifier to finish clarifying? The halt. Where the Operator cannot answer,
the Question stays unresolved with the missing parts listed, and the run stops. The same rule covers metrics,
populations, windows, approvals and Claims: nothing is filled in to make a field non-empty.
Can I watch it? analysis-progress.yaml in the Finding directory carries the stage, the status, the reason
and when it was last written. It is orchestration bookkeeping — outside the content digest, never referenced by
a Claim, never shown to a Reader.
It's working if
- Given only a raw ask, it creates the Finding and asks its clarifying questions in one round rather than one at a time.
- Every stage appears in
analysis-progress.yamlbefore it starts and again when it ends. - A run stopped for missing input, restarted after the answer arrives, picks up at the stage that stopped instead of starting over.
- A blocking review finding stops the run at
analysis_review, and the reason names the reviewer and the finding. - A write the harness refuses stops the run where it happened, with the refused paths and the denial text in the last line of its output — and nothing was written around the refusal.
- A Finding it completes carries three current reviews and no attestation, and the summary reports publication
readiness in the word
aftergrid checkused — includingunknown. - Its last two commands are
aftergrid checkand thenaftergrid review status, and nothing it did after them changed a file: a memo edited after the reviews sends it back to/analysis-reviewinstead. - The summary quotes
review status's counts line and verdict line verbatim, and a superseded review is reported as superseded rather than as a review to redo. - On the recorded path the reviewers were told the Check outcomes were agent-reported
(
checks_reported_by_agent), and the summary names the tool that ran them and theartifact_replay-only guarantee. - It never runs a fourth visual pass, a second narrative shaping, or
/analysis-reviewtwice on the same content.
The checked-analysis stage
The checked_analysis stage runs one Analysis and leaves behind evidence someone else can trust without rerunning it:
Checks written before the numbers were seen, results pinned to the SQL that produced them and to the tool that
ran it, and an analysis.yaml recording what was assumed, what was explored and what is still missing.
The order is the point. The stage probes the catalog before committing to a plan, writes the applicable Checks
(invariant, reconciliation, minimum-data, falsifier) before the final analysis queries, runs each query and
Check through the harness's own data tool, pins what ran with aftergrid record, and records the execution order
as it happened. A Check written after the number is known is a Check written to agree with it.
That recorded path is the default (ADR 0010, docs/contracts/record.md). Where the
Instance configures an adapter, the stage captures the tables it will read and runs everything against those
retained inputs with aftergrid execute instead: the same Analysis, with retained inputs, mechanically observed
Check outcomes, check --mode rerun and Revisit on top.
It used to be the model-invoked /checked-analysis skill. It is now a stage /analyze runs itself, from
skills/analyze/references/checked-analysis.md. The shared clarification procedure lives beside it, in
skills/analyze/references/clarification.md, and /grill-question ships an identical copy, so the two reuse one
interview without a user-invoked skill calling another.
Contract for everything the stage writes: docs/contracts/analysis-directory.md;
for the recorded path, docs/contracts/record.md.
When the stage runs again
- A Question is sharpened and the evidence behind it does not exist yet.
- An Analysis must be run again after a query or a Check changed, against the same retained inputs where an adapter exists, and otherwise re-run by the harness and recorded again.
/revise-findingclassified a request as numeric or query, which reopens this stage.- A run has to stop: a definition is only proposed, a clarification is unanswered, or a provisional-access sign-off was never recorded.
Reach for /grill-question instead when the ask is not yet a Question and you want to run the interview
yourself. Reach for /write-finding when the evidence is pinned and the memo is what is missing.
Checked-analysis stage: common questions
Does the stage need an adapter? No. Without one it runs on the recorded path: your harness's tool (an MCP
server, a CLI, psql) runs every query and every Check, and aftergrid record --tool "<name>" pins the SQL,
the parameters, the result, the evidence and who ran it. An Instance (aftergrid.yaml) is still required; an
adapter is not, and its absence is not a halt. What you do not get: retained inputs, so snapshot.guarantees is
exactly [artifact_replay], aftergrid check --mode rerun is refused with rerun_unavailable, and the Finding
cannot be revisited until inputs are captured.
If aftergrid did not run the Checks, what does a passing Check mean? That the tool said it passed. On the
recorded path a Check outcome is agent-reported: record refuses a pass with no evidence file, check
verifies that the file is present and still hashes to what was pinned, and the report carries
checks_reported_by_agent: true as its own fact. An agent-reported outcome can lower publication readiness and
never raises it: unknown at most. The guardrail hook inspects shell commands in Claude Code, so a query run
through an MCP server or an in-process client never passed a shell and the guard never saw it; that
non-coverage is stated on the Finding rather than left to be inferred.
Can it read my warehouse during the analysis? On the recorded path your tool reads it every time a query
runs, and nothing is copied into the Finding but the SQL, the parameters and the result. On the adapter path it
reads the source in two places, and both are reads: the catalog probe, and the capture that copies the declared
tables into the Finding. After that, aftergrid execute opens the retained extracts, verifies every hash, and
reports missing_file or hash_mismatch rather than fall back to the source. There is no flag that changes
this.
Does the capture only take the rows my window needs? On the adapter path, no. It copies whole tables, and
the recorded source.method says so. The window is applied in the analysis SQL, which converts to the
analytical timezone explicitly, so nothing depends on how wide the extract is. A --description claiming the
extract was bounded or filtered is refused, because provenance inside the content digest may not describe a read
that did not happen.
What if a table is too big to copy whole? Because the read is the whole table, a table the planner puts
over the Instance's admission limit is refused with admission before anything is read or written, and there is
no narrower flag to reach for: build the bounded table for this Question yourself (into the Instance's DuckDB
file, or as a table in the schema a Postgres Instance reads) with a provenance table beside it, and capture
that, the analytical window still living in the SQL
(docs/contracts/adapters.md, "Large sources: the windowed Instance pattern").
aftergrid capture <finding-dir> --catalog reports each table's scan rows, its bytes where the source states
them, and its admissibility before anything is copied.
What happens when a Check fails? It depends on which Check, and the distinction is deliberate. A failing
minimum_data Check is a business result: the Finding's outcome becomes insufficient_data and the memo says
what is missing. A failing required invariant or reconciliation Check means the Analysis does not establish
what it asserts, and it is reported as an error. A Check that errors is never a business result.
A failing falsifier is a third thing, and the most important one. A falsifier is never required: true:
required asks whether the numbers stand, a falsifier asks whether the Answer does. So a falsifier that
records the outcome it did not expect makes the Finding inconclusive (or needs_reframing) and the run
carries straight on to /write-finding. check reports it as a falsifier_failed warning, not an error, and
render writes the page with the falsifier on it. The one thing the stage will not do is loosen the threshold,
drop required, flip expected_outcome or rewrite the SQL after seeing the result; checks_preregistered
records each Check's hash when it was written so a reviewer can check that mechanically.
What if no approved definition fits? It proposes one (lifecycle: proposed, no approval block), and that
definition may back a supporting or diagnostic Claim with its status shown. It can never be the published
decision metric. If the Question's decision metric is the one that is only proposed, the run stops with a
needs_input item of kind definition_approval naming you.
Where does the middle of the analysis go: the dead ends, the days that went nowhere? Into
analysis.yaml#/probes, in place, as it happens. Each entry carries at (the harness's clock at the time,
never a time reconstructed afterward) and a kind: exploratory for a look that informed the plan,
dead_end for a path tried or considered and abandoned, reframe for a look that changed the Question, which
must then say what changed. Dead ends are kept, not deleted, and the list is read in at order as the timeline
of the run. check warns when it is out of order rather than refusing it, because the honest repair is the
times and not the sort. The writer may cite a dead end in a Finding's Limitations; it may never turn one into a
number.
How are exploratory cuts kept from becoming the headline? The primary comparison is registered in
analysis.yaml before any cut is explored, with registered_before_cuts telling the truth about when. A cut
decided afterward is marked exploratory: true in execution_order, and its Claim carries
comparison.pre_registered: false all the way into the Finding.
Will it rewrite an approved Finding? No. aftergrid execute and aftergrid record both refuse a revision
carrying attestations when the run would change the content digest they bind to: nothing is written, and the
report says to bump the revision. Attestations and reviews are never written, refreshed or dropped.
What does the writer get? analysis.yaml, manifest.yaml and results/. The writer sharpens sentences and
writes the memo; it does not change a query, a result, a definition, or a Claim's type, comparison, population or
window.
Checked-analysis stage: it's working if
analysis.yaml#/execution_orderlists every Check before the first analysis query, and the Check files' git history agrees with that order.- Every exploratory cut is labeled in
execution_orderand in the Claim resting on it. - Each probe records what it asked and what it observed, and at least one of them changed the plan.
- Every probe carries
atandkind, the entries are inatorder, and the dead ends are still there. The middle of the analysis is legible from the list alone: where it went, what it abandoned, what it reframed. - On the recorded path: every declared execution and Check has been recorded with the tool that ran it,
aftergrid checkreports evidencevalidwith norecorded_pathwarning left, and the handover says the Check outcomes were reported rather than executed. - On the adapter path:
aftergrid executereportssql performedwith no errors, andaftergrid check --mode rerunreports evidencevalidwith norerun_mismatch. snapshot.guaranteeswas set by a run that actually happened, not by capturing the inputs, and it is empty when the run saved no result, because there is then nothing to replay. A recorded Finding carriesartifact_replayand never claimsanalysis_rerun.- Every Check names the execution it runs against, so on the adapter path
check --mode rerunresolves it to the same retained inputsexecuteused; a Check that resolves to no execution is refused instead of quietly passing. - Every evidence reference names a cell that exists, and a causal Claim records
causal_basis. - Every assumption the Question did not settle has an entry with an honest
basis, includingunverified. - A halted run names each
needs_inputitem with an owner, and neither the state nor the outcome is rounded up. - A non-answer carries
what_would_be_needed, soinsufficient_datais usable rather than a shrug.