| name | theory-vs-reality |
| description | Audit a plan vs an implementation. Build an interactive HTML checklist of end-user acceptance criteria, then spawn parallel agents to verify each item against the actual code. Use when validating whether a built feature matches its stated plan, stories, tasks, or subtasks โ or when you need a copy/paste-friendly feedback artifact tied to the plan. |
theory-vs-reality
Compare a plan (stories / tasks / subtasks / acceptance criteria) against the actual built code. Produce an interactive HTML artifact a HUMAN can pick up cold and run, with per-item verdicts pre-filled by code-checking subagents.
The artifact is a test script, not a summary. Anyone who reads a row should know exactly which URL to open, which command to run, which file to upload, and what to look for on screen โ without reading the plan or the code.
Two purposes:
- Plan sanity โ spot when the plan itself doesn't match what the user wanted.
- Build sanity โ spot misalignments between plan and implementation.
Stage 0 โ Get the source
Accept any of:
- Direct paste โ plan text in the conversation
- File paths โ e.g.
stories/*.md, subtasks.json, plan.md, tasks/*.json
- A retrieval recipe โ e.g. "run
aaa ralph subtasks list", "read all .md under docs/plans/"
If the source isn't clear, ASK ONCE. Don't guess.
Stage 1 โ Distill end-user criteria (titles only)
Read the source. KEEP ONLY criteria that involve a HUMAN END USER โ things they see, click, read, hear, copy/paste, or are blocked by. DROP backend-only criteria (DB schema, API contracts, internal refactors) UNLESS they surface in UX.
Group criteria into sections by feature/story (A, B, Cโฆ). Number each item within its section (A1, A2, B1, B2โฆ). These IDs are PERMANENT โ the user will reference them in feedback.
For each item, output ONLY:
id โ e.g. A1
title โ โค8-word headline
intent โ ONE line of what the user should be able to do (no procedure yet)
If no human-facing criteria exist, say so plainly and stop.
Stage 2 โ Per-item: discover recipe + verify (parallel)
For each item, spawn ONE subagent IN PARALLEL. Batch up to 10 per message (one message = multiple Agent tool calls = parallel execution).
Each subagent receives:
- The single item (
id, title, intent)
- The working directory
- A list of likely sources:
GUIDE.md, README.md, docs/, scripts/, e2e/, fixtures/, docker-compose.yml, Makefile, package.json scripts, runbooks
- The output schema below
The subagent has TWO jobs, done together:
Job A โ Build a test recipe a stranger could run
Hunt the repo for the CONCRETE artifacts a human needs. Required shape:
Setup:
- <prereqs: how to start the app, base URL, login creds, env vars, seeded data>
Steps:
1. <observable action: click, navigate, run command, upload file>
2. <observable action>
3. ...
Look for:
โ
<specific observable signal: text on screen, badge, status, exit code, file appearing>
โ <specific failure signal>
Rules:
- Every command must be copy-pasteable (no
<placeholder> unless the user obviously fills it).
- Every URL must include host (use
http://localhost:<port> if local; cite the port from code/docs).
- Every fixture or input file must include its path in the repo.
- If a credential is needed, name the seed user / file where it's defined.
- If the recipe can't be built from the repo (no docs, no e2e, no scripts), say so in
recipe_gaps and write the most-defensible best guess.
Job B โ Verify against the built code
Now check whether the recipe actually works against the current codebase. Return one of:
pass โ the feature is wired up; a human running the recipe would see โ
fail โ broken or missing; a human would see โ
partial โ wired up for some paths/inputs but not others; name which
unknown โ can't tell from code alone (e.g. needs a running env or external service)
Verdict MUST be expressed in observable terms โ what the user would SEE โ with a code pointer in parens for the engineer.
Output schema (one subagent โ one JSON blob)
{
"id": "A1",
"recipe": {
"setup": ["โฆ"],
"steps": ["โฆ"],
"look_for_pass": "โฆ",
"look_for_fail": "โฆ"
},
"recipe_gaps": "optional โ what couldn't be filled and why",
"verdict": {
"status": "pass|fail|partial|unknown",
"observable": "what the user would see, in plain English",
"code_pointer": "path/file.ext:line"
}
}
Collect all blobs. If any subagent fails, retry that one ONCE.
Stage 3 โ Generate the HTML artifact
Spawn ONE subagent using the highest-intelligence model the host supports.
Inputs: all the JSON blobs from Stage 2, sections from Stage 1.
Output path: <cwd>/<project-name>-theory-vs-reality-<YYYYMMDD-HHmm>.html (project-relative, NOT /tmp/ โ the user wants it next to the code).
HARD requirements for the HTML โ two-level accordion layout:
-
Single self-contained file โ inline CSS, inline JS. NO external deps. NO CDN.
-
Sticky toolbar at top (stays visible while scrolling):
- Title (project + milestone)
- Live status counts: โ
/ โ / โ ๏ธ / โ / โณ
- Progress bar:
N / TOTAL reviewed (anything that isn't โณ counts as reviewed)
- Filter chips:
All ยท Pending ยท Failed ยท Partial (clicking a chip auto-expands matching items)
- Buttons:
Export markdown โค ยท Expand all โ ยท Collapse all
-
Two-level accordion:
- Level 1 โ Section (
A โ Import โฆ): collapsible. Header shows N items ยท โ
X โ ๏ธY โZ rollup.
- Level 2 โ Item (
A1, A2, โฆ): collapsible. Collapsed header shows ID badge, title, and current status pill.
- Default open state on first load: the FIRST section that has any โณ pending item is expanded; everything else is collapsed. All items inside that section stay collapsed. This lands the user on "where work begins."
-
Status stripe โ each item row has a colored left edge (border-left: 4px solid <status color>) so the user can scan and spot pending/failed at a glance.
-
When an item is expanded, it shows (flat, no further accordions except the verdict reveal):
- Status selector:
โณ pending ยท โ
works ยท โ broken ยท โ ๏ธ partial ยท โ unclear
How to test block in a distinct background:
Setup โ bulleted list
Steps โ numbered list
Look for โ
/ Look for โ โ two color-coded lines
Notes โ textarea
โถ Reveal pre-filled verdict (form your own opinion first) โ closed by default. When opened, reveals: status emoji + observable sentence + code pointer.
-
Keyboard nav (when no textarea focused):
j / k โ next / previous item (auto-expanding the focused one)
Enter / Space โ toggle expand on focused item
1 / 2 / 3 / 4 โ set status works / broken / partial / unclear on focused item
v โ toggle "reveal verdict" on focused item
e โ focus the notes textarea of the focused item
/ โ focus the filter chips
- Show a small
Keyboard shortcuts hint at the bottom of the sticky toolbar.
-
Export button copies all annotations to clipboard as markdown:
## A1 โ <title>
Status: โ
Pre-filled verdict: โ ๏ธ partial โ <observable> (<path:line>)
Notes: <user notes>
-
Readable โ monospace IDs, generous whitespace, respects prefers-color-scheme. Status colors are consistent everywhere (stripe, pill, counts).
Visual reference โ what the HTML must look like
The HTML must mirror this layout. Treat it as the spec, not a suggestion:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ <Project> <Milestone> โ Theory vs Reality โ
โ โ
3 โ 1 โ ๏ธ 5 โ 0 โณ 4 [Export markdown โค] โ
โ Progress โโโโโโโโโโโโโโโโโโโโโโ 9 / 13 reviewed โ
โ Filter: ( All ) ( Pending ) ( Failed ) ( Partial ) โ
โ [Expand all โ] [Collapse all] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โผ A โ <Section title> 3 items ยท โ
1 โ ๏ธ2
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ โถ A1 <title> โ ๏ธ partial โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ โผ A2 <title> โ ๏ธ partial โ
โ โ โ
โ โ Your call: [ โณ pending ] [ โ
works ] [ โ broken ] โ
โ โ [ โ ๏ธ partial ] [ โ unclear ] โ
โ โ โ
โ โ โญโ How to test โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ โ
โ โ โ Setup โ โ
โ โ โ โข <prereq line> โ โ
โ โ โ Steps โ โ
โ โ โ 1. <action> โ โ
โ โ โ 2. <action> โ โ
โ โ โ Look for โ โ
โ โ โ โ
<pass signal> โ โ
โ โ โ โ <fail signal> โ โ
โ โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ โ
โ โ โ
โ โ Notes โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ โ
โ โ โ โ โ
โ โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ โ
โ โ โ
โ โ โถ Reveal pre-filled verdict (form your own opinion first) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ โถ A3 <title> โ
pass โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โถ B โ <Section title> 3 items ยท โ
2 โ1
โถ C โ <Section title> 2 items ยท โ ๏ธ2
The โ is the status stripe โ coloured by current status.
Verify the file exists before continuing. If the HTML subagent fails, retry ONCE with a simpler brief; if still failing, report the failure and stop.
Stage 4 โ Hand off
- Print the absolute path to the file.
- Open it:
open <path> (macOS) ยท xdg-open <path> (Linux) ยท else print file://<abs-path>.
- End with a one-line tally + an actionable starting point:
๐ <N> items ยท โ
X ยท โ Y ยท โ ๏ธ Z ยท โ W โ start with the โs, then the โ ๏ธs.
Recipe template + worked examples
The template every recipe MUST follow
Fill in EVERY slot. If a slot can't be filled from the repo, leave the marker AND record the gap in recipe_gaps. Never delete a slot to hide a gap.
<ID> โ <โค8-word title>
Setup:
- <how to start the system: command + URL>
- <auth / credentials, with the file where they're seeded>
- <any required env vars, services, or seed data>
Steps:
1. <ONE observable action โ navigate, click, run a command, upload a file>
2. <next observable action>
3. <โฆkeep going until the moment of truth>
Look for:
โ
<ONE specific signal the tester can see / read / measure>
โ <ONE specific failure signal>
Pre-filled verdict (hidden by default):
<status emoji> <pass|fail|partial|unknown> ยท <observable sentence> ยท (<path/file.ext:line>)
Rules of thumb:
- "Steps" are physical actions a human takes. Not "the system validates X" โ that's a Look-for, not a Step.
- "Look for" is what shows up ON SCREEN / IN TERMINAL, not what happens in the database (unless the test deliberately inspects the DB via a documented command).
- If a step has a "wait", give a concrete duration ("wait ~15s", not "wait a bit").
Example 1 โ UI flow (web app)
A2 โ Failed zip shows visible terminal state
Setup:
pnpm dev then open http://localhost:3000/imports
- Login:
admin@jtsupport.com / dev (seeded in packages/auth-server/src/seed-auth-development-link.ts:88)
Steps:
- Drop
fixtures/corrupt-archive.zip into ./local-blob/inbound/
- Wait ~15s, refresh
/imports
- Find the row for
corrupt-archive.zip
Look for:
- โ
Row shows red "Failed" badge AND a non-empty error message column
- โ Row never appears, or shows "Processingโฆ" indefinitely, or empty error text
Pre-filled verdict (hidden): โ ๏ธ partial ยท Invalid-XML zips do show Failed on /imports, but corrupt-archive zips only log to the ZipIngestLog table and never surface in the browse UI. (apps/api/src/procedures/imports.ts:425)
Example 2 โ CLI command
B1 โ Replay CLI recovers stuck zips
Setup:
- Local DB up:
pnpm db:up
- Seed a stuck zip:
pnpm seed:stuck-zip (writes one row to ZipIngestLog with state=Extracted)
Steps:
- Run
pnpm blob:replay --state=Extracted --limit=1
- Observe terminal output
- Open http://localhost:3000/imports
Look for:
- โ
CLI prints
Replayed 1 zip with exit code 0, AND the zip now appears on /imports with status "Imported"
- โ CLI exits non-zero, prints "No matching zips", or
/imports still shows nothing
Pre-filled verdict (hidden): โ
pass ยท blob:replay --state=Extracted runs and re-enqueues the row; integration test covers exact path. (packages/data/tests/integration/blob-replay-cli.test.ts:276)
Example 3 โ Cross-environment / permission check
D2 โ Pam can upload via AAD to cti-inbound
Setup:
az login as pam@jtsupport.com (her real AAD account)
- Confirm her OID:
az ad signed-in-user show --query id -o tsv
Steps:
- From any folder, run:
az storage blob upload --auth-mode login --account-name ctidev2 --container-name cti-inbound --name pam-smoke.zip --file fixtures/comverse-sample.zip
- Wait ~30s
- Open https://admin.dev-2.example.com/imports
Look for:
- โ
az command exits 0, AND a row for pam-smoke.zip appears on /imports with status "Imported"
- โ
az command returns AuthorizationPermissionMismatch / 403, or row never appears
Pre-filled verdict (hidden): โ fail ยท az ... upload returns 403 because Pam's OID is missing from cti_inbound_uploader_oids. (new-terraform/environments/dev-2/dev-2.auto.tfvars:96)
Contrast โ what NOT to produce
โ "JT staff can see imported company, account, bill period, charges, and failure error text in /imports, the company dashboard, and the usage report."
โ This restates the criterion. No URL host. No login. No fixture. No specific text/badge. Useless to a tester.
โ "A developer can run inbound-import rehearsal locally in either Mode A or Mode B."
โ Which command? What proves "it worked"? Tester has nothing to do.
Constraints
- ONE file output. The HTML. Don't scatter other artifacts.
- Read-only verification. Don't modify code, don't run tests.
- If the plan has zero human-facing criteria, say so and stop โ don't fabricate items.
- If a recipe genuinely can't be built (the feature is so unbuilt nothing exists to test), surface that in
recipe_gaps rather than inventing fictional URLs/commands.