[object Object]

← back to Cli Printing Press

fix(skills): improve retro skill prioritization and skip/do criteria (#81)

64ded0aeaa86bc791685181c879fa3696befd61b · 2026-03-30 19:34:48 -0700 · Trevin Chow

* fix(skills): improve retro skill prioritization and skip/do criteria

The retro was producing docs with human time estimates (hours/days/weeks),
formulaic numerical scoring, and over-aggressive punting to Backlog/Skip.
This undermined the improvement loop by letting findings die instead of
driving machine improvements.

Key changes:
- Replace time estimates with complexity sizing (small/medium/large)
- Remove numerical tradeoff formula — use judgment-based reasoning
- Collapse 3 tiers + Skip into 2 buckets: Do (now/next) and Skip
- Reframe fallback cost around reliability, not time
- Sharpen Skip criteria: only vendor-specific idiosyncrasies unlikely
  to recur across 50 APIs
- Add positive/negative examples for skip boundary
- Stress test across API shapes × input methods (OpenAPI, crowd-sniff,
  HAR, no-spec)
- Mark retro as advisory in ce-plan handoff, not declarative

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(skills): add install-internal-skills script

Adds a script to copy internal development skills (e.g., printing-press-retro)
to ~/.claude/skills/ for users running Claude Code outside the repo.
Documents the script and auto-loading behavior in AGENTS.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 64ded0aeaa86bc791685181c879fa3696befd61b
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Mon Mar 30 19:34:48 2026 -0700

    fix(skills): improve retro skill prioritization and skip/do criteria (#81)
    
    * fix(skills): improve retro skill prioritization and skip/do criteria
    
    The retro was producing docs with human time estimates (hours/days/weeks),
    formulaic numerical scoring, and over-aggressive punting to Backlog/Skip.
    This undermined the improvement loop by letting findings die instead of
    driving machine improvements.
    
    Key changes:
    - Replace time estimates with complexity sizing (small/medium/large)
    - Remove numerical tradeoff formula — use judgment-based reasoning
    - Collapse 3 tiers + Skip into 2 buckets: Do (now/next) and Skip
    - Reframe fallback cost around reliability, not time
    - Sharpen Skip criteria: only vendor-specific idiosyncrasies unlikely
      to recur across 50 APIs
    - Add positive/negative examples for skip boundary
    - Stress test across API shapes × input methods (OpenAPI, crowd-sniff,
      HAR, no-spec)
    - Mark retro as advisory in ce-plan handoff, not declarative
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
    
    * feat(skills): add install-internal-skills script
    
    Adds a script to copy internal development skills (e.g., printing-press-retro)
    to ~/.claude/skills/ for users running Claude Code outside the repo.
    Documents the script and auto-loading behavior in AGENTS.md.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
    
    ---------
    
    Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---
 .claude/scripts/install-internal-skills.sh   |  41 +++++
 .claude/skills/printing-press-retro/SKILL.md | 217 ++++++++++++++++-----------
 AGENTS.md                                    |  12 ++
 3 files changed, 183 insertions(+), 87 deletions(-)

diff --git a/.claude/scripts/install-internal-skills.sh b/.claude/scripts/install-internal-skills.sh
new file mode 100755
index 00000000..334c5c8f
--- /dev/null
+++ b/.claude/scripts/install-internal-skills.sh
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+# Install internal printing-press development skills to ~/.claude/skills/
+# so they're available globally without needing the repo checked out.
+#
+# Usage: .claude/scripts/install-internal-skills.sh
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+REPO_SKILLS="$SCRIPT_DIR/../skills"
+USER_SKILLS="$HOME/.claude/skills"
+
+if [ ! -d "$REPO_SKILLS" ]; then
+  echo "error: no skills found at $REPO_SKILLS"
+  exit 1
+fi
+
+mkdir -p "$USER_SKILLS"
+
+copied=0
+for skill_dir in "$REPO_SKILLS"/*/; do
+  [ -d "$skill_dir" ] || continue
+  skill_name="$(basename "$skill_dir")"
+  target="$USER_SKILLS/$skill_name"
+
+  if [ -d "$target" ]; then
+    echo "updating: $skill_name"
+    rm -rf "$target"
+  else
+    echo "installing: $skill_name"
+  fi
+
+  cp -R "$skill_dir" "$target"
+  copied=$((copied + 1))
+done
+
+if [ "$copied" -eq 0 ]; then
+  echo "no skills found to install"
+else
+  echo "installed $copied skill(s) to $USER_SKILLS"
+fi
diff --git a/.claude/skills/printing-press-retro/SKILL.md b/.claude/skills/printing-press-retro/SKILL.md
index 344f0825..a5e8f237 100644
--- a/.claude/skills/printing-press-retro/SKILL.md
+++ b/.claude/skills/printing-press-retro/SKILL.md
@@ -83,7 +83,7 @@ becomes a row in Phase 3 — don't filter yet, just collect.
 ### 2a. Errors and retries
 
 Any time a command failed and was re-run, a build broke, or the generator produced
-code that didn't compile. What broke, what fixed it, and how long did it take?
+code that didn't compile. What broke and what fixed it?
 
 ### 2b. Manual code edits
 
@@ -178,58 +178,100 @@ unreliable. A finding that felt universal during the Postman Explore session mig
 be specific to proxy-envelope APIs, or to sniffed specs, or to APIs with entity-type
 enum params.
 
-**Step A: Cross-API stress test.** Mentally test each finding against at least three
-different API shapes:
+**Step A: Cross-API stress test.** Test across both API shapes and input methods
+since the printing press handles all combinations:
 
-- A standard REST API with clean OpenAPI spec (e.g., Stripe, GitHub)
-- A minimal/undocumented API discovered via sniff or HAR
-- An API with a different auth model or response format
+API shapes:
+- A standard REST API with clean resources (e.g., Stripe, GitHub)
+- A non-standard or minimal API (proxy-envelope, RPC-style, etc.)
 
-For each, ask: "Would this exact problem occur? Would the proposed fix help, be
-irrelevant, or actively hurt?"
+Input methods:
+- OpenAPI spec (well-documented, machine-readable)
+- Crowd-sniffed (derived from npm/GitHub SDKs, may have gaps)
+- HAR-sniffed (derived from captured traffic)
+- No spec (Claude researches and builds from scratch)
+
+For each relevant combination, ask: "Would this exact problem occur? Would the
+proposed fix help, be irrelevant, or actively hurt?"
 
 **Step B: Estimate frequency.** Based on the stress test, assign a blast radius:
 
-- **Every API** — occurs regardless of API shape. Be skeptical of this label — it
-  must fail the stress test for all three shapes.
+- **Every API** — occurs regardless of API shape or input method.
 - **Most APIs** — affects common patterns. Name the triggering condition.
 - **API subclass** — affects a specific pattern. Name the subclass precisely:
   proxy-envelope, GraphQL, sniffed-only, offset-paginated, etc.
 - **This API only** — isolated quirk.
 
 **Step C: Assess fallback cost.** This is what happens if the machine does NOT have
-the fix. For each finding, the fallback is one of:
-
-| Fallback | Cost | Example |
-|----------|------|---------|
-| **Claude rewrites from scratch** | High — 10+ min, error-prone, may forget | Rewriting the entire sync command for offset pagination |
-| **Claude makes targeted edits** | Medium — 2-5 min, usually succeeds | Fixing serviceForPath routing, changing a default path |
-| **Claude deletes/tweaks one thing** | Low — <1 min, mechanical | Removing 3 dead functions, rewriting a Short description |
-| **CLI ships broken** | Critical — user hits the bug at runtime | Infinite sync loop, wrong API responses, empty search |
-
-**Step D: Make the tradeoff.** The decision to add conditional logic to the machine
-is NOT just "is it general enough?" — it's a cost-benefit:
-
-```
-machine fix justified when:
-  (frequency × fallback cost) > (implementation effort + regression risk)
-```
-
-This means:
-- A finding affecting only 20% of APIs (API subclass) can still justify a machine fix
-  if the fallback is "Claude completely rewrites the sync command" (high cost) or
-  "CLI ships with an infinite loop" (critical).
-- A finding affecting 100% of APIs might NOT justify a machine fix if the fallback is
-  "Claude changes one line" (low cost) and the fix is complex to implement.
-- Narrow-scope fixes should include conditional logic (activate when X, skip otherwise)
-  so they don't regress the simple case. But the mere fact that a condition is required
-  is not a reason to skip the fix — it's a reason to scope it carefully.
-
-**The counterpoint to being conservative:** if the machine doesn't cover a wide enough
-breadth of cases, the printed CLIs suffer. Best case is Claude dynamically fixes the
-problem during generation — which is inefficient, error-prone, and might not happen.
-Worst case is the CLI ships with the defect. Every finding left out of the machine is
-a bet that Claude will catch it every time, and Claude won't.
+the fix. The cost is NOT measured in time — Claude does the implementation. The cost
+is about **reliability and quality degradation**:
+
+| Fallback | Cost | Why it's costly |
+|----------|------|-----------------|
+| **Claude rewrites from scratch** | High — error-prone, may produce inconsistent code | Claude might forget, get it wrong, or produce code that doesn't match the generator's patterns |
+| **Claude makes targeted edits** | Medium — usually succeeds but adds friction | Claude generally catches it, but each manual edit is a chance for the CLI to diverge from machine quality |
+| **Claude deletes/tweaks one thing** | Low — mechanical, reliable | Claude catches it consistently, fix is trivial and deterministic |
+| **CLI ships broken** | Critical — user hits the bug at runtime | Defect reaches the user. No amount of Claude effort during generation prevents this if the machine emits wrong code |
+
+The key question: **how reliably will Claude catch and fix this every time, across
+every future API?** A "simple" edit that Claude forgets 30% of the time is actually
+high cost because those 30% ship with the defect.
+
+**Step D: Make the tradeoff.** The default is to **fix it in the machine**. The
+retro exists to feed an improvement loop — if findings get punted to Skip by
+default, the loop stalls and CLIs don't get better. Every finding left out of the
+machine is a bet that Claude will catch it every time, and Claude won't.
+
+The burden of proof is on *not* fixing, not on fixing.
+
+**Skip** when the behavior is unlikely to occur with other APIs or services.
+
+For each finding, ask: **"If I'm building CLIs for 50 different APIs — big
+enterprise platforms, small indie services, legacy systems, modern startups — is
+this something I'd plausibly encounter again, or is it truly this one vendor's
+idiosyncratic choice?"**
+
+Use your knowledge of the API landscape to make this judgment. You know how APIs
+are built, what conventions exist across the industry, and what patterns different
+vendors converge on independently. Trust that judgment rather than trying to apply
+a mechanical rule.
+
+Examples of findings that were correctly skipped:
+- **Valve's `input_json` wrapping** — Steam's "Service" interfaces require all params
+  encoded as `input_json={"steamid":"xxx"}`. Valve invented this for their internal
+  service RPC layer. Across 50 APIs, you wouldn't see this convention again.
+
+Examples of findings that LOOK isolated but should NOT be skipped:
+- "This API uses API keys in query params instead of headers" — many older APIs
+  do this. You'd see it again.
+- "This API returns 200 for errors with an error field in the body" — common
+  anti-pattern across many APIs.
+- "This API has no pagination and returns everything in one call" — a pagination
+  variant (none/single-page), not a quirk.
+
+**Do** when you'd plausibly encounter the behavior again — even if this is the
+first API to trigger it.
+
+The printing press handles multiple input paths — OpenAPI specs, crowd-sniffed
+specs, HAR-sniffed specs, and no-spec APIs. Findings can surface from any path.
+Examples that cut across input methods:
+- Offset pagination (vs cursor) — fundamental pagination approach, surfaces whether
+  you have an OpenAPI spec or are sniffing traffic
+- Response envelope wrapping — most APIs do this regardless of how the spec was
+  obtained
+- Incomplete parameter discovery — happens with crowd-sniff (SDK gaps), HAR sniff
+  (limited traffic capture), and even OpenAPI specs (undocumented params)
+- Auth pattern detection — needed whether auth info comes from an OpenAPI
+  `securitySchemes`, an SDK constructor, or HAR request headers
+- Resource naming mismatches — the generator's command structure vs user-friendly
+  names is independent of spec source
+
+With the diversity of APIs and input methods the printing press targets, **true
+vendor quirks are rarer than they seem.** When in doubt, lean toward Do with a
+guard rather than Skip.
+
+When the finding applies to a subclass, include conditional logic so it doesn't
+regress the simple case.
 
 When the finding applies to an API subclass, the recommendation must include:
 - **Condition:** When to activate (e.g., "spec has `x-proxy-routes`")
@@ -262,25 +304,17 @@ pagination and verify sync terminates after fetching all pages."
 
 ## Phase 4: Prioritize
 
-Score each finding using the tradeoff from Question 4:
+Group findings into two buckets using judgment, not a formula. No "backlog" —
+backlog is where findings go to die. Either it's worth doing or it's not.
 
-```
-priority = (frequency × fallback cost) / (implementation effort + regression risk)
-```
+- **Do** — worth a machine fix. This is the default. Split into "Do now" (scoped
+  cleanly, can implement immediately) and "Do next" (needs design work or careful
+  guards — plan before implementing).
+- **Skip** — unlikely to encounter again across other APIs. State why.
 
-Where:
-- **Frequency**: every=4, most=3, subclass=2, this-API=1
-- **Fallback cost**: critical=4, high(rewrite)=3, medium(targeted edit)=2, low(one-liner)=1
-- **Implementation effort**: 1(hours) to 4(weeks)
-- **Regression risk**: 0(conditional/guarded) to 3(blanket change touching all APIs)
-
-Present as a ranked table. Group into tiers:
-- **Tier 1: Do now** — high frequency×fallback, low effort, guarded implementation
-- **Tier 2: Plan** — high frequency×fallback but needs design work or careful guards
-- **Tier 3: Backlog** — low fallback cost (Claude handles it fine dynamically) or
-  inherent friction with cheap mitigations
-- **Skip** — this-API-only findings or cases where the dynamic fix is genuinely easier
-  than the machine fix
+Do NOT use numerical scoring formulas. The inputs (frequency=3, fallback=4) are
+ordinal gut-feels, and multiplying them produces fake precision that obscures
+judgment. State the reasoning in words instead.
 
 ## Phase 5: Write the retro
 
@@ -295,19 +329,19 @@ Present as a ranked table. Group into tiers:
 - Fix loops: <N>
 - Manual code edits: <N>
 - Features built from scratch: <N>
-- Time to ship: ~<X>m
 
 ## Findings
 
 ### 1. <Title> (<category>)
 - **What happened:** ...
 - **Root cause:** Component + what's specifically wrong
-- **Cross-API check:** Would this occur for [standard REST]? [sniffed API]? [different auth]?
+- **Cross-API check:** Would this recur across other APIs and input methods?
 - **Frequency:** every API / most / subclass:<name> / this API only
-- **Fallback if machine doesn't fix it:** What Claude has to do dynamically (rewrite,
-  edit, one-liner) or what ships broken
-- **Tradeoff:** Is the machine fix worth it given frequency × fallback cost vs
-  implementation effort + regression risk?
+- **Fallback if machine doesn't fix it:** What Claude has to do dynamically and how
+  reliably Claude catches it (always / usually / sometimes / never). If "sometimes"
+  or "never", the CLI ships with the defect — that's the real cost.
+- **Worth a machine fix?** Default is yes. Only no if you'd be unlikely to encounter
+  this across other APIs. State the reasoning in plain language.
 - **Inherent or fixable:** ...
 - **Durable fix:** Concrete machine change. If subclass-scoped, include:
   - Condition: when to activate
@@ -320,17 +354,17 @@ Present as a ranked table. Group into tiers:
 
 ## Prioritized Improvements
 
-### Tier 1: Do Now
-| # | Fix | Component | Frequency | Fallback Cost | Effort | Guards |
-|---|-----|-----------|-----------|--------------|--------|--------|
+### Do Now
+| # | Fix | Component | Frequency | Fallback Reliability | Complexity | Guards |
+|---|-----|-----------|-----------|---------------------|------------|--------|
 
-### Tier 2: Plan
-| # | Fix | Component | Frequency | Fallback Cost | Effort | Guards |
-|---|-----|-----------|-----------|--------------|--------|--------|
+### Do Next (needs design/planning)
+| # | Fix | Component | Frequency | Fallback Reliability | Complexity | Guards |
+|---|-----|-----------|-----------|---------------------|------------|--------|
 
-### Tier 3: Backlog
-| # | Fix | Component | Frequency | Fallback Cost | Effort | Guards |
-|---|-----|-----------|-----------|--------------|--------|--------|
+### Skip
+| # | Fix | Why unlikely to recur |
+|---|-----|----------------------|
 
 ## Work Units
 
@@ -341,7 +375,7 @@ Present as a ranked table. Group into tiers:
   - positive test: ...
   - negative test: ...
 - **Scope boundary:** ...
-- **Effort:** ...
+- **Complexity:** small / medium / large
 
 ### WU-2: ...
 
@@ -364,7 +398,7 @@ The retro's findings are analytical. To bridge to implementation planning (e.g.,
 via `/compound-engineering:ce-plan`), group related findings into coherent work units that a planner
 could pick up directly.
 
-For each tier 1 or tier 2 group, produce a work unit block:
+For each "Do now" or "Do next" group, produce a work unit block:
 
 ```markdown
 ## Work Units
@@ -378,7 +412,7 @@ For each tier 1 or tier 2 group, produce a work unit block:
   - "Generate from Stripe spec → sync still uses cursor-based pagination (negative test)"
 - **Scope boundary:** What this does NOT include
 - **Dependencies:** Other work units that must complete first (if any)
-- **Estimated effort:** hours/days
+- **Complexity:** small (1-2 files, straightforward) / medium (3-5 files, needs design) / large (new capability, multiple subsystems)
 ```
 
 To resolve target files, actually look at the printing-press repo:
@@ -398,8 +432,8 @@ one fix enables another. For example:
 - Entity-specific store tables + FTS index generation + typed columns
   → "WU: Schema-driven store generation"
 
-A good work unit is something one person could implement in 1-3 days with a clear
-definition of done. If a work unit is bigger than that, split it.
+A good work unit is scoped to a single implementable chunk with a clear definition
+of done. If it touches more than ~5 files across multiple subsystems, split it.
 
 ## Phase 6: Save and present
 
@@ -427,7 +461,7 @@ Save the retro to two places:
 
 ### Present summary
 
-Show the user: top 3 findings, the tier 1 table, and the work units.
+Show the user: the "Do now" table and the work units.
 
 ### Offer to plan
 
@@ -438,7 +472,7 @@ standalone installs). Use `AskUserQuestion` to offer next steps:
 > "Retro saved to `docs/retros/<date>-<api>-retro.md`. Found <N> findings across
 > <M> work units. Want to plan implementation?"
 >
-> 1. **Plan Tier 1 work units** — invoke `/compound-engineering:ce-plan` with the retro's Tier 1 work
+> 1. **Plan "Do now" work units** — invoke `/compound-engineering:ce-plan` with the retro's Do now work
 >    units as input
 > 2. **Plan a specific work unit** — pick one WU to plan
 > 3. **Done for now** — retro is saved, plan later
@@ -454,6 +488,11 @@ opportunities documented in the retro. The retro includes prioritized work units
 with target files, acceptance criteria, and scope boundaries:
 docs/retros/<date>-<api>-retro.md
 [If option 2: Focus on work unit WU-<N>: <title>.]
+
+Note: the retro is advisory, not declarative. During planning you may discover
+valid reasons to adjust scope — combining work units, dropping items that turn out
+to be impractical once you read the code, or adding items the retro missed. That's
+expected. Use the retro as a starting point, not a spec.
 ```
 
 If neither skill name resolves, fall back to:
@@ -472,15 +511,19 @@ If neither skill name resolves, fall back to:
   dismiss friction as inherent without considering alternatives.
 - Be honest about what went well. Protecting good patterns is as important as
   fixing bad ones.
-- **Resist over-generalization.** You just spent an entire session with one API. Your
-  intuition about "every API needs this" is based on a sample size of one. Stress-test
-  every finding against other API shapes before claiming broad blast radius. A fix
-  that helps proxy-envelope APIs but adds unnecessary complexity to a clean REST API
-  is a regression, not an improvement. When in doubt, scope the fix narrowly with
-  conditional logic and let future retros widen it if the pattern recurs.
+- **Bias toward fixing.** The retro feeds an improvement loop. If the loop is too
+  conservative, CLIs don't get better. When in doubt, fix it — scope narrowly with
+  conditional logic if needed. A guarded fix for a rare case is better than no fix
+  at all. Future retros can widen the scope when the pattern recurs.
+- **Look for broader patterns.** Before skipping a finding as API-specific, consider
+  whether it's the first sighting of a behavior you'd encounter again. Offset
+  pagination was first seen on one API — but it's clearly a pattern across many.
 - When a fix only applies to a subclass of APIs, the recommendation must include the
   condition (when to activate) AND the guard (when to skip). A generator change
   without a guard is a blanket change, and blanket changes break simple cases.
+- **No time estimates.** Claude does the implementation. Hours/days/weeks are
+  meaningless. Use complexity sizing (small/medium/large) based on number of files
+  touched and design work needed.
 - Be thorough. The retro document is a reference for future planning — include
   enough detail that someone reading it months later can understand the finding,
   the tradeoff reasoning, and the proposed fix without needing the original
diff --git a/AGENTS.md b/AGENTS.md
index ad12fa27..d80932e4 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -98,3 +98,15 @@ Generated artifacts live under the user's home directory, not in this repo.
 The API slug is derived by the generator from the spec title (`cleanSpecName`), not manually chosen. The CLI name is `<api-slug>-pp-cli`. Never hardcode an API slug when the generator can derive it — names with periods (cal.com, dub.co) normalize differently than you'd guess.
 
 The `-pp-` infix exists to avoid colliding with official CLIs. `notion-pp-cli` can coexist with whatever `notion-cli` Notion ships themselves.
+
+## Internal Skills
+
+`.claude/skills/` contains internal skills for developing the printing press itself (e.g., `printing-press-retro`). These load automatically when Claude Code is started from inside this repo — no setup needed.
+
+If you're running Claude Code from a different directory and need these skills available, install them globally:
+
+```bash
+.claude/scripts/install-internal-skills.sh
+```
+
+This copies the internal skills to `~/.claude/skills/`.

← 1f918585 fix(skills): archive manuscripts unconditionally after shipc  ·  back to Cli Printing Press  ·  fix(cli): crowd-sniff and generator improvements from Steam 8a8916fd →