[object Object]

← back to Cli Printing Press

fix(skills): reprint discovers and carries prior patches (#1649)

f9bb82c892c5628fdb73ae950841d486dc865ca0 · 2026-05-19 00:17:49 -0700 · Trevin Chow

* fix(skills): reprint discovers and carries prior patches

Reprinting silently dropped post-publish hand-fixes recorded in
.printing-press-patches.json, regressing live-validated API quirks
and architectural patterns. Phase B now re-fetches the public
patches file (covering the case where amends landed without a
regen, so local can lag even when run_id matches), and Phase D
threads non-empty patches into the /printing-press hand-off under
a new "## Prior Patches" heading.

Patches are framed as an informational watch-list, not a re-apply
mandate — the machine may have absorbed some upstream, and the
retro+bugfix loop is the right mechanism for that drift, not a
per-patch reconciliation gate.

* fix(skills): make patches discovery's fetch durable

Address Greptile P2 findings: the prior mktemp-based fetch leaked the
temp file on the success path (accumulating across repeated reprint
runs) and used the ephemeral path as the downstream `$PATCHES_SOURCE`
reference, which a long-running /printing-press hand-off could find
absent.

Refresh the local patches file from public via mktemp + atomic
rename instead. The temp file is always either renamed or removed,
and PATCHES_SOURCE is always the stable local path the downstream
agent can open.

Files touched

Diff

commit f9bb82c892c5628fdb73ae950841d486dc865ca0
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Tue May 19 00:17:49 2026 -0700

    fix(skills): reprint discovers and carries prior patches (#1649)
    
    * fix(skills): reprint discovers and carries prior patches
    
    Reprinting silently dropped post-publish hand-fixes recorded in
    .printing-press-patches.json, regressing live-validated API quirks
    and architectural patterns. Phase B now re-fetches the public
    patches file (covering the case where amends landed without a
    regen, so local can lag even when run_id matches), and Phase D
    threads non-empty patches into the /printing-press hand-off under
    a new "## Prior Patches" heading.
    
    Patches are framed as an informational watch-list, not a re-apply
    mandate — the machine may have absorbed some upstream, and the
    retro+bugfix loop is the right mechanism for that drift, not a
    per-patch reconciliation gate.
    
    * fix(skills): make patches discovery's fetch durable
    
    Address Greptile P2 findings: the prior mktemp-based fetch leaked the
    temp file on the success path (accumulating across repeated reprint
    runs) and used the ephemeral path as the downstream `$PATCHES_SOURCE`
    reference, which a long-running /printing-press hand-off could find
    absent.
    
    Refresh the local patches file from public via mktemp + atomic
    rename instead. The temp file is always either renamed or removed,
    and PATCHES_SOURCE is always the stable local path the downstream
    agent can open.
---
 skills/printing-press-reprint/SKILL.md | 116 +++++++++++++++++++++++++++++----
 1 file changed, 102 insertions(+), 14 deletions(-)

diff --git a/skills/printing-press-reprint/SKILL.md b/skills/printing-press-reprint/SKILL.md
index af4a2e0f..ede5878c 100644
--- a/skills/printing-press-reprint/SKILL.md
+++ b/skills/printing-press-reprint/SKILL.md
@@ -2,12 +2,13 @@
 name: printing-press-reprint
 description: >
   Regenerate an existing printed CLI from scratch under the current Printing
-  Press, with prior research and prior novel features carried into the
-  novel-features subagent's reprint reconciliation rather than dropped on
-  the floor. Pulls the CLI from the public library if it isn't local,
-  recommends reuse-vs-redo of prior research based on age, then hands off
-  to /printing-press with the right context. Use when a machine upgrade
-  would benefit a published CLI more than manual polish.
+  Press, with prior research, prior novel features, and prior patches
+  (post-publish hand-fixes) carried into the writing pipeline as
+  reconciliation context rather than dropped on the floor. Pulls the CLI
+  from the public library if it isn't local, recommends reuse-vs-redo of
+  prior research based on age, then hands off to /printing-press with the
+  right context. Use when a machine upgrade would benefit a published CLI
+  more than manual polish.
   Trigger phrases: "reprint <api>", "regenerate <api>", "redo the <api> CLI",
   "rebuild <api> from scratch", "this CLI would benefit from a reprint".
 allowed-tools:
@@ -59,6 +60,12 @@ fetch the public library `registry.json` once, then exact → normalized →
 fuzzy match. The argument can be an API slug (`notion`), a brand name
 (`cal.com`), an old `<api>-pp-cli` form, or close enough.
 
+Capture from the matched registry entry: `API_SLUG` (from `.name`) and
+`LIB_PATH` (from `.path`, e.g., `library/productivity/cal-com`). Phase B
+uses `$LIB_PATH` for the public patches fetch. For the "present | absent"
+never-published row, `$LIB_PATH` stays empty — Phase B's fetch
+short-circuits on that.
+
 Then check what exists locally and reconcile against the public library by
 reading both provenance manifests' `run_id` and `generated_at`:
 
@@ -75,10 +82,12 @@ When invoking `/printing-press-import`, let it own backup, overwrite,
 build-verify, and module-path-rewrite. Wait for it to return clean before
 continuing.
 
-## Phase B — Verify prior research is reconcilable
+## Phase B — Verify reconcilable prior context
 
-Confirm the two paths the novel-features subagent checks for prior research
-are populated:
+Locate the two artifacts the writing pipeline should be aware of: research
+(drives novel-features Pass 2(d)) and patches (post-publish hand-fixes
+recorded by `/printing-press-amend`, e.g. live-discovered API quirks the
+spec didn't reveal).
 
 ```bash
 LIB_TARGET="$PRESS_LIBRARY/$API_SLUG"
@@ -86,10 +95,12 @@ LIB_RESEARCH="$LIB_TARGET/research.json"
 MAN_RESEARCH=$(ls -1t "$PRESS_MANUSCRIPTS/$API_SLUG"/*/research.json 2>/dev/null | head -1)
 ```
 
-If neither exists, the published CLI predates `research.json` provenance.
-The subagent will treat the run as a first print and Pass 2(d) reprint
-reconciliation will not fire — there is nothing for it to read. Surface
-this and ask:
+### Research absent
+
+If neither research path exists, the published CLI predates `research.json`
+provenance. The subagent will treat the run as a first print and Pass 2(d)
+reprint reconciliation will not fire — there is nothing for it to read.
+Surface this and ask:
 
 > Published `<api>` was built before `research.json` provenance landed.
 > Without it, the novel-features subagent will treat this as a first
@@ -99,6 +110,43 @@ this and ask:
 If the user declines, exit. If they continue, record the absence so the
 hand-off prompt notes that this is a degraded reprint.
 
+### Patches discovery
+
+Refresh the local patches file from public when reachable, then read
+locally so downstream references are durable. Amends may have landed
+against the public copy without triggering a regen, so the local file
+can lag even when `run_id` matches; this step closes that gap. The fetch
+writes to a temp file and atomically renames on success, so a partial
+transfer never replaces a good local copy.
+
+```bash
+PATCHES_SOURCE="$LIB_TARGET/.printing-press-patches.json"
+if [[ -n "$LIB_PATH" ]]; then
+  PATCHES_TMP=$(mktemp)
+  if gh api -H "Accept: application/vnd.github.v3.raw" \
+       "repos/mvanhorn/printing-press-library/contents/$LIB_PATH/.printing-press-patches.json" \
+       > "$PATCHES_TMP" 2>/dev/null; then
+    mv "$PATCHES_TMP" "$PATCHES_SOURCE"
+  else
+    rm -f "$PATCHES_TMP"
+  fi
+fi
+PATCH_COUNT=$(jq '.patches | length' "$PATCHES_SOURCE" 2>/dev/null || echo 0)
+```
+
+If `$PATCH_COUNT == 0` or the file is missing entirely (older CLI
+predating the patches contract), skip the rest of this subsection — no
+patches block in the hand-off.
+
+If `$PATCH_COUNT > 0`, surface a one-liner to the user before continuing:
+
+> Public `<api>` has `$PATCH_COUNT` recorded patch(es) against the prior
+> printed CLI. Will carry into the brief as a watch-list (informational,
+> not a re-apply mandate) so the fresh code doesn't silently regress
+> live-validated fixes.
+
+Hold `$PATCHES_SOURCE` and `$PATCH_COUNT` for Phase D.
+
 ## Phase C — Recency recommendation
 
 Pull `researched_at` from the most-recent prior `research.json` and
@@ -147,7 +195,7 @@ Ask via `AskUserQuestion`:
 
 ## Phase D — Hand off to `/printing-press`
 
-Invoke `/printing-press <api>` and bundle three things into the prompt:
+Invoke `/printing-press <api>` and bundle these into the prompt:
 
 1. **A header line** stating the user already chose to regenerate, so
    Phase 0's library-check should select "Generate a fresh CLI" and not
@@ -158,6 +206,46 @@ Invoke `/printing-press <api>` and bundle three things into the prompt:
    block. This propagates into the brief as `## User Vision` and becomes
    Pass 2(e) input to the novel-features subagent — the right hook for
    "I want better MCP support" → bias the brainstorm accordingly.
+4. **Prior patches** — only when Phase B found `$PATCH_COUNT > 0`.
+   Include under a `## Prior Patches` heading.
+
+   Lead the section with this framing sentence, verbatim:
+
+   > The following were hand-fixed against the prior printed CLI. They
+   > are informational — the machine may have absorbed some of these
+   > upstream since the patch was applied. Stay aware so the fresh code
+   > doesn't silently regress the underlying API truth or architectural
+   > pattern, but do not treat as a re-apply checklist.
+
+   Then summarize the patches from `$PATCHES_SOURCE`. Lead each entry
+   with the *substance* the patch encodes (the API truth, the
+   architectural pattern, the cross-file convention) drawn from each
+   patch's `reason` and `validated_outcome` — not just `summary`. Patch
+   metadata (`id`, `files`) is fine as parenthetical context but never
+   the headline.
+
+   Scale the section to patch count:
+
+   - **1–3 patches**: one short paragraph per patch — substance first,
+     then how it manifested. Model the shape on:
+     > Linear's personal API keys go in `Authorization: lin_api_…` raw,
+     > with no `Bearer` prefix; OAuth tokens use `Authorization: Bearer
+     > <token>`. The prior CLI shipped with `auth set-token` only
+     > (Bearer-defaulted) and was patched post-publish to add
+     > `auth set-api-key` plus the raw-header path in `config.go`
+     > (`linear-auth-api-key-vs-oauth-token`).
+   - **4–9 patches**: one tight sentence per patch, same substance-first
+     shape.
+   - **10+ patches**: thematic summary by category (auth, pagination,
+     query encoding, MCP additions, helpers, classifiers, error
+     envelopes). Two or three sentences per theme citing 2–3 patch `id`s
+     as evidence. Do not enumerate all entries inline.
+
+   Close the section with a pointer so a downstream agent can drill into
+   any specific patch when working in the relevant area:
+
+   > Full patch detail: `$PATCHES_SOURCE`. Schema:
+   > `PatchesIndex` in `internal/pipeline/climanifest.go`.
 
 Do **not** pass a separate "this is a reprint" marker. The novel-features
 subagent runs unconditionally on every print and discovers prior research

← b5b1dd59 feat(cli): add cliutil.LooksLikeJWT shared validator with le  ·  back to Cli Printing Press  ·  feat(cli): add auth set-token escape hatch for cookie-auth C 275646a3 →