[object Object]

← back to Cli Printing Press

refactor(skill): Claude Code IS the brain, not the Go binary

6e934000d38fe4f11e87c9131ace8d4d2a64a58c · 2026-03-25 12:19:53 -0700 · Matt Van Horn

Fundamental architecture fix: the /printing-press skill now treats
Claude Code as the LLM brain. Claude Code does the research (WebFetch),
writes specs (Write), polishes output (Edit), and scores results (Bash).
The Go binary is just a template engine.

Before: Go binary shells out to claude CLI for LLM calls (broken -
claude can't call itself from within Claude Code).

After: Skill tells Claude Code to use its own tools. WebFetch reads
API docs. Claude Code understands them and writes the YAML spec.
Go binary renders templates. Claude Code edits the output to improve it.

The internal/llm/ and internal/llmpolish/ packages remain as a fallback
for CLI-only users running printing-press generate --polish from their
terminal without Claude Code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Files touched

Diff

commit 6e934000d38fe4f11e87c9131ace8d4d2a64a58c
Author: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Date:   Wed Mar 25 12:19:53 2026 -0700

    refactor(skill): Claude Code IS the brain, not the Go binary
    
    Fundamental architecture fix: the /printing-press skill now treats
    Claude Code as the LLM brain. Claude Code does the research (WebFetch),
    writes specs (Write), polishes output (Edit), and scores results (Bash).
    The Go binary is just a template engine.
    
    Before: Go binary shells out to claude CLI for LLM calls (broken -
    claude can't call itself from within Claude Code).
    
    After: Skill tells Claude Code to use its own tools. WebFetch reads
    API docs. Claude Code understands them and writes the YAML spec.
    Go binary renders templates. Claude Code edits the output to improve it.
    
    The internal/llm/ and internal/llmpolish/ packages remain as a fallback
    for CLI-only users running printing-press generate --polish from their
    terminal without Claude Code.
    
    Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---
 .../2026-03-25-refactor-skill-is-the-brain-plan.md | 198 ++++++++++
 skills/printing-press/SKILL.md                     | 429 ++++++++-------------
 2 files changed, 369 insertions(+), 258 deletions(-)

diff --git a/docs/plans/2026-03-25-refactor-skill-is-the-brain-plan.md b/docs/plans/2026-03-25-refactor-skill-is-the-brain-plan.md
new file mode 100644
index 00000000..4d273e7c
--- /dev/null
+++ b/docs/plans/2026-03-25-refactor-skill-is-the-brain-plan.md
@@ -0,0 +1,198 @@
+---
+title: "The Skill IS the Brain - Claude Code Drives Everything"
+type: refactor
+status: active
+date: 2026-03-25
+---
+
+# The Skill IS the Brain
+
+## The Mistake
+
+I put the LLM intelligence inside the Go binary (`internal/llm/`, `internal/llmpolish/`). The Go binary shells out to `claude` CLI to get LLM answers. This is insane because:
+
+1. Claude Code can't call itself (nesting)
+2. The skill runs INSIDE Claude Code - Claude Code IS the LLM
+3. Shelling out to an LLM from a Go binary is solving a problem that doesn't exist
+
+## The Correct Architecture
+
+```
+User: /printing-press Notion
+
+Claude Code (the brain):
+  1. WebFetch Notion API docs
+  2. Read the docs, understand every endpoint
+  3. Write a YAML spec file (Claude Code writes this, not regex)
+  4. Run: printing-press generate --spec <spec-I-wrote>
+  5. Read the generated code
+  6. Edit the help text, examples, README (Claude Code edits files directly)
+  7. Run: go test ./... to verify
+  8. Run scorecard
+  9. Report results
+
+Go binary (the template engine):
+  - Takes a YAML/OpenAPI spec
+  - Renders Go templates
+  - Runs quality gates
+  - That's it. No intelligence.
+```
+
+Claude Code is already running. It already has WebFetch, Read, Write, Edit, Bash, Agent tools. It doesn't need a Go package to shell out to itself.
+
+## What Changes
+
+### The Skill Does the Thinking
+
+The SKILL.md file (`skills/printing-press/SKILL.md`) becomes the orchestrator. It tells Claude Code exactly what to do at each step. The skill IS the product.
+
+**Before (broken):** Go binary tries to be smart by calling `claude` CLI
+**After (correct):** Skill tells Claude Code to be smart, Go binary just renders templates
+
+### The Go Binary Stays Dumb
+
+The Go binary (`printing-press generate`) keeps doing what it does well:
+- Parse OpenAPI specs
+- Parse internal YAML specs
+- Render 14 Go templates
+- Run 7 quality gates
+- That's it
+
+No `internal/llm/`. No `internal/llmpolish/`. No `GenerateFromDocsLLM`. The Go binary is a tool that Claude Code uses, not a brain.
+
+### The Workflow
+
+When someone says `/printing-press Notion`:
+
+**Step 1: Research (Claude Code does this)**
+- WebFetch the Notion API docs page
+- Search GitHub for competing CLIs (GitHub API via WebFetch or Bash curl)
+- Read competitor READMEs
+- Understand the API - every endpoint, auth method, base URL
+- Write a ce:plan file: `docs/plans/<api>-research-plan.md`
+
+**Step 2: Write Spec (Claude Code does this)**
+- Based on research, Claude Code writes a YAML spec file
+- Uses the internal YAML format that `spec.ParseBytes` understands
+- Write to a temp file or to the pipeline directory
+- Claude Code knows the API because it just READ the docs
+
+**Step 3: Generate (Go binary does this)**
+- `printing-press generate --spec <spec-claude-wrote> --output <dir> --force`
+- Template engine renders Go code
+- Quality gates verify it compiles
+- Claude Code reads the output
+
+**Step 4: Polish (Claude Code does this)**
+- Read the generated help descriptions and rewrite them
+- Read the generated examples and improve them
+- Read the generated README and rewrite it to sell the tool
+- Use Edit tool to modify files directly - no Go package needed
+
+**Step 5: Score (Go binary does this)**
+- Run the scorecard: `go test ./internal/pipeline/ -run TestScorecardOnRealCLI`
+- Claude Code reads the scorecard output
+- If score is low, Claude Code writes a fix plan and iterates
+
+**Step 6: Report (Claude Code does this)**
+- Print the scorecard to the user
+- Show before/after (regex spec vs Claude-written spec)
+- Suggest next steps
+
+## Implementation Units
+
+### Unit 1: Rewrite the Skill
+
+**File:** `skills/printing-press/SKILL.md`
+
+The skill needs complete rewrite. It should have these workflows:
+
+**Workflow 0: Natural Language** (`/printing-press Notion`)
+```
+1. Is there an OpenAPI spec? (check KnownSpecs, apis-guru, web search)
+   YES -> go to Workflow 1
+   NO  -> go to step 2
+
+2. WebFetch the API docs URL
+3. Read the docs. Write a YAML spec based on what you find.
+   - List every endpoint (method, path, description)
+   - Identify auth (bearer, api_key, oauth)
+   - Find the base URL
+   - Group by resource
+   - Write spec to /tmp/<api>-spec.yaml
+
+4. Run: printing-press generate --spec /tmp/<api>-spec.yaml --output ./<api>-cli --force
+5. If quality gates fail, read the errors, fix the spec, regenerate
+6. Read the generated --help output, rewrite any bad descriptions using Edit tool
+7. Read the generated README.md, rewrite to sell the tool using Edit tool
+8. Run the scorecard, report results
+```
+
+**Workflow 1: From Spec** (`/printing-press --spec <url>`)
+```
+1. Run: printing-press generate --spec <url> --output ./<name>-cli --force
+2. Read generated code
+3. Polish with Edit tool if --polish flag
+4. Run scorecard, report
+```
+
+**Workflow 2: From Docs** (`/printing-press --docs <url>`)
+```
+1. WebFetch the docs URL
+2. Write YAML spec (Claude Code does this, not regex)
+3. Run: printing-press generate --spec <spec> --output ./<name>-cli --force
+4. Polish, score, report
+```
+
+**Workflow 5: Scorecard** (`/printing-press score <dir>`)
+```
+1. Run: SCORECARD_CLI_DIR=<dir> go test ./internal/pipeline/ -run TestScorecardOnRealCLI -v
+2. Read output, present to user
+```
+
+### Unit 2: Remove Go LLM Packages (or Mark as Optional Fallback)
+
+The `internal/llm/`, `internal/llmpolish/` packages aren't needed when running from Claude Code. Two options:
+
+**Option A: Delete them.** The skill does everything. The Go binary is pure templates.
+
+**Option B: Keep as CLI-only fallback.** When someone runs `printing-press generate --polish` from their terminal (without Claude Code), the Go binary can still try to shell out to claude/codex for polish. But this is a fallback, not the primary path.
+
+**Recommendation: Option B.** Keep the code but make it clear in docs that the primary path is through the skill. The Go packages are for headless/CI use when Claude Code isn't available.
+
+### Unit 3: Test End-to-End from Claude Code
+
+After rewriting the skill, test it by invoking it:
+
+```
+/printing-press Notion
+```
+
+This should:
+1. Claude Code WebFetches Notion docs
+2. Claude Code writes a YAML spec with 20+ endpoints
+3. Go binary generates the CLI
+4. Claude Code polishes the output
+5. Scorecard runs
+6. All within this Claude Code session - no nesting, no external calls
+
+**This is the test that proves the architecture works.**
+
+## Acceptance Criteria
+
+- [ ] Skill SKILL.md rewritten with Claude Code as the brain
+- [ ] `/printing-press Notion` works end-to-end in Claude Code
+- [ ] Claude Code writes the YAML spec (not regex, not shelled-out LLM)
+- [ ] Generated Notion CLI has 15+ commands (Claude Code reads docs properly)
+- [ ] Claude Code polishes help text by editing files directly
+- [ ] Scorecard runs and reports results
+- [ ] No `claude` CLI nesting required
+- [ ] Go binary stays dumb - just templates + quality gates
+
+## Scope Boundaries
+
+- Do NOT delete internal/llm/ or internal/llmpolish/ (keep as CLI fallback)
+- Do NOT change the Go template engine
+- Do NOT change the scorecard
+- The skill is the only thing that changes
+- --polish flag on the Go binary still works for CLI-only users
diff --git a/skills/printing-press/SKILL.md b/skills/printing-press/SKILL.md
index 8baa54db..ae0ef2cf 100644
--- a/skills/printing-press/SKILL.md
+++ b/skills/printing-press/SKILL.md
@@ -1,7 +1,7 @@
 ---
 name: printing-press
-description: Generate production Go CLIs from API descriptions or OpenAPI specs. Say an API name and get a compiled CLI binary. Supports autonomous multi-phase pipeline mode.
-version: 0.4.0
+description: Generate production Go CLIs from any API. Claude Code is the brain - it researches, writes specs, generates, polishes, and scores. Say an API name and get a complete CLI.
+version: 0.5.0
 allowed-tools:
   - Bash
   - Read
@@ -21,333 +21,246 @@ allowed-tools:
 
 # /printing-press
 
-Generate a production Go CLI from an API description or spec file.
+Generate a production Go CLI from any API. Claude Code does the thinking. The Go binary does the template rendering.
+
+## Architecture
+
+```
+Claude Code (brain)  ->  printing-press binary (template engine)  ->  Claude Code (polish)
+  researches API           renders Go templates                       improves help text
+  writes YAML spec         runs quality gates                         rewrites README
+  analyzes competitors     deterministic, fast                        scores output
+```
 
 ## Quick Start
 
 ```
-/printing-press Stytch authentication API
+/printing-press Notion
+/printing-press Plaid payments API
 /printing-press --spec ./openapi.yaml
-/printing-press --spec https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json
+/printing-press --docs https://developers.notion.com/reference --name notion
 ```
 
 ## Prerequisites
 
-- Go 1.21+ installed (`go version`)
+- Go 1.21+ installed
 - The printing-press repo at `~/cli-printing-press`
+- printing-press binary built: `cd ~/cli-printing-press && go build -o ./printing-press ./cmd/printing-press`
 
 ## Workflows
 
 ### Workflow 0: Natural Language (Primary)
 
-When the user provides an API name or description (no --spec flag):
-
-**Step 1: Parse intent**
-Extract the API name from the user's message. Examples:
-- "Stytch authentication API" -> API name: "Stytch"
-- "Stripe payments" -> API name: "Stripe"
-- "the Loops email API" -> API name: "Loops"
+When the user provides an API name or description:
 
-**Step 2: Check known-specs registry**
-Read `~/cli-printing-press/skills/printing-press/references/known-specs.md` and search for the API name.
+**Step 1: Parse intent and find the spec**
 
-If found: use the spec URL from the registry. Go to Step 4.
+Extract the API name. Then search for an OpenAPI spec:
 
-**Step 3: Search for OpenAPI spec online**
-If not in registry, search in this order:
+1. Check KnownSpecs in `~/cli-printing-press/internal/pipeline/discover.go`
+2. Check catalog: `ls ~/cli-printing-press/catalog/<name>.yaml`
+3. WebSearch: `"<api-name>" openapi spec site:github.com`
+4. Try common URLs: `https://raw.githubusercontent.com/<org>/openapi/main/openapi.yaml`
 
-1. WebSearch: `"<api-name>" openapi spec site:github.com`
-2. WebSearch: `"<api-name>" openapi.yaml OR openapi.json`
-3. Try common URL patterns:
-   - `https://raw.githubusercontent.com/<org>/openapi/main/openapi.yaml`
-   - `https://api.<domain>/openapi.json`
+If OpenAPI spec found: go to Step 3 (generate from spec).
+If no spec found: go to Step 2 (research and write spec).
 
-If a URL is found, verify it's accessible with a brief WebFetch check (first 200 bytes should contain `openapi:` or `"openapi"` or `swagger:`).
+**Step 2: Research the API and write a spec (Claude Code does this)**
 
-If no spec found: go to Step 6 (generate from docs).
+This is where Claude Code IS the brain. No regex. No shelling out to another LLM.
 
-**Step 4: Download and generate from OpenAPI spec**
+a. **Fetch the API docs:**
+```
+WebFetch the API documentation URL (e.g., developers.notion.com/reference)
+```
 
+b. **Research competitors:**
+```
+WebSearch: "<api-name> cli" site:github.com
+```
+For each competitor found, note: name, stars, language, last updated.
+
+c. **Read the docs and identify EVERY endpoint:**
+Read the fetched docs content. List every API endpoint you find:
+- HTTP method (GET, POST, PUT, PATCH, DELETE)
+- Path (/v1/databases, /v1/pages/{id})
+- Description
+- Parameters (path params, query params, body fields)
+- Auth method (Bearer, API key, OAuth)
+- Base URL
+
+d. **Write the YAML spec:**
+Read the spec format reference: `cat ~/cli-printing-press/skills/printing-press/references/spec-format.md`
+
+Write a complete YAML spec to `/tmp/<name>-spec.yaml` with ALL endpoints found. Include:
+- name (kebab-case)
+- description
+- base_url
+- auth (type, header, env_vars)
+- resources (group endpoints by resource)
+- endpoints (method, path, params, body)
+
+e. **Generate:**
 ```bash
-# Download the spec
-curl -sL -o /tmp/printing-press-spec-$$.yaml "<spec-url>"
-
-# Generate the CLI
 cd ~/cli-printing-press && ./printing-press generate \
-  --spec /tmp/printing-press-spec-$$.yaml \
+  --spec /tmp/<name>-spec.yaml \
   --output ./<name>-cli \
-  --validate
+  --force
 ```
 
-If the binary doesn't exist, build it first:
+f. **If quality gates fail:** Read the error. Fix the YAML spec. Regenerate. Max 3 retries.
+
+g. Go to Step 4 (polish).
+
+**Step 3: Generate from OpenAPI spec**
+
 ```bash
-cd ~/cli-printing-press && go build -o ./printing-press ./cmd/printing-press
+cd ~/cli-printing-press && ./printing-press generate \
+  --spec "<spec-url>" \
+  --output ./<name>-cli \
+  --force --lenient
 ```
 
-If all 7 quality gates pass: go to Step 7 (present result).
-If gates fail: go to Step 5 (retry).
-
-**Step 5: Retry on quality gate failure**
+Use `--lenient` for specs with broken $refs (PagerDuty, Intercom).
 
-Read the error output carefully. Common fixes:
-- "newline in string" -> descriptions have unescaped newlines (should not happen with current templates, but if it does, the spec description needs cleaning)
-- "undefined" -> a template function is missing a type mapping
-- Module errors -> run `go mod tidy` in the output directory
+If all 7 quality gates pass: go to Step 4 (polish).
+If gates fail: read error, fix, retry (max 3).
 
-If the error is in the spec (not the templates):
-1. Read the spec file
-2. Fix the issue (e.g., remove problematic descriptions, fix types)
-3. Delete the output directory
-4. Re-run `printing-press generate`
+**Step 4: Polish (Claude Code does this)**
 
-Max 2 retries. If still failing after retries, present the error to the user and suggest they inspect the generated code.
+Read the generated code and improve it directly:
 
-**Step 6: Generate internal YAML spec from documentation**
+a. **Improve help descriptions:**
+Read each command file in `<output>/internal/cli/*.go`. Find `Short:` strings. If any are jargon-heavy spec descriptions, use Edit to rewrite them to be developer-friendly (under 80 chars, starts with a verb).
 
-When no OpenAPI spec exists:
+b. **Improve examples:**
+Read each command's `Example:` string. If it uses generic values like "value" or "<id>", use Edit to replace with realistic values (e.g., "usr_abc123", "2026-01-01", "user@example.com").
 
-1. Read the API documentation using WebFetch
-2. Read `~/cli-printing-press/skills/printing-press/references/spec-format.md` for the YAML format
-3. Generate a YAML spec following the format exactly. Include:
-   - name (kebab-case)
-   - base_url (from API docs)
-   - auth config (type, header, env_vars)
-   - resources (group endpoints logically)
-   - endpoints (method, path, params, body)
-   - types (for response objects)
-4. Save to `./<name>-spec.yaml`
-5. Run `printing-press generate --spec ./<name>-spec.yaml`
-6. On failure: read error, fix YAML, retry (max 2 attempts)
-7. Present result with note: "Generated spec saved to ./<name>-spec.yaml - you can edit and regenerate."
+c. **Improve README:**
+Read `<output>/README.md`. If the description is generic spec text, use Edit to rewrite:
+- Add a one-line hook that makes developers want to install it
+- Add "Why This Exists" if there's no official CLI for this API
+- Ensure Quick Start has real 3-command workflow
 
-**Step 7: Present result**
+d. **Note: this step is optional.** If the user doesn't want polish, skip it. If the generated output is already good, skip it. Use judgment.
 
-Show the user:
-1. What was generated (directory name, number of resources/endpoints)
-2. Example commands they can try
-3. How to install: `cd <name>-cli && go install ./cmd/<name>-cli`
-4. If resource/endpoint limits were hit, note what was truncated
+**Step 5: Score (optional)**
 
-Example output:
+Run the Steinberger scorecard:
+```bash
+cd ~/cli-printing-press && SCORECARD_CLI_DIR=./<name>-cli SCORECARD_PIPELINE_DIR=/tmp/<name>-pipeline \
+  go test ./internal/pipeline/ -run TestScorecardOnRealCLI -v 2>&1 | tail -20
 ```
-Generated stytch-cli with 8 resources and 42 endpoints.
 
-Try it:
-  cd stytch-cli
-  go install ./cmd/stytch-cli
-  stytch-cli --help
-  stytch-cli users list --limit 10
-  stytch-cli doctor
+Report the score to the user.
 
-All 7 quality gates passed.
-```
+**Step 6: Present result**
+
+Show:
+1. What was generated (directory, resources, commands)
+2. Example commands to try
+3. How to install: `cd <name>-cli && go install ./cmd/<name>-cli`
+4. Steinberger score if computed
+5. Competitors found (if any)
+6. Note if spec was auto-written vs from OpenAPI
 
 ### Workflow 1: From Spec File
 
-When the user provides `--spec <local-path>`:
+When `--spec <local-path>`:
+1. Verify file exists
+2. `cd ~/cli-printing-press && ./printing-press generate --spec <path> [--output <dir>] --lenient`
+3. Optional: polish (Step 4 above)
+4. Present result
 
-1. Verify the file exists
-2. Run `cd ~/cli-printing-press && ./printing-press generate --spec <path> [--output <dir>]`
-3. Present result (Step 7 above)
+### Workflow 2: From Docs URL
 
-### Workflow 2: From URL
+When `--docs <url>`:
+1. WebFetch the docs URL
+2. Claude Code reads the docs and writes a YAML spec (Step 2 above)
+3. Generate from the spec
+4. Polish
+5. Present result
 
-When the user provides `--spec <url>`:
+### Workflow 3: Submit to Catalog
 
-1. Download with `curl -sL -o /tmp/printing-press-spec-$$.yaml "<url>"`
-2. Run `cd ~/cli-printing-press && ./printing-press generate --spec /tmp/printing-press-spec-$$.yaml [--output <dir>]`
-3. Present result (Step 7 above)
+When `submit <name>`:
+1. Gather metadata (ask user for display name, description, category, homepage)
+2. Write `~/cli-printing-press/catalog/<name>.yaml`
+3. `git checkout -b catalog/<name> && git add catalog/<name>.yaml && git commit && git push && gh pr create`
+4. Present PR URL
 
-### Workflow 3: Submit to Catalog
+### Workflow 4: Autonomous Pipeline
 
-When the user invokes `/printing-press submit <name>`:
-
-**Step 1: Gather metadata**
-Ask the user for:
-- API display name (e.g., "Stripe")
-- One-line description
-- Category (payments, auth, email, developer-tools, project-management, communication, crm, example)
-- Homepage URL
-- OpenAPI spec URL (if they used one)
-
-**Step 2: Create catalog entry**
-Write a YAML file to `~/cli-printing-press/catalog/<name>.yaml`:
-
-```yaml
-name: <name>
-display_name: <display_name>
-description: <description>
-category: <category>
-spec_url: <spec_url>
-spec_format: <yaml|json>
-openapi_version: "3.0"
-tier: community
-verified_date: "<today's date>"
-homepage: <homepage>
-notes: <any notes>
-```
+When `print <api-name>`:
 
-**Step 3: Open a PR**
-```bash
-cd ~/cli-printing-press
-git checkout -b catalog/<name>
-git add catalog/<name>.yaml
-git commit -m "feat(catalog): add <display_name> catalog entry"
-git push -u origin catalog/<name>
-gh pr create --title "feat(catalog): add <display_name>" --body "Adds catalog entry for <display_name> API.
-
-Spec URL: <spec_url>
-Category: <category>
-Tier: community
-
-Tested locally - generated CLI compiles and passes all quality gates."
-```
+Uses the multi-phase pipeline with ce:plan -> ce:work loops:
 
-**Step 4: Present the PR URL**
-Show the user the PR link and note that CI will validate the entry.
+| Phase | What Claude Code Does |
+|-------|----------------------|
+| Preflight | Verify Go, download spec, cache conventions |
+| Research | WebSearch competitors, WebFetch their READMEs, analyze |
+| Scaffold | Write spec (if needed), run `printing-press generate` |
+| Enrich | Read generated output, identify missing endpoints, improve spec |
+| Regenerate | Re-run generator with enriched spec |
+| Review | Run scorecard, dogfood Tier 1, polish output |
+| Comparative | Score vs competitors |
+| Ship | Git init, write report |
 
-### Workflow 4: Autonomous Pipeline
+Each phase: read the plan seed -> expand with ce:plan -> execute with ce:work -> write next phase's plan -> chain to next session.
+
+**Step 1: Initialize**
+```bash
+cd ~/cli-printing-press && go build -o ./printing-press ./cmd/printing-press
+./printing-press print <api-name> [--output <dir>] [--force]
+```
 
-| Phase | Purpose |
-|-------|---------|
-| 0. Preflight | Validate environment and discover the OpenAPI spec |
-| 1. Scaffold | Generate the initial CLI and verify quality gates |
-| 2. Enrich | Deep-read the spec and research missing hints |
-| 3. Regenerate | Merge enrichments and re-generate the CLI |
-| 4. Review | Static quality checks + autonomous dogfooding against real API |
-| 5. Ship | Initialize git repo, commit, write report |
+**Step 2: Phase execution loop**
+For each phase where `plan_status` is not "completed":
+a. Read plan file
+b. If `status: seed`: run `Skill("compound-engineering:ce:plan", plan_path)` to expand
+c. If `status: active`: run `Skill("compound-engineering:ce:work", plan_path)` to execute
+d. Update state.json
+e. Chain to next session via CronCreate (30s)
 
-The Review phase dogfoods the generated CLI in three tiers:
-- **Tier 1** (always): version, doctor, help, dry-run, output modes - no credentials needed
-- **Tier 2** (if credentials available): list, get, auth error handling - read-only API calls
-- **Tier 3** (sandbox APIs only): create/delete roundtrip with cleanup - write operations on safe test servers
+Budget gate: stop after 3 hours. Write morning report.
 
-Results feed a combined quality score (static 0-50 + dogfood 0-50 = total 0-100).
+### Workflow 5: Resume Pipeline
 
-When the user says "print <api-name>":
+When `resume <api-name>`:
+1. Load state.json
+2. Budget gate first
+3. Continue from next incomplete phase
 
-**Step 1: Initialize**
-- Build the press binary: `go build -o ./printing-press ./cmd/printing-press`
-- Run: `./printing-press print <api-name> [--output <dir>] [--force]`
-- This creates `docs/plans/<api-name>-pipeline/` with 6 plan seeds + `state.json`
-- Show the user: API name, spec URL, output directory, and phase list
+### Workflow 6: Scorecard
 
-**Step 2: Heartbeat safety net**
-Schedule a heartbeat to resume if the session dies unexpectedly:
+When `score <dir>`:
 ```bash
-RESUME_MIN=$(date -v+45M '+%M')
-RESUME_HOUR=$(date -v+45M '+%H')
-RESUME_DAY=$(date -v+45M '+%d')
-RESUME_MONTH=$(date -v+45M '+%m')
+cd ~/cli-printing-press && SCORECARD_CLI_DIR=<dir> SCORECARD_PIPELINE_DIR=/tmp/score-pipeline \
+  go test ./internal/pipeline/ -run TestScorecardOnRealCLI -v
 ```
-Then CronCreate with cron expression `$RESUME_MIN $RESUME_HOUR $RESUME_DAY $RESUME_MONTH *` and prompt: `/printing-press resume <api-name>`
-
-**Step 3: Phase execution loop**
-For each phase in state.json where `plan_status` is not "completed":
-
-a. Read the plan file at the phase's `plan_path`
-b. Check the `status` field in the plan's YAML frontmatter:
-   - If `status: seed` - the plan is a thin prompt that needs expansion:
-     1. Run `Skill("compound-engineering:ce:plan", plan_path)` to expand the seed into a full plan with research
-     2. ce:plan overwrites the file with the expanded plan (frontmatter becomes `status: active`)
-     3. Update state.json: set `plan_status` to "expanded":
-     ```bash
-     python3 -c "
-     import json
-     s = json.load(open('docs/plans/<api-name>-pipeline/state.json'))
-     s['phases']['<phase>']['plan_status'] = 'expanded'
-     json.dump(s, open('docs/plans/<api-name>-pipeline/state.json', 'w'), indent=2)
-     "
-     ```
-   - If `status: active` - the plan is already expanded, ready for execution:
-     1. Run `Skill("compound-engineering:ce:work", plan_path)` to implement, test, and check off criteria
-     2. Update state.json: mark phase completed:
-     ```bash
-     python3 -c "
-     import json
-     s = json.load(open('docs/plans/<api-name>-pipeline/state.json'))
-     s['phases']['<phase>']['status'] = 'completed'
-     s['phases']['<phase>']['plan_status'] = 'completed'
-     json.dump(s, open('docs/plans/<api-name>-pipeline/state.json', 'w'), indent=2)
-     "
-     ```
-c. Run budget gate (Step 4)
-d. If more phases remain and budget gate says CONTINUE:
-   - CronCreate 30 seconds from now: `/printing-press resume <api-name>`
-   - Print: `"[phase] complete. Chaining to [next_phase] in 30s with fresh context..."`
-   - END SESSION (the cron fires a new session with fresh context)
-
-**Note:** Each phase may take TWO sessions - one for ce:plan expansion, one for ce:work execution. This is by design: ce:plan gets a fresh context window for research, ce:work gets a fresh context window for implementation.
-
-**Step 4: Budget gate (between every phase)**
+
+### Workflow 7: Full Test Run
+
+When `test` or `fullrun`:
 ```bash
-python3 -c "
-import json, datetime
-s = json.load(open('docs/plans/<api-name>-pipeline/state.json'))
-started = datetime.datetime.fromisoformat(s['started_at'].replace('Z', '+00:00'))
-elapsed = (datetime.datetime.now(datetime.timezone.utc) - started).total_seconds() / 3600
-print('STOP' if elapsed > 3 else 'CONTINUE')
-print(f'Elapsed: {elapsed:.1f}h / 3h budget')
-"
-```
-If STOP: go to Step 6 (morning report), do NOT chain.
-
-**Step 5: Error handling**
-- If ce:plan fails on a phase: log error to state.json errors array, retry once. If still fails, mark phase "failed", skip to next phase.
-- If ce:work fails: retry once with same plan. If still fails, mark "failed", skip to next.
-- If 2+ consecutive phases fail: write morning report and STOP. Do not chain.
-- NEVER die silently. Always update state.json before ending a session.
-- If CronCreate fails: print manual resume command as fallback: `claude "/printing-press resume <api-name>"`
-
-**Step 6: Morning report**
-Write `docs/plans/<api-name>-pipeline/report.md`:
-```markdown
-# Pipeline Report: <api-name>
-
-- **API:** <api-name>
-- **Spec URL:** <spec_url from state.json>
-- **Output:** <output_dir from state.json>
-- **Phases completed:** N/M
-- **Total elapsed:** Xh Ym
-- **Status:** completed | budget-exhausted | failed
-
-## Phase Results
-| Phase | Status | Notes |
-|-------|--------|-------|
-| preflight | completed | ... |
-| scaffold | completed | ... |
-| ... | ... | ... |
-
-## Next Steps
-- [ ] Review generated CLI at <output_dir>
-- [ ] Run `<cli-name> doctor` to verify
-- [ ] Submit to catalog: `/printing-press submit <api-name>`
+cd ~/cli-printing-press && FULL_RUN=1 go test ./internal/pipeline/ -run TestFullRun -v -timeout 10m
 ```
 
-### Workflow 5: Resume Pipeline
+## Key Principle
 
-When the user says "resume <api-name>" or the `--resume` flag is detected:
+**Claude Code IS the LLM brain.** The printing-press Go binary is a template engine. When the skill says "read the API docs and write a spec," that means YOU (Claude Code) use WebFetch to read the docs and Write to create the spec file. You don't shell out to another LLM. You ARE the LLM.
 
-1. Load `docs/plans/<api-name>-pipeline/state.json`
-2. **MANDATORY:** Run budget gate FIRST (Workflow 4 Step 4) before any work
-3. If STOP: write morning report if not already written, print summary, exit
-4. If CONTINUE:
-   - Show status table: which phases are done, which is next
-   - Delete stale heartbeat crons: CronList, then CronDelete any matching `/printing-press resume`
-   - Schedule new heartbeat (45 min from now, same as Workflow 4 Step 2)
-   - Go to Workflow 4 Step 3 (phase execution loop) starting from the next incomplete phase
+The Go binary's `internal/llm/` and `internal/llmpolish/` packages exist as a fallback for when someone runs `printing-press generate --polish` from their terminal without Claude Code. But inside this skill, YOU do the thinking.
 
 ## Safety Gates
 
-- **Preview before generating**: Show the API name, base URL, and estimated resource count before running the generator
-- **Output directory conflict**: If the output directory already exists, ask the user before overwriting
-- **Untrusted specs**: If the spec was downloaded from a URL not in the known-specs registry, note this to the user
+- Preview before generating: show API name, base URL, estimated resources
+- Output directory conflict: ask before overwriting
+- Untrusted specs: note if spec is from a URL not in known-specs registry
 
 ## Limitations
 
-- Only generates Go CLIs (no Bash, Python, etc.)
-- OpenAPI 3.0+ and Swagger 2.0 supported; other formats are not
-- Large APIs (500+ endpoints) are automatically truncated to 50 resources / 50 endpoints per resource
-- No GraphQL support
-- Pipeline mode requires Compound Engineering plugin (`compound-engineering:ce:plan` and `compound-engineering:ce:work`)
-- Pipeline budget gate is 3 hours max
+- Only generates Go CLIs
+- OpenAPI 3.0+ and Swagger 2.0 supported
+- Large APIs truncated to 50 resources / 50 endpoints per resource
+- No GraphQL support (but Claude Code can write a YAML spec that wraps GraphQL, as we did for Linear)

← 95e26838 feat(scorecard): add breadth dimension + fix LLM runner + in  ·  back to Cli Printing Press  ·  fix(skill): always research, polish, and score - never skip 4ee08952 →