otrebu/skills · Archived

theory-vs-reality

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.

First seen May 29, 2026

Installation

$ npx skills add otrebu/skills --skill theory-vs-reality

Summary

  • 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.

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from otrebu/skills · top by installs.

npx skills add otrebu/skills

Browse all from otrebu/skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

License LICENSE
Default branch main
Open issues 0
Status Archived

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 17,525 B
  • docs SUMMARY.md 376 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 12 installs

SKILL.md

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:

  1. Plan sanity — spot when the plan itself doesn't match what the user wanted.
  2. 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:

  1. Single self-contained file — inline CSS, inline JS. NO external deps. NO CDN.
  1. 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

  1. 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."

  1. 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.
  1. 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.

  1. 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.

  1. Export button copies all annotations to clipboard as markdown:

`` ## A1 — <title> Status: ❌ Pre-filled verdict: ⚠️ partial — <observable> (<path:line>) Notes: <user notes> ``

  1. 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

  1. Print the absolute path to the file.
  2. Open it: open <path> (macOS) · xdg-open <path> (Linux) · else print file://<abs-path>.
  3. 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: [email protected] / dev (seeded in packages/auth-server/src/seed-auth-development-link.ts:88)

Steps:
1. Drop fixtures/corrupt-archive.zip into ./local-blob/inbound/
2. Wait ~15s, refresh /imports
3. 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:
1. Run pnpm blob:replay --state=Extracted --limit=1
2. Observe terminal output
3. 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 [email protected] (her real AAD account)
- Confirm her OID: az ad signed-in-user show --query id -o tsv

Steps:
1. 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
2. Wait ~30s
3. 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 ctiinbounduploader_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.