← back to Cli Printing Press
feat(skills): add /printing-press-polish standalone skill (#90)
03487972b10a619115de5059ed8e70441e05e6b6 · 2026-03-30 22:13:46 -0700 · Matt Van Horn
Extract and replace the inline emboss mode with a standalone polish skill
that automatically fixes generated CLIs for publish readiness. Runs
diagnostics (dogfood, verify, scorecard, go vet), fixes verify failures,
dead code, descriptions, and README, reports the before/after delta, and
offers to publish. Always suggested after shipcheck in Phase 6 alongside
publish and retro options.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Files touched
A docs/brainstorms/2026-03-30-printing-press-polish-skill-requirements.mdA docs/plans/2026-03-30-012-feat-printing-press-polish-skill-plan.mdA docs/retros/2026-03-30-redfin-retro.mdA skills/printing-press-polish/SKILL.mdM skills/printing-press/SKILL.md
Diff
commit 03487972b10a619115de5059ed8e70441e05e6b6
Author: Matt Van Horn <mvanhorn@users.noreply.github.com>
Date: Mon Mar 30 22:13:46 2026 -0700
feat(skills): add /printing-press-polish standalone skill (#90)
Extract and replace the inline emboss mode with a standalone polish skill
that automatically fixes generated CLIs for publish readiness. Runs
diagnostics (dogfood, verify, scorecard, go vet), fixes verify failures,
dead code, descriptions, and README, reports the before/after delta, and
offers to publish. Always suggested after shipcheck in Phase 6 alongside
publish and retro options.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---
...-30-printing-press-polish-skill-requirements.md | 78 +++++
...30-012-feat-printing-press-polish-skill-plan.md | 188 ++++++++++++
docs/retros/2026-03-30-redfin-retro.md | 256 ++++++++++++++++
skills/printing-press-polish/SKILL.md | 322 +++++++++++++++++++++
skills/printing-press/SKILL.md | 54 ++--
5 files changed, 867 insertions(+), 31 deletions(-)
diff --git a/docs/brainstorms/2026-03-30-printing-press-polish-skill-requirements.md b/docs/brainstorms/2026-03-30-printing-press-polish-skill-requirements.md
new file mode 100644
index 00000000..9d659293
--- /dev/null
+++ b/docs/brainstorms/2026-03-30-printing-press-polish-skill-requirements.md
@@ -0,0 +1,78 @@
+---
+date: 2026-03-30
+topic: printing-press-polish-skill
+---
+
+# printing-press-polish: Standalone CLI Improvement Skill
+
+## Problem Frame
+
+After the printing press generates a CLI, there's always a gap between "it compiles" and "it's publish-ready." This gap currently requires manual work: fixing verify failures, removing dead code, cleaning up descriptions, writing the README, and other CLI-specific improvements that the generator can't anticipate because they depend on the unique characteristics of each API.
+
+Today this work happens ad-hoc during the session (or not at all). The Redfin session required 5,504 lines of hand-written code and multiple fix iterations to go from 52% verify to 96%. That manual process should be a repeatable skill.
+
+The existing "polish" mode inside the main printing-press skill is awkwardly named (polishing during printing is confusing) and underspecified (5 bullet points). It should be extracted into a standalone skill with clear phases, autonomy to fix everything, and a publish offer at the end.
+
+## Requirements
+
+**Skill Identity**
+- R1. Standalone Claude Code skill at `skills/printing-press-polish/SKILL.md`
+- R2. Invocable as `/printing-press-polish <cli-name-or-path>`
+- R3. Name resolution: accept CLI name ("redfin"), full name ("redfin-pp-cli"), or path ("~/printing-press/library/redfin-pp-cli")
+- R4. If name doesn't resolve, search `$PRESS_LIBRARY/` for close matches and let the user pick (same UX as current polish name resolution)
+
+**Diagnostics Phase**
+- R5. Run all three diagnostic tools against the CLI: `printing-press dogfood`, `printing-press verify`, `printing-press scorecard`
+- R6. Parse results into a structured finding list with categories: verify failures, dead code, dead flags, path issues, README gaps, description issues, example gaps, data pipeline issues
+- R7. Report the baseline: scorecard total, verify pass rate, dogfood verdict, with specific failing items listed
+
+**Fix Phase**
+- R8. Fix everything automatically without asking for approval. The user reviews the diff after, not before.
+- R9. Fix categories (in priority order):
+ 1. Verify failures (cobra Args constraints, dry-run handling, missing mock arg support)
+ 2. Dead code (unused functions, unused imports, stale files like unregistered promoted commands)
+ 3. CLI description and metadata (root Short/Long descriptions, command examples)
+ 4. README (update to reflect actual commands, install instructions, quick start)
+ 5. Dogfood-flagged issues (path validity, auth mismatches, example drift)
+- R10. After fixing, rebuild the binary and re-run all three diagnostic tools
+- R11. Report the delta: before/after scorecard, verify pass rate, specific improvements made
+
+**Publish Offer**
+- R12. After showing the delta, offer to publish (same flow as Phase 6 in the main skill)
+- R13. If the CLI still has critical issues after the fix pass, report them and offer: fix again, publish anyway, or done
+
+**Integration with Main Skill**
+- R14. The main printing-press skill should always suggest `/printing-press-polish` as an option after shipcheck, alongside publish and retro
+- R15. Remove the inline "polish mode" from the main printing-press skill. Replace with a reference to the standalone skill.
+- R16. The in-flow improvement loop (shipcheck fix iterations) keeps its current behavior but drops the "polish" name
+
+## Success Criteria
+
+- Running `/printing-press-polish redfin` on the Redfin CLI as-generated (before manual fixes) would automatically achieve the same 52% -> 96% verify improvement we did manually
+- The skill completes in one pass for typical CLIs (no iterative user interaction needed)
+- The main printing-press skill no longer contains polish mode instructions
+
+## Scope Boundaries
+
+- Does not add new features to the CLI (that's the absorb manifest's job during generation)
+- Does not improve the printing-press generator itself (that's /printing-press-retro)
+- Does not re-run the research or generation phases
+- Does not handle APIs that require re-generation (broken specs, wrong endpoints)
+
+## Key Decisions
+
+- **Standalone skill, not embedded mode**: Polishing mid-print is confusing. A separate invocation after printing makes the mental model clearer.
+- **Full autonomy**: Fix everything, report results. No approval gates. The user sees the before/after delta and can review the diff. This matches the "fix everything, report results" pattern.
+- **Includes publish offer**: One continuous flow from "CLI needs work" to "CLI is published." No handoff friction.
+- **Always suggested after shipcheck**: Not gated on score threshold. Even a 90/100 CLI might benefit from polishing.
+
+## Outstanding Questions
+
+### Deferred to Planning
+- [Affects R9][Technical] What's the exact fix strategy for each verify failure type? The Redfin session showed that cobra Args constraints are the main culprit, but other CLIs may have different patterns.
+- [Affects R6][Needs research] Should the diagnostics phase also run `go vet` and check for compilation warnings beyond what dogfood/verify/scorecard cover?
+- [Affects R15][Technical] How to cleanly remove polish from SKILL.md without breaking the existing polish name resolution and library search logic that other parts of the skill reference.
+
+## Next Steps
+
+`/ce:plan` for structured implementation planning
diff --git a/docs/plans/2026-03-30-012-feat-printing-press-polish-skill-plan.md b/docs/plans/2026-03-30-012-feat-printing-press-polish-skill-plan.md
new file mode 100644
index 00000000..3e06797c
--- /dev/null
+++ b/docs/plans/2026-03-30-012-feat-printing-press-polish-skill-plan.md
@@ -0,0 +1,188 @@
+---
+title: "feat: /printing-press-polish standalone skill"
+type: feat
+status: completed
+date: 2026-03-30
+origin: docs/brainstorms/2026-03-30-printing-press-polish-skill-requirements.md
+---
+
+# feat: /printing-press-polish standalone skill
+
+## Overview
+
+Create a standalone `/printing-press-polish` skill that automatically fixes a generated CLI to pass verify, removes dead code, cleans up descriptions and README, and offers to publish. Extract and replace the inline "Emboss Mode" from the main printing-press skill. Always suggest after shipcheck.
+
+## Problem Frame
+
+Generated CLIs compile and have working API commands, but consistently fail verify (52% for Redfin) due to mechanical issues the generator can't prevent: cobra Args constraints that block no-arg testing, dead helper functions, placeholder descriptions, and generic READMEs. This manual fix work should be automated into a skill that runs diagnostics, fixes everything, reports the delta, and offers publish. (see origin: `docs/brainstorms/2026-03-30-printing-press-polish-skill-requirements.md`)
+
+## Requirements Trace
+
+- R1. Standalone skill at `skills/printing-press-polish/SKILL.md`
+- R2. Invocable as `/printing-press-polish <cli-name-or-path>`
+- R3-R4. Name resolution with fuzzy matching and user selection
+- R5-R7. Diagnostics: dogfood + verify + scorecard + go vet, parsed into categorized findings
+- R8-R11. Fix: autonomous fixes in priority order, rebuild, re-diagnose, report delta
+- R12-R13. Publish offer after showing delta
+- R14-R16. Integration: suggest after shipcheck, remove emboss from main skill
+
+## Scope Boundaries
+
+- Does not add features to the CLI (that's the absorb manifest)
+- Does not improve the generator (that's /printing-press-retro)
+- Does not re-run research or generation
+- Does not handle broken specs needing re-generation
+
+## Context & Research
+
+### Relevant Code and Patterns
+
+- `~/.claude/skills/printing-press-retro/SKILL.md` - Pattern for standalone skill structure (frontmatter, setup, phases)
+- `skills/printing-press/SKILL.md:74-97` - Current emboss mode to extract and replace
+- `skills/printing-press/SKILL.md:1520-1570` - Phase 6 publish flow to extend with polish option
+- `skills/printing-press/SKILL.md:84-86` - Name resolution pattern to reuse
+- `skills/printing-press-publish/` - Publish skill invoked at the end
+
+### Institutional Learnings
+
+- Scorecard accuracy: dead code detection uses `Count >= 2` to avoid matching the function's own definition (see `docs/solutions/logic-errors/scorecard-accuracy-broadened-pattern-matching-2026-03-27.md`)
+- Validation must not mutate source directory (see `docs/solutions/best-practices/validation-must-not-mutate-source-directory-2026-03-29.md`) - relevant because polish DOES mutate; it should be explicit about this
+
+## Key Technical Decisions
+
+- **Skill file, not Go code**: This is a SKILL.md (LLM instructions) like the retro skill, not a new Go command in the binary. The LLM reads diagnostic output and applies fixes using standard tools (Edit, Write, Bash).
+- **Diagnostics before fixes**: Run all diagnostic tools first to establish baseline. Don't fix piecemeal.
+- **Priority-ordered fix categories**: Verify failures first (biggest impact), then dead code, then cosmetic. Each category has a known fix strategy from the Redfin session.
+- **Replace emboss, don't coexist**: The main skill's emboss section becomes a one-line reference to `/printing-press-polish`. No dual maintenance.
+- **Add `go vet` to diagnostics**: Cheap, catches things dogfood misses (unused variables, unreachable code). Run alongside the three existing tools.
+
+## Open Questions
+
+### Resolved During Planning
+
+- **Fix strategy for verify failures?** Two patterns cover ~95%: (1) Remove cobra `Args:` constraints, add `if len(args) == 0 { return cmd.Help() }` in RunE. (2) Add nil-data guards after API calls in dry-run mode. The skill instructs the LLM to apply these patterns systematically.
+- **How to remove emboss from SKILL.md?** Replace the "Emboss Mode" section (lines 74-97) with a reference: "For second-pass improvements, use `/printing-press-polish <cli-name>`." Update Phase 6 to add polish as an option in the AskUserQuestion.
+
+### Deferred to Implementation
+
+- **Edge cases in name resolution**: The current emboss name resolution logic handles fuzzy matching. Implementation should replicate this exactly from the existing code.
+- **Fix strategies for rare verify failure types**: The Redfin session established the common patterns. New patterns may emerge with other APIs; the skill should have a fallback "read the error, fix it" instruction for unknown failure types.
+
+## Implementation Units
+
+- [ ] **Unit 1: Create the printing-press-polish skill file**
+
+ **Goal:** Write the complete SKILL.md with frontmatter, setup, diagnostics, fix, and publish phases.
+
+ **Requirements:** R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13
+
+ **Dependencies:** None
+
+ **Files:**
+ - Create: `skills/printing-press-polish/SKILL.md`
+
+ **Approach:**
+ - Follow the retro skill's frontmatter pattern: name, description, trigger phrases, allowed-tools
+ - Trigger phrases: "polish", "improve the CLI", "fix verify", "make it publish-ready", "clean up the CLI"
+ - Setup phase: resolve CLI name/path using the same name resolution pattern from emboss (search `$PRESS_LIBRARY/`, fuzzy match, AskUserQuestion for disambiguation)
+ - Diagnostics phase: run `printing-press dogfood`, `printing-press verify`, `printing-press scorecard`, and `go vet` against the CLI. Parse output into categorized findings. Report baseline scores.
+ - Fix phase: apply fixes in priority order. For each category, describe the fix strategy:
+ 1. Verify: remove cobra Args constraints, add len(args) check + cmd.Help(), add nil-data guards for dry-run
+ 2. Dead code: grep for each flagged function, remove if truly unused, clean up imports
+ 3. Descriptions: read root.go Short field, rewrite from API-speak to user-speak using the research brief if available
+ 4. README: generate from actual `--help` output and command list
+ 5. Dogfood: address remaining flagged issues
+ - After fixes: rebuild binary (`go build -o <cli-name> ./cmd/<cli-name>`), re-run all diagnostics
+ - Report: before/after table showing scorecard, verify pass rate, dogfood verdict, specific improvements
+ - Publish: same AskUserQuestion flow as Phase 6 in the main skill. If critical issues remain, offer: fix again, publish anyway, or done
+
+ **Patterns to follow:**
+ - `~/.claude/skills/printing-press-retro/SKILL.md` for overall structure
+ - `skills/printing-press/SKILL.md:84-86` for name resolution
+ - `skills/printing-press/SKILL.md:1544-1570` for publish offer flow
+
+ **Test scenarios:**
+ - Happy path: Invoke `/printing-press-polish redfin` on an as-generated CLI with 52% verify -> skill fixes Args, dead code, README, reports 96%+ verify, offers publish
+ - Happy path: Invoke with full path `~/printing-press/library/redfin-pp-cli` -> resolves correctly
+ - Edge case: CLI name doesn't match -> fuzzy search finds close matches, user picks
+ - Edge case: CLI already at 85+ scorecard -> diagnostics run, few/no fixes needed, reports "already clean"
+ - Edge case: Fixes don't fully resolve verify -> reports remaining issues, offers "fix again" option
+
+ **Verification:** The skill file exists, has valid frontmatter, and contains all phases described in the requirements.
+
+- [ ] **Unit 2: Remove emboss from main printing-press skill**
+
+ **Goal:** Replace inline emboss mode with a reference to `/printing-press-polish`. Drop the "emboss" name from the in-flow improvement loop.
+
+ **Requirements:** R15, R16
+
+ **Dependencies:** Unit 1
+
+ **Files:**
+ - Modify: `skills/printing-press/SKILL.md`
+
+ **Approach:**
+ - Replace lines 74-97 (Emboss Mode section) with: "For second-pass improvements to an existing CLI, use `/printing-press-polish <cli-name>`. See the printing-press-polish skill."
+ - Remove the emboss examples from the top-level usage examples (lines 30-32)
+ - Update any references to "emboss mode" or "Emboss Cycle" elsewhere in the file (line 301)
+ - The in-flow shipcheck fix loop (Phase 4) keeps its behavior but any "emboss" naming is removed
+
+ **Patterns to follow:**
+ - The main skill's existing section structure
+
+ **Test scenarios:**
+ - Happy path: grep for "emboss" in SKILL.md returns only the reference to the new skill (not inline instructions)
+ - Happy path: The shipcheck fix loop in Phase 4 still works as before (no behavioral change)
+ - Edge case: Any cross-references to emboss in other skills or docs are updated
+
+ **Verification:** No inline emboss instructions remain. The reference to `/printing-press-polish` is clear.
+
+- [ ] **Unit 3: Add polish suggestion to Phase 6 publish flow**
+
+ **Goal:** Always suggest `/printing-press-polish` as an option after shipcheck, alongside publish and retro.
+
+ **Requirements:** R14
+
+ **Dependencies:** Unit 1
+
+ **Files:**
+ - Modify: `skills/printing-press/SKILL.md` (Phase 6 section)
+
+ **Approach:**
+ - In the Phase 6 AskUserQuestion, add a new option: "Polish first" - Run `/printing-press-polish` to improve the CLI before publishing
+ - Place it as option 2 (between "Yes - publish now" and "No - I'm done")
+ - For `ship-with-gaps` verdicts, recommend the polish option
+ - If the user picks polish, invoke `/printing-press-polish <cli-name>`. The polish skill handles everything from there including its own publish offer at the end.
+
+ **Patterns to follow:**
+ - Existing AskUserQuestion pattern in Phase 6
+
+ **Test scenarios:**
+ - Happy path: After shipcheck with `ship` verdict -> publish offer includes "Polish first" option
+ - Happy path: After shipcheck with `ship-with-gaps` -> polish is recommended
+ - Happy path: User picks "Polish first" -> skill invokes `/printing-press-polish`
+ - Edge case: `hold` verdict -> Phase 6 skipped entirely (no change to existing behavior)
+
+ **Verification:** The AskUserQuestion in Phase 6 shows the polish option. Selecting it invokes the polish skill.
+
+## System-Wide Impact
+
+- **Interaction graph:** The polish skill invokes printing-press CLI tools (dogfood, verify, scorecard) and the publish skill. It reads and modifies generated CLI source files.
+- **API surface parity:** The polish skill's publish offer must match Phase 6's publish flow exactly to avoid UX divergence.
+- **Unchanged invariants:** The main printing-press skill's generation phases (0-4) are unchanged. The shipcheck fix loop behavior is unchanged (only naming changes).
+
+## Risks & Dependencies
+
+| Risk | Mitigation |
+|------|------------|
+| Polish skill fixes break working code | The fix strategies are mechanical and well-tested from the Redfin session. Rebuild + re-verify after fixes catches regressions. |
+| Removing emboss breaks existing user muscle memory | Low risk - emboss is rarely invoked manually. The main skill's reference to `/printing-press-polish` provides the redirect. |
+| Polish + retro confusion | Clear scope separation: polish fixes the CLI, retro improves the generator. Skill descriptions make this explicit. |
+
+## Sources & References
+
+- **Origin document:** [docs/brainstorms/2026-03-30-printing-press-polish-skill-requirements.md](docs/brainstorms/2026-03-30-printing-press-polish-skill-requirements.md)
+- Retro skill pattern: `~/.claude/skills/printing-press-retro/SKILL.md`
+- Main skill emboss section: `skills/printing-press/SKILL.md:74-97`
+- Main skill Phase 6: `skills/printing-press/SKILL.md:1520-1570`
+- Redfin retro (evidence for fix strategies): `docs/retros/2026-03-30-redfin-retro.md`
diff --git a/docs/retros/2026-03-30-redfin-retro.md b/docs/retros/2026-03-30-redfin-retro.md
new file mode 100644
index 00000000..8c0077b9
--- /dev/null
+++ b/docs/retros/2026-03-30-redfin-retro.md
@@ -0,0 +1,256 @@
+# Printing Press Retro: Redfin
+
+## Session Stats
+- API: Redfin (unofficial Stingray API)
+- Spec source: Hand-crafted OpenAPI from community docs (no official spec, no sniff - bot detection blocked automated browsers)
+- Scorecard: 81/100 (Grade A)
+- Verify pass rate: 52%
+- Fix loops: 2 (both ineffective)
+- Manual code edits: 5 (client JSONP strip, User-Agent, env vars, promoted cmd removal, store rewrite)
+- Features built from scratch: 17 files (search, property, portfolio, trends, deals, mortgage, score, invest, pulse, compare-hoods, track, report, analyze-zips, data, stale, schools, store rewrite)
+- Generated LOC: 5,106 | Hand-written LOC: 5,504
+
+## Findings
+
+### 1. Verify cannot test commands that require positional args (tool limitation)
+
+- **What happened:** 11 of 23 commands failed verify because `workflowTestFlags()` in `internal/pipeline/runtime.go:308` only provides mock args for a hardcoded set of known workflow commands (pr-triage, stale, actions-health, etc.). Hand-written transcendence commands (pulse, deals, mortgage, score, etc.) are unknown to verify, so they're invoked with no positional args. Cobra's `Args: cobra.ExactArgs(1)` rejects the invocation before `RunE` ever runs.
+- **Root cause:** `internal/pipeline/runtime.go` - `workflowTestFlags()` has a closed set of known commands. `classifyCommandKind()` (line 282) defaults everything unknown to "read", meaning these commands get the full dry-run + exec test suite but with no args to satisfy their cobra Args constraint.
+- **Cross-API check:** This will occur on every API where Claude builds custom commands beyond the generated stingray/API layer. The absorb manifest always produces hand-written commands. This is not API-specific.
+- **Frequency:** every API
+- **Fallback if machine doesn't fix it:** Claude would need to either (a) avoid ExactArgs and check inside RunE, or (b) the skill would need to instruct Claude to do this. Claude sometimes forgets, and the verify failure gives no actionable guidance (it just says "manual execution fix required"). Reliability: sometimes.
+- **Worth a machine fix?** Yes. This is the single biggest quality gap. 11/23 commands failing verify is a false negative that makes the verify tool unreliable for hand-built commands.
+- **Inherent or fixable:** Fixable. Two complementary approaches:
+ 1. **Verify: infer required args from --help output.** Parse the `Use:` line (e.g., `pulse <region>`) and supply synthetic values for positional args. `<region>` -> `"mock-region"`, `<property-id>` -> `"12345"`, `<price>` -> `"500000"`, `<zip>` -> `"94102"`.
+ 2. **Verify: classify custom commands as "data-layer" when they call store.Open().** Grep the command source for `store.Open` and reclassify. Data-layer commands skip the dry-run/exec tests that fail.
+- **Durable fix:** Option 1 is more durable. In `runtime.go`, after discovering commands, parse each command's `--help` output to extract the Use line. Use a regex like `<(\w+[-\w]*)>` to find positional arg placeholders. Map common placeholder names to synthetic values: region/location -> "mock-city", property-id/id -> "12345", price -> "500000", zip -> "94102", url -> "/mock/path". Supply these as extra args alongside `--dry-run`.
+- **Test:** Generate a CLI with a custom command requiring `<region>` positional arg. Verify should supply "mock-city" and pass.
+- **Evidence:** Verify output showing 11 commands at score 1/3 despite all working correctly when tested manually with real args.
+
+### 2. Generated store has only generic tables, no entity-specific schema (template gap)
+
+- **What happened:** The generator emitted a store with `resources` (generic) and `stingray` (single JSON blob table). The entire store had to be rewritten to add 7 domain tables (properties, valuations, price_history, regions, trends, portfolio, scoring_profiles) with typed columns, proper indexes, and FTS. This was 400+ lines of hand-written Go.
+- **Root cause:** `internal/generator/templates/store.go.tmpl` - The store template emits one generic `resources` table and one table per API service. It doesn't analyze the spec to derive entity-specific tables with typed columns.
+- **Cross-API check:** Every CLI session includes a Phase 3 where the absorb manifest demands entity-specific tables. The linear-pp-cli needed issues/projects/cycles tables. The discord-pp-cli needed guilds/channels/messages tables. This is universal.
+- **Frequency:** every API
+- **Fallback if machine doesn't fix it:** Claude rewrites the store from scratch every time. This is the single largest hand-written block (~400 LOC). Claude usually gets it right but it's error-prone and inconsistent across CLIs. Reliability: usually, but quality varies.
+- **Worth a machine fix?** Yes. This is the highest-leverage improvement. If the generator could derive entity tables from spec schemas, it would eliminate the biggest chunk of manual work.
+- **Inherent or fixable:** Fixable. The spec already has response schemas that name the primary entities. The generator could:
+ 1. Identify the top-N entities from response schemas (objects with id/name/status-like fields)
+ 2. Generate a table per entity with typed columns for high-gravity fields
+ 3. Generate `Upsert<Entity>` and `Get<Entity>` methods
+ 4. Generate FTS virtual tables for entities with text fields
+- **Durable fix:** In `internal/generator/entity_mapper.go` (or new file), build an entity extraction pipeline that reads spec schemas, identifies primary entities, and emits table DDL + Go struct types + CRUD methods. Feed this into the store template as `.Entities`. The template iterates and emits one table + methods per entity.
+ - Condition: Always run when spec has response schemas with object types
+ - Guard: Skip for specs with no response schemas (fall back to generic store)
+- **Test:** Generate a CLI from the GitHub spec. Verify the store has tables for `issues`, `pull_requests`, `repositories` with typed columns, not just a generic `resources` table.
+- **Evidence:** Store rewrite from 408 LOC generic store to 900+ LOC with 7 domain tables.
+
+### 3. Promoted command collides with API group command (bug)
+
+- **What happened:** The generator emitted both `newStingrayCmd()` (the API group command with subcommands) and `newStingrayPromotedCmd()` (a top-level alias for `get-above-the-fold`). Both registered as `stingray` in the root command, causing a duplicate entry in `--help`. Had to manually remove the promoted command.
+- **Root cause:** `internal/generator/generator.go:456` - `buildPromotedCommands()` doesn't check whether the promoted name collides with the API service group command name.
+- **Cross-API check:** Occurs when the API has a single service (not multi-service). The promoted command name is derived from the service name, which is the same as the group command name.
+- **Frequency:** most APIs (any single-service API)
+- **Fallback if machine doesn't fix it:** Claude notices the duplicate in `--help` and removes it. Reliability: usually, but it's easy to miss during a fast session.
+- **Worth a machine fix?** Yes. Simple guard.
+- **Inherent or fixable:** Fixable. In `buildPromotedCommands()`, skip any promoted command whose name matches an existing service group command.
+- **Durable fix:** In `internal/generator/generator.go`, add a check in the promoted commands loop: if `pc.PromotedName == serviceName`, skip it.
+- **Test:** Generate from a single-service spec. Verify no duplicate commands in `--help` output.
+- **Evidence:** `--help` showed two `stingray` entries.
+
+### 4. Client doesn't handle non-JSON response prefixes (template gap)
+
+- **What happened:** Redfin's Stingray API prefixes all JSON responses with `{}&&` (JSONP/XSSI protection). The generated client returned raw bytes without stripping this prefix, causing JSON parse failures everywhere. Had to manually add `bytes.TrimPrefix` logic.
+- **Root cause:** `internal/generator/templates/client.go.tmpl` - The client template assumes API responses are clean JSON. No handling for common response wrappers like JSONP prefixes, BOM markers, or response envelopes.
+- **Cross-API check:** JSONP/XSSI prefixes (`)]}'`, `{}&&`, `)]}'\n`) are used by Google APIs, Facebook APIs, and other services that serve browser-facing JSON. This is a known API subclass pattern.
+- **Frequency:** API subclass: JSONP-protected APIs (estimated 10-15% of APIs)
+- **Fallback if machine doesn't fix it:** Claude must manually edit client.go every time. Reliability: sometimes (Claude must know the API uses JSONP protection, which requires research).
+- **Worth a machine fix?** Yes. A response sanitization step in the client template is low-cost and high-value.
+- **Inherent or fixable:** Fixable. Add a `sanitizeResponse()` function to the client template that strips known JSONP/XSSI prefixes before JSON parsing.
+- **Durable fix:** In `client.go.tmpl`, add after reading the response body:
+ ```go
+ respBody = sanitizeJSONResponse(respBody)
+ ```
+ Where `sanitizeJSONResponse` strips `)]}'`, `{}&&`, and UTF-8 BOM. This is safe for clean JSON responses (no-op if no prefix found).
+ - Condition: Always active (no-op for clean responses)
+ - Guard: None needed (safe for all APIs)
+- **Test:** Generate a CLI, mock a response with `{}&&{"ok":true}`, verify it parses correctly.
+- **Evidence:** Client fix at `internal/client/client.go` adding bytes.TrimPrefix logic.
+
+### 5. Env var names use hyphens instead of underscores (bug)
+
+- **What happened:** Config env vars were `REDFIN-STINGRAY-UNOFFICIAL_CONFIG` and `REDFIN-STINGRAY-UNOFFICIAL_BASE_URL`. Hyphens are not valid in most shell env var names. Had to manually fix to `REDFIN_PP_CLI_CONFIG` and `REDFIN_PP_CLI_BASE_URL`.
+- **Root cause:** `internal/generator/templates/config.go.tmpl` - The template derives env var names from the CLI name without replacing hyphens with underscores.
+- **Cross-API check:** Any API whose derived name contains hyphens (most of them, since `naming.CLI()` uses kebab-case).
+- **Frequency:** every API
+- **Fallback if machine doesn't fix it:** Claude fixes it manually. Reliability: usually.
+- **Worth a machine fix?** Yes. Trivial fix.
+- **Inherent or fixable:** Fixable. In the config template, apply `strings.ReplaceAll(name, "-", "_")` and `strings.ToUpper()` when generating env var names.
+- **Durable fix:** In `config.go.tmpl`, change the env var name derivation to use `{{envName .Name}}` where `envName` is a template function that converts to SCREAMING_SNAKE_CASE.
+- **Test:** Generate a CLI named `foo-bar-pp-cli`. Verify env vars are `FOO_BAR_PP_CLI_CONFIG`, not `FOO-BAR-PP-CLI_CONFIG`.
+- **Evidence:** Config fix at `internal/config/config.go`.
+
+### 6. No-spec APIs need hand-crafted OpenAPI as intermediate format (recurring friction)
+
+- **What happened:** Redfin has no official API or spec. Had to research 13 tools, reverse-engineer 40+ endpoints from community docs, and hand-write a 531-line OpenAPI YAML to feed the generator. This was a significant time investment.
+- **Root cause:** The printing press pipeline assumes a spec exists (catalog, OpenAPI, or sniffed). When no spec exists, Claude must write one from scratch. The generator can't consume a "docs list" directly.
+- **Cross-API check:** Many popular services have no official API (ESPN, Craigslist, etc.) or have unofficial APIs that are only documented in community reverse-engineering projects. This is a common input class.
+- **Frequency:** API subclass: no-spec/unofficial APIs (estimated 20-30%)
+- **Fallback if machine doesn't fix it:** Claude researches and writes the spec manually. Works, but time-intensive and error-prone (missed endpoints, wrong param types).
+- **Worth a machine fix?** Yes, but medium complexity. The `--docs` flag exists but wasn't used here because the docs are scattered across GitHub repos and blog posts, not a single URL.
+- **Inherent or fixable:** Partially fixable. The current `--docs` path could be enhanced to accept multiple URLs and synthesize a spec from them. A `--research` mode could take Claude's research brief as input and auto-generate a spec stub.
+- **Durable fix:** New `printing-press generate --from-research <brief.md>` mode that parses a structured research brief (the one Claude already writes in Phase 1) and generates an OpenAPI spec from the endpoint list. This would replace the manual spec-writing step.
+ - Condition: No spec found and no single docs URL available
+ - Guard: Skip when spec or docs URL is available
+ - Frequency estimate: ~20-30% of APIs
+- **Test:** Feed the redfin brief to `--from-research` and verify it produces a valid OpenAPI spec with the documented endpoints.
+- **Evidence:** 531-line hand-written OpenAPI spec at `research/redfin-stingray-spec.yaml`.
+
+### 7. Skill doesn't instruct Claude to present ship decision after shipcheck (skill instruction gap)
+
+- **What happened:** After shipcheck came back `ship-with-gaps` at 81/100, Claude wrote the shipcheck artifact, archived manuscripts, and printed a summary. Then stopped. Never asked the user whether they wanted to publish to GitHub, fix the gaps, or take any other action. The user had to ask "why didn't you ask me to publish?"
+- **Root cause:** `skills/printing-press/SKILL.md` - The skill has detailed phases for research, generation, building, and verification, but no post-shipcheck decision phase. The skill ends at "write the shipcheck artifact" without instructing Claude to present the ship recommendation to the user and ask for next steps.
+- **Cross-API check:** This will occur on every single printing press run. There's no instruction telling Claude what to do after shipcheck.
+- **Frequency:** every API
+- **Fallback if machine doesn't fix it:** Claude just stops and the user has to prompt for next steps. Reliability: never (Claude consistently stops after writing the shipcheck because the skill says nothing about what happens next).
+- **Worth a machine fix?** Yes. This is a critical UX gap. The whole point of the skill is to ship a CLI, and it drops the ball at the last step.
+- **Inherent or fixable:** Fixable. Add a Phase 6 to the skill.
+- **Durable fix:** Add to `SKILL.md` after Phase 5:
+
+ ```markdown
+ ## Phase 6: Ship Decision
+
+ Present the shipcheck results to the user via `AskUserQuestion`:
+
+ > "Shipcheck: [verdict] ([score]/100). [summary of gaps if any]. What do you want to do?"
+ >
+ > 1. **Publish to GitHub** — init repo, commit, push, create release
+ > 2. **Fix gaps first** — address the verify/dogfood failures before publishing
+ > 3. **Done for now** — CLI is at ~/printing-press/library/<api>-pp-cli, ship later
+
+ If the user picks "Publish to GitHub":
+ 1. `cd` to the CLI directory
+ 2. `git init && git add -A && git commit -m "Initial generation"`
+ 3. Create GitHub repo via `gh repo create mvanhorn/<api>-pp-cli --public --source=.`
+ 4. `git push -u origin main`
+ 5. Report the repo URL
+ ```
+
+- **Test:** Run a printing press session to completion. Verify Claude asks the user what to do after shipcheck instead of just stopping.
+- **Evidence:** User feedback: "Why didn't you ask me to publish?"
+
+### 8. Dead helper functions emitted by generator template (recurring friction)
+
+- **What happened:** Dogfood flagged 15 dead functions in `helpers.go`: apiErr, bold, colorEnabled, compactFields, compactListFields, compactObjectFields, filterFields, isTerminal, levenshteinDistance, newTabWriter, notFoundErr, paginatedGet, printCSV, printOutput, rateLimitErr. These are emitted by the generator but not called by the generated or hand-written commands.
+- **Root cause:** `internal/generator/templates/helpers.go.tmpl` - The template emits a full set of helpers regardless of which ones the generated commands actually use.
+- **Cross-API check:** Occurs on every generation. The helpers template is one-size-fits-all.
+- **Frequency:** every API
+- **Fallback if machine doesn't fix it:** Claude ignores them (they're harmless) or manually deletes them. Reliability: always (they're cosmetic, not functional). But they inflate LOC counts and dogfood reports.
+- **Worth a machine fix?** Yes, but low priority. These are cosmetic.
+- **Inherent or fixable:** Partially inherent (templates must be generic) but fixable via post-generation cleanup.
+- **Durable fix:** Add a `printing-press polish --dead-code` post-processing step that runs `go vet` analysis and removes unused functions from helpers.go. Alternatively, make the generator conditionally emit helpers based on which commands were generated.
+- **Test:** Generate a CLI, run polish, verify no dead functions in dogfood output.
+- **Evidence:** Dogfood report showing 15 dead functions.
+
+### 9. Bot detection blocks sniff and live smoke for protected sites (recurring friction)
+
+- **What happened:** Redfin blocks both headless and headed automated browsers with bot detection. The sniff phase (Phase 1.7) failed completely, and live smoke (Phase 5) was skipped. This left us with no live API validation.
+- **Root cause:** Not a machine bug. Redfin has aggressive bot detection. But the skill doesn't have a fallback path for when sniff fails due to bot detection.
+- **Cross-API check:** Many high-value sites have bot detection: Zillow, LinkedIn, Amazon, etc. This is a common obstacle for no-spec APIs.
+- **Frequency:** API subclass: bot-protected sites (estimated 15-25%)
+- **Fallback if machine doesn't fix it:** Claude skips sniff and proceeds with docs. Reliability: always (Claude handled this correctly in this session).
+- **Worth a machine fix?** Partial. The skill could be more explicit about the fallback path when bot detection is encountered.
+- **Inherent or fixable:** Mostly inherent (can't bypass bot detection), but the skill could add stealth browser options (playwright with stealth plugin, residential proxies) as optional tools.
+- **Durable fix:** In the sniff gate instructions, add a "bot detection fallback" section that explicitly says: "If both browser-use and agent-browser are blocked by bot detection, immediately fall back to docs-based generation. Do not retry. Report: 'Site has bot detection, proceeding with documented endpoints only.'" This makes the failure path fast instead of exploratory.
+- **Test:** N/A (inherent limitation, just improving the instruction clarity).
+- **Evidence:** browser-use and agent-browser both redirected to ratelimited.redfin.com.
+
+## Prioritized Improvements
+
+### Do Now
+| # | Fix | Component | Frequency | Fallback Reliability | Complexity | Guards |
+|---|-----|-----------|-----------|---------------------|------------|--------|
+| 7 | Add Phase 6 ship decision to skill | SKILL.md | every API | never | small | none |
+| 5 | Fix env var naming (hyphens -> underscores) | config.go.tmpl | every API | usually | small | none |
+| 3 | Prevent promoted command name collision | generator.go | most APIs | usually | small | none |
+
+### Do Next (needs design/planning)
+| # | Fix | Component | Frequency | Fallback Reliability | Complexity | Guards |
+|---|-----|-----------|-----------|---------------------|------------|--------|
+| 1 | Verify: infer positional args from --help | runtime.go | every API | sometimes | medium | none |
+| 4 | Client: sanitize JSONP/XSSI prefixes | client.go.tmpl | subclass: JSONP APIs | sometimes | small | no-op for clean JSON |
+| 2 | Entity-specific store tables from spec | store.go.tmpl, entity_mapper.go | every API | usually (but inconsistent) | large | skip when no response schemas |
+| 8 | Dead code cleanup post-processing | new polish command | every API | always (cosmetic) | medium | none |
+
+### Skip
+| # | Fix | Why unlikely to recur |
+|---|-----|----------------------|
+| 9 | Bot detection bypass | Inherent limitation. Improved skill instruction is sufficient. Can't bypass bot detection without ethical/legal concerns. |
+| 6 | --from-research generation mode | High value but very large scope. The current pattern (Claude writes spec from research) works. This is a v2 feature, not a retro fix. |
+
+## Work Units
+
+### WU-1: Post-shipcheck ship decision (finding #7)
+- **Goal:** Claude presents ship options after shipcheck instead of silently stopping
+- **Target files:** `skills/printing-press/SKILL.md`
+- **Acceptance criteria:**
+ - positive: Run printing press to completion. After shipcheck, Claude asks "What do you want to do?" with publish/fix/done options
+ - negative: N/A
+- **Scope boundary:** Only adds the Phase 6 section. Does not change any other phase.
+- **Complexity:** small
+
+### WU-2: Env var and promoted command fixes (findings #3, #5)
+- **Goal:** Generated CLIs have valid env var names and no duplicate promoted commands
+- **Target files:** `internal/generator/templates/config.go.tmpl`, `internal/generator/generator.go`
+- **Acceptance criteria:**
+ - positive: Generate from a hyphenated API name -> env vars use SCREAMING_SNAKE_CASE
+ - positive: Generate from a single-service spec -> no duplicate command names in --help
+ - negative: Generate from a multi-service spec -> promoted commands still work
+- **Scope boundary:** Does not change any other template behavior.
+- **Complexity:** small
+
+### WU-3: Verify positional arg inference (finding #1)
+- **Goal:** Verify supplies synthetic positional args to commands that require them
+- **Target files:** `internal/pipeline/runtime.go`
+- **Acceptance criteria:**
+ - positive: Generate a CLI with `pulse <region>`. Verify passes dry-run and exec.
+ - positive: Generate a CLI with `mortgage [price]`. Verify passes with synthetic price.
+ - negative: Commands without positional args still tested correctly
+- **Scope boundary:** Only changes workflowTestFlags and/or command discovery. Does not change how verify scores results.
+- **Complexity:** medium
+
+### WU-4: JSONP/XSSI response sanitization (finding #4)
+- **Goal:** Generated clients automatically strip common JSONP/XSSI response prefixes
+- **Target files:** `internal/generator/templates/client.go.tmpl`
+- **Acceptance criteria:**
+ - positive: Client receiving `{}&&{"ok":true}` returns `{"ok":true}`
+ - positive: Client receiving `)]}'{"ok":true}` returns `{"ok":true}`
+ - negative: Client receiving clean `{"ok":true}` returns it unchanged
+- **Scope boundary:** Only adds sanitization. Does not change request behavior.
+- **Complexity:** small
+
+### WU-5: Entity-specific store generation (finding #2)
+- **Goal:** Generator emits domain-specific tables with typed columns from spec response schemas
+- **Target files:** `internal/generator/entity_mapper.go` (new or existing), `internal/generator/templates/store.go.tmpl`, `internal/generator/generator.go`
+- **Acceptance criteria:**
+ - positive: Generate from GitHub spec -> store has `issues` table with `title TEXT`, `state TEXT`, etc.
+ - positive: Generated store includes FTS virtual tables for text-heavy entities
+ - negative: Spec with no response schemas -> falls back to generic store (no regression)
+- **Scope boundary:** Does not change how sync or search commands work. Only changes the store schema and CRUD methods.
+- **Complexity:** large
+
+## Anti-patterns
+
+- **Treating shipcheck output as terminal state.** The shipcheck is a decision point, not an endpoint. The skill must instruct Claude to act on the recommendation.
+- **Delegating store rewrites to agents without typed method contracts.** Two agents wrote code concurrently -- one for the store, one for commands. The commands agent had to use generic store methods because the store agent might not have added typed methods yet. This created unnecessary indirection.
+
+## What the Machine Got Right
+
+- **Generator produced 28 working API commands from a hand-crafted spec.** All stingray commands compiled and worked correctly with the spec.
+- **Adaptive rate limiter in the client.** Proactive rate limiting with ceiling discovery is a strong default for unofficial APIs.
+- **Response caching.** The 5-minute cache in the client is appropriate for real estate data that doesn't change frequently.
+- **Agent-native flags.** The `--agent` meta-flag that sets `--json --compact --no-input --no-color --yes` is a good pattern.
+- **Exit code classification.** Typed exit codes (0/2/3/4/5/7) in helpers.go are well-designed.
+- **MCP server generation.** The generator auto-emitted an MCP server alongside the CLI.
+- **Spec filter flexibility.** The generator accepted the hand-crafted YAML spec without complaint via `--lenient --validate`.
diff --git a/skills/printing-press-polish/SKILL.md b/skills/printing-press-polish/SKILL.md
new file mode 100644
index 00000000..37297ebe
--- /dev/null
+++ b/skills/printing-press-polish/SKILL.md
@@ -0,0 +1,322 @@
+---
+name: printing-press-polish
+description: >
+ Polish a generated CLI to pass verification and become publish-ready. Runs
+ diagnostics (dogfood, verify, scorecard, go vet), automatically fixes all
+ issues (verify failures, dead code, descriptions, README), reports the
+ before/after delta, and offers to publish. Use after any /printing-press run,
+ or on any CLI in ~/printing-press/library/. Trigger phrases: "polish",
+ "improve the CLI", "fix verify", "make it publish-ready", "clean up the CLI",
+ "get this ready to ship".
+allowed-tools:
+ - Bash
+ - Read
+ - Glob
+ - Grep
+ - Write
+ - Edit
+ - Agent
+ - AskUserQuestion
+---
+
+# /printing-press-polish
+
+Fix a generated CLI so it passes verification and is ready to publish. This skill
+does the work that `/printing-press-retro` *identifies* for the machine -- but
+applied to the CLI itself.
+
+The retro improves the generator. Polish improves the generated CLI.
+
+```bash
+/printing-press-polish redfin
+/printing-press-polish redfin-pp-cli
+/printing-press-polish ~/printing-press/library/redfin-pp-cli
+```
+
+## When to run
+
+After any `/printing-press` generation, especially when:
+- The shipcheck verdict is `ship-with-gaps`
+- The verify pass rate is below 80%
+- The scorecard is below 85
+- You want the CLI publish-ready in one pass
+
+Can also be run standalone on any CLI in `~/printing-press/library/`.
+
+## Setup
+
+```bash
+PRESS_HOME="$HOME/printing-press"
+PRESS_LIBRARY="$PRESS_HOME/library"
+```
+
+### Resolve CLI
+
+The argument can be:
+- A short name: `redfin` (searches for `redfin-pp-cli` in `$PRESS_LIBRARY`)
+- A full name: `redfin-pp-cli` (looks up `$PRESS_LIBRARY/redfin-pp-cli`)
+- A path: `~/printing-press/library/redfin-pp-cli` (used directly)
+
+Resolution order:
+1. If the argument is an absolute or `~`-prefixed path and exists, use it
+2. Try `$PRESS_LIBRARY/<arg>` (exact match)
+3. Try `$PRESS_LIBRARY/<arg>-pp-cli` (append suffix)
+4. Fuzzy search: `ls $PRESS_LIBRARY/ | grep -i <arg>` for close matches
+
+If no match or multiple matches, present via `AskUserQuestion`. Show at most 4
+matches sorted by modification time (most recent first) with human-friendly
+relative timestamps (e.g., "generated 2 hours ago").
+
+```bash
+CLI_DIR="<resolved path>"
+CLI_NAME="$(basename "$CLI_DIR")"
+
+# Verify it's a valid Go CLI
+if [ ! -f "$CLI_DIR/go.mod" ]; then
+ echo "Not a valid CLI directory: $CLI_DIR"
+ exit 1
+fi
+
+echo "Polishing: $CLI_NAME"
+echo "Location: $CLI_DIR"
+```
+
+## Phase 1: Diagnostics
+
+Run all diagnostic tools to establish a baseline. Capture output for comparison.
+
+```bash
+cd "$CLI_DIR"
+
+# Find the spec if one exists in manuscripts
+API_SLUG="${CLI_NAME%-pp-cli}"
+SPEC_PATH=""
+for f in "$PRESS_HOME/manuscripts/$API_SLUG"/*/research/*.yaml "$PRESS_HOME/manuscripts/$API_SLUG"/*/research/*.json; do
+ if [ -f "$f" ]; then
+ SPEC_PATH="$f"
+ break
+ fi
+done
+
+SPEC_FLAG=""
+if [ -n "$SPEC_PATH" ]; then
+ SPEC_FLAG="--spec $SPEC_PATH"
+fi
+```
+
+### 1.1 Run diagnostics
+
+```bash
+# Build the binary first
+go build -o "$CLI_NAME" ./cmd/"$CLI_NAME" 2>&1
+
+# Run all four diagnostic tools
+printing-press dogfood --dir "$CLI_DIR" $SPEC_FLAG 2>&1 | tee /tmp/polish-dogfood.txt
+printing-press verify --dir "$CLI_DIR" $SPEC_FLAG --json 2>&1 | tee /tmp/polish-verify.json
+printing-press scorecard --dir "$CLI_DIR" $SPEC_FLAG 2>&1 | tee /tmp/polish-scorecard.txt
+go vet ./... 2>&1 | tee /tmp/polish-govet.txt
+```
+
+### 1.2 Parse findings
+
+Parse the diagnostic output into categorized findings:
+
+| Category | Source | Example |
+|----------|--------|---------|
+| Verify failures | verify --json | Command "pulse" dry-run FAIL, exec FAIL |
+| Dead code | dogfood | 15 dead functions in helpers.go |
+| Dead flags | dogfood | 7 flags declared but never read |
+| Stale files | dogfood + grep | promoted_stingray.go not registered |
+| README gaps | scorecard | README score 5/10 |
+| Description issues | dogfood | Root Short is "Reverse-engineered..." |
+| Example gaps | dogfood | analyze-zips missing example |
+| Data pipeline | verify | Data pipeline FAIL |
+| Go vet issues | go vet | Unused variables, unreachable code |
+
+### 1.3 Report baseline
+
+Present the baseline clearly:
+
+```
+Baseline for <CLI_NAME>:
+ Scorecard: XX/100 (Grade X)
+ Verify: XX% (N/M passed)
+ Dogfood: PASS/FAIL
+ Go vet: N issues
+
+Findings:
+ [N] verify failures
+ [N] dead code items
+ [N] description/README issues
+ [N] other issues
+```
+
+## Phase 2: Fix
+
+Fix everything automatically in priority order. Do not ask for approval. The user
+reviews the diff after all fixes are applied.
+
+### Priority 1: Verify failures
+
+The most common verify failure pattern is cobra `Args:` constraints that prevent
+the verify tool from testing commands with no positional args. The verify tool
+runs `<binary> <cmd> --dry-run` and `<binary> <cmd> --json` with no positional
+args.
+
+**Fix strategy:**
+
+For each command that fails verify dry-run or exec:
+
+1. Read the command file (e.g., `internal/cli/pulse.go`)
+2. Find `Args: cobra.ExactArgs(N)` or similar constraint in the cobra.Command struct
+3. Remove the `Args:` field entirely
+4. Add at the top of `RunE`:
+ ```go
+ if len(args) == 0 {
+ return cmd.Help()
+ }
+ ```
+5. For commands needing 2+ args (like `compare-hoods`), use `if len(args) < 2`
+6. For commands that use only flags (no positional args), ensure they show help
+ and exit 0 when required flags are missing
+
+Also check for dry-run nil-data crashes:
+- If a command makes API calls and the dry-run response is nil, add a guard:
+ ```go
+ if flags.dryRun {
+ return nil
+ }
+ ```
+
+### Priority 2: Dead code
+
+For each dead function flagged by dogfood:
+
+1. Grep all `.go` files to verify the function is truly unused (not just its
+ definition matching itself)
+2. If truly unused: remove the function
+3. If used internally by another helper: leave it (dogfood false positive)
+4. After removal, check for unused imports and remove them
+5. Delete any stale files (e.g., promoted commands that are no longer registered
+ in root.go)
+
+### Priority 3: CLI description and metadata
+
+1. Read the root command's `Short` field in `internal/cli/root.go`
+2. If it contains generator boilerplate (e.g., "Reverse-engineered...", raw API
+ title), rewrite it to be user-friendly:
+ - Pattern: `"<Product> CLI with <key-capability-1>, <key-capability-2>, and <key-capability-3>"`
+ - Example: `"Redfin real estate CLI with offline search, market analysis, and portfolio tracking"`
+3. Check all commands for missing `Example` fields. Add realistic examples with
+ domain-specific values.
+
+### Priority 4: README
+
+1. Read the current README.md
+2. If it uses the generator's template with placeholder names or generic examples,
+ rewrite it:
+ - Title: CLI name
+ - One-line description matching the root Short
+ - Install section with `go install` command
+ - Quick start with 3-5 real usage examples
+ - Command list organized by category (from `--help` output)
+ - Output format section showing `--json`, `--select`, `--dry-run`
+ - Configuration section
+3. Use actual command names and realistic example values from the API domain
+
+### Priority 5: Remaining dogfood issues
+
+Address any remaining issues flagged by dogfood:
+- Path validity mismatches
+- Auth protocol mismatches
+- Example drift (examples that reference wrong commands)
+- Data pipeline integrity issues
+
+### After all fixes
+
+```bash
+# Rebuild
+go build -o "$CLI_NAME" ./cmd/"$CLI_NAME"
+
+# Format
+gofmt -w .
+```
+
+## Phase 3: Re-diagnose
+
+Re-run all diagnostic tools on the fixed CLI.
+
+```bash
+printing-press dogfood --dir "$CLI_DIR" $SPEC_FLAG 2>&1 | tee /tmp/polish-dogfood-after.txt
+printing-press verify --dir "$CLI_DIR" $SPEC_FLAG --json 2>&1 | tee /tmp/polish-verify-after.json
+printing-press scorecard --dir "$CLI_DIR" $SPEC_FLAG 2>&1 | tee /tmp/polish-scorecard-after.txt
+go vet ./... 2>&1 | tee /tmp/polish-govet-after.txt
+```
+
+## Phase 4: Report delta
+
+Present the before/after comparison:
+
+```
+Polish Results for <CLI_NAME>:
+
+ Before After Delta
+ Scorecard: XX/100 XX/100 +N
+ Verify: XX% XX% +N%
+ Dogfood: FAIL PASS
+ Go vet issues: N N -N
+
+Fixes applied:
+ - Fixed N commands for no-arg handling (verify)
+ - Removed N dead functions
+ - Rewrote CLI description
+ - Updated README
+ - [other fixes]
+
+Remaining issues:
+ - [any issues that couldn't be fixed automatically]
+```
+
+## Phase 5: Publish offer
+
+If the final scorecard is >= 65 and verify is PASS (or >= 80%):
+
+Present via `AskUserQuestion`:
+
+> "<CLI_NAME> polished: scorecard XX/100, verify XX%. Ready to publish?"
+>
+> 1. **Publish now** — validate, package, and open a PR to printing-press-library
+> 2. **Polish again** — run another fix pass on remaining issues
+> 3. **Done for now** — CLI is at ~/printing-press/library/<cli-name>
+
+If the verdict was `ship-with-gaps` or there are remaining issues, prepend:
+"Note: some issues remain (see above)."
+
+### If "Publish now"
+
+Check for existing PR:
+```bash
+gh pr list --repo mvanhorn/printing-press-library --head "feat/$CLI_NAME" --state open --author @me --json number,url --jq '.[0]' 2>/dev/null
+```
+
+Then invoke `/printing-press publish <cli-name>`.
+
+### If "Polish again"
+
+Re-run Phase 2-4. Maximum 2 additional polish passes.
+
+### If "Done for now"
+
+End normally.
+
+## Rules
+
+- Fix everything. Do not ask for approval before fixing.
+- Report results honestly. Show what improved and what didn't.
+- Do not add new features. Polish fixes quality issues, not feature gaps.
+- Do not re-run research or generation. Polish works with the CLI as-is.
+- Do not modify the printing-press generator. That's `/printing-press-retro`.
+- Prefer mechanical fixes over creative decisions. When a creative decision is
+ needed (like the CLI description), use the research brief from manuscripts if
+ available to inform the choice.
+- Maximum 3 total polish passes (initial + 2 retries).
diff --git a/skills/printing-press/SKILL.md b/skills/printing-press/SKILL.md
index 60a19269..860a3c38 100644
--- a/skills/printing-press/SKILL.md
+++ b/skills/printing-press/SKILL.md
@@ -27,9 +27,6 @@ Generate the best useful CLI for an API without burning an hour on phase theater
/printing-press --har ./capture.har --name MyAPI
/printing-press https://postman.com/explore
/printing-press https://postman.com
-/printing-press emboss notion
-/printing-press emboss notion-pp-cli
-/printing-press emboss ~/printing-press/library/notion-pp-cli
```
## What Changed In v2
@@ -71,30 +68,15 @@ Keep on Claude:
If Codex fails 3 times in a row, stop delegating and finish locally.
-### Emboss Mode
+### Polish Mode (Standalone Skill)
-If the arguments start with `emboss`, this is a second-pass improvement cycle for an existing generated CLI.
+For second-pass improvements to an existing CLI, use the standalone polish skill:
```bash
-/printing-press emboss notion-pp-cli
-/printing-press emboss notion
-/printing-press emboss ~/printing-press/library/notion-pp-cli
+/printing-press-polish redfin
```
-#### Emboss Name Resolution
-
-The CLI accepts a name or path directly (`printing-press emboss notion`). If the CLI errors with "no CLI named X found," search `$PRESS_LIBRARY/` for close matches and use `AskUserQuestion` to let the user pick. Show at most 4 matches, sorted by directory modification time (most recent first), with human-friendly relative timestamps (e.g. "generated 2 hours ago").
-
-#### Emboss Cycle
-
-Emboss is:
-1. audit baseline
-2. quick re-research
-3. top-5 gap analysis
-4. implement improvements
-5. re-audit and report delta
-
-Do not run emboss automatically.
+See the `printing-press-polish` skill for details. It runs diagnostics, fixes verify failures, removes dead code, cleans up descriptions and README, and offers to publish.
## Rules
@@ -298,7 +280,7 @@ Before new research:
3. **"Review prior research first"** — Show the full research brief and absorb manifest before deciding.
If the user picks option 1, proceed to Phase 1 (research) and then Phase 2 (generate) as normal.
- If the user picks option 2, switch to emboss mode (see Emboss Cycle above).
+ If the user picks option 2, invoke `/printing-press-polish <cli-name>` to improve the existing CLI.
If the user picks option 3, display the prior research, then re-present options 1 and 2.
If no CLI exists in the library, skip this step and proceed normally.
@@ -1554,20 +1536,30 @@ Present via `AskUserQuestion`:
**If no existing PR:**
-> "<cli-name> passed shipcheck. Want to publish it to the printing-press-library?"
+> "<cli-name> passed shipcheck ([score]/100, verify [pass-rate]%). What do you want to do?"
>
-> 1. **Yes — publish now** (validate, package, and open a PR)
-> 2. **No — I'm done**
+> 1. **Publish now** (validate, package, and open a PR)
+> 2. **Polish first** (run `/printing-press-polish` to fix verify failures, dead code, and README before publishing)
+> 3. **Run retro** (analyze the session to find improvements for the generator)
+> 4. **Done for now**
+
+If the verdict was `ship-with-gaps`, prepend: "Note: shipcheck found minor gaps (see the shipcheck report above)." and recommend the polish option.
+
+### If "Publish now"
+
+Invoke `/printing-press publish <cli-name>`. The publish skill handles everything from there.
+
+### If "Polish first"
-If the verdict was `ship-with-gaps`, prepend: "Note: shipcheck found minor gaps (see the shipcheck report above)."
+Invoke `/printing-press-polish <cli-name>`. The polish skill runs diagnostics, fixes issues, reports the delta, and offers its own publish at the end.
-### If accepted
+### If "Run retro"
-Invoke `/printing-press publish <cli-name>`. The publish skill handles everything from there — name resolution, category, validation, packaging, git ops, and PR creation or update.
+Invoke `/printing-press-retro`. The retro skill analyzes the session for generator improvements.
-### If declined
+### If "Done for now"
-End normally. The CLI is in `$PRESS_LIBRARY/<api>-pp-cli` and the user can run `/printing-press publish` later.
+End normally. The CLI is in `$PRESS_LIBRARY/<api>-pp-cli` and the user can run `/printing-press publish` or `/printing-press-polish` later.
## Fast Guidance
← 6c3c8db8 fix(cli): four generator improvements from Redfin retro (#89
·
back to Cli Printing Press
·
feat(skills): add API reachability gate before generation (# bed2f97c →