name: regenerating-patches description: Regenerates Node.js patches against pristine upstream source so each applies independently. Use after a Node.js version bump, when patches fail to apply, or when restructuring the patch chain. user-invocable: true allowed-tools: Agent, Read, Edit, Write, Glob, Grep, Bash(git:), Bash(patch:), Bash(diff:), Bash(cp:), Bash(rm:), Bash(mkdir:), Bash(ls:), Bash(cat:), Bash(head:), Bash(tail:), Bash(wc:), Bash(awk:), Bash(grep:), Bash(sed:), Bash(find:*), AskUserQuestion
regenerating-patches
Regenerate Node.js patches against the current pristine upstream tag so every patch applies cleanly in numeric order. This is the canonical recovery flow when an upstream version bump shifts line numbers under our patches.
Scope
- node —
packages/node-smol-builder/patches/source-patched/*.patchagainstpackages/node-smol-builder/upstream/node
Phase 1 — validate environment
cd "$CLAUDE_PROJECT_DIR" 2>/dev/null || cd "$(git rev-parse --show-toplevel)"
git status --short
For each in-scope target:
- Confirm submodule is initialized:
[ -d "$UPSTREAM_DIR/.git" ](file-or-dir). - Resolve the pristine version from the submodule's HEAD tag — do not hardcode:
For node, this should match the value in repo-rootcd "$UPSTREAM_DIR" VERSION=$(git describe --tags --exact-match 2>/dev/null || git rev-parse --short HEAD).node-version(with a leadingv). - Reset to pristine:
git checkout -- . && git clean -fd(inside the submodule). - List the patch files and capture which ones currently fail. The fast pre-flight:
The 4-line skip is thefor p in $PATCH_DIR/*.patch; do tail -n +4 "$p" > /tmp/test.patch git apply --check /tmp/test.patch >/dev/null 2>&1 || echo "FAIL $(basename $p)" done# @…-versions:/# @description:/ blank-#/ blank-#header that lives above the unified diff (see Patch Format below). All passing patches keep their existing content; only the FAIL list gets regenerated.
If any pre-flight step errors, surface the diagnostic to the user and stop.
Phase 2 — spawn the regen Agent
Dispatch one Agent call with subagent_type: general-purpose. The full task — read each failing patch, re-anchor against pristine upstream, write the regenerated patch back to its original path — is delegated to the agent. The skill's role is to construct the prompt and validate the output; it does not edit patches itself.
The Agent call MUST include in its prompt:
UPSTREAM_DIR(absolute) andPATCH_DIR(absolute)- The resolved
VERSIONstring (e.g.v26.1.0) - The complete list of failing patches from Phase 1 (basename + the failing target line from
git apply --check) - The list of passing patches in numeric order — those must be applied before the regen target so each regen sees the cumulative state of all earlier patches (some patches anchor inside regions added by earlier patches, e.g.
023-smol-power-binding.patchmodifies a block that018-smol-builtin-bindings.patchintroduces) - A pointer to
reference.mdfor edge cases (timestamp collisions, target-file-not-found, header normalization, common failure modes)
The agent's per-patch loop:
- Reset the submodule to pristine:
git checkout -- . && git clean -fd - Replay every passing patch in numeric order whose number is below the current target. Use the patch tool with the
tail -n +4strip:tail -n +4 "$EARLIER_PATCH" | patch -p1 --silent - Read the current (broken) target patch to extract: original header (
# @node-versions: …and# @description: …lines), all+/−content lines, and the file path(s) it modifies. - Apply intent manually: copy the modified file from the cumulative-patched tree to
/tmp/patch-rebuild/b/<file>, copy a parallel pristine-cumulative version to/tmp/patch-rebuild/a/<file>, and use the Edit tool to add/remove the same lines the original patch did. The Edit tool's exact-match semantics force the agent to preserve indentation and surrounding context byte-for-byte. - Generate the new patch:
That seconddiff -ruN /tmp/patch-rebuild/a/ /tmp/patch-rebuild/b/ \ | sed -E 's@^(--- |\+\+\+ )/tmp/patch-rebuild/[ab]/@\1a/@; t; s@^(--- |\+\+\+ )/tmp/patch-rebuild/[ab]/@\1b/@'sedis wrong — use this canonical form instead:diff -ruN /tmp/patch-rebuild/a/ /tmp/patch-rebuild/b/ \ | sed -E 's@/tmp/patch-rebuild/a/@a/@; s@/tmp/patch-rebuild/b/@b/@' \ | grep -v '^[-+]\{3\}.*\t' # strip timestamps - Prepend the original header verbatim (4 lines:
# @node-versions: …,# @description: …, optional# detaillines each followed by a#separator — see Patch Format below). - Validate:
tail -n +4 NEW_PATCH | git apply --check(against the cumulative-patched tree). Must exit 0. - Write the regenerated patch back to
$PATCH_DIR/<original-name>.patch, overwriting. - Reset the submodule again before the next iteration.
After all failing patches are regenerated, run a final pristine→all-patches replay to confirm every patch in the directory still applies in numeric order. End with the submodule at pristine HEAD (not committed).
Phase 3 — report
The skill (not the agent) should print:
version: the pristine tag/SHAregenerated: list of patch basenames the agent rewroteunchanged: count of patches that already appliedunrecoverable: any patches the agent couldn't fix automatically + the diagnostic
The skill does not commit. The user reviews the diff and commits manually.
Patch format
Patches use a 4-line metadata header above the unified diff:
# @node-versions: v26.1.0
# @description: One-line summary
#
# Optional multi-line detail. Each non-blank line begins with #.
#
--- a/<target-file>
+++ b/<target-file>
@@ -<line>,<n> +<line>,<n> @@
context
-old
+new
context
For iocraft patches: replace # @node-versions: with # @iocraft-versions:. Never put timestamps on the ---/+++ lines (diff -ruN adds them; the post-process sed strips them).
The validator git apply --check rejects timestamps and demands matching context — those are the two most common regen failures. See reference.md § Common Failure Modes for the full list.
Constraints
- Don't modify upstream/node tree state at the end. The submodule must be at pristine HEAD when the skill returns; uncommitted modifications would mask drift on the next run.
- Don't commit or push. The user reviews the regenerated patches before committing.
- Don't create
.backup-*files. Earlier versions of this skill did; they pollute the patch directory and confusefind $PATCH_DIR -name '*.patch'. If a working tree already has them, leave them — but generate new patches only. - Use
diff -ruNfor the regen, nevergit difforgit format-patch. Both inject git-specific markers (index <hash>,new file mode) that the build pipeline'spatch -p1doesn't expect. - Strip timestamps before validating:
grep -v $'^[-+]\{3\}.*\t'(or equivalent). - Run the agent once. Spawning per-patch agents loses cumulative state and is much slower than one agent processing the list serially.
- Explanatory comments belong in the header, not inline in hunks. Add
# …lines between# @description:and the first--- a/; never add# …or// …lines inside hunk bodies just to explain the change. Inline comments inflate the diff against upstream, force hunk-count bumps when edited, and survive into the patched source as noise. Seedocs/references/btm-source-patches.md§ Comments: header, not inline.
See reference.md for: edge cases, rollback procedures, retry logic, header normalization details, common failure modes, and cross-platform considerations (BSD vs GNU diff/sed).