redis/finalize
Distil a PR's accumulated knowledge into the durable squash commit message — reconcile the /reflect experience notes across its commits with the review feedback into one end-state record, and propose promoting cross-cutting lessons to learning skills/memory. Use just before squash-merging a PR. Prepares the message and the gh merge command; does not merge.
npx skills add https://github.com/redis/docs --skill finalize
/reflect scatters provisional experience notes across a PR's WIP commits while the work
happens. The PR then accumulates review feedback. **This skill compresses both into the one
commit that survives the squash** — the durable, location-reachable record a future agent
meets via git log / git blame.
Vocabulary, subject convention, and format are defined once in
../_shared/commit-trailers.md — read it first, then
read this for *how to reconcile and where to put the output*.
/reflect → WIP commit messages [episodic, provisional, disposable]
PR review + bots → PR comments [external critique]
↓ THIS SKILL, just before squash
/finalize → squash commit message [durable, location-reachable]
↓ fork
promotion → learning skills / memory [cross-cutting, always-loaded]
The easy version — collect every trailer and paste them in — is wrong, because **the PR's job
is to change its own mind. A WIP note the PR later contradicted must be dropped**, not
carried, or it lands frozen and wrong on main:
> WIP commit 1: Rejected: server-side sessions | too much infra. Review pushes back; commit
> 4 *adopts* server-side sessions. Concatenation carries Rejected: server-side sessions onto
> main — now actively misleading. Reconciliation drops it (or flips it to a Constraint).
So read the whole arc in order (commits + comments + check outcomes) and emit only what is
still true at the end. This is judgment work — it's why this is a skill, and why it's a
human-triggered pre-merge step, not an automated merge hook.
git log --reverse main..HEAD --format='%(trailers:only,unfold)' forthe trailers, plus the commit bodies for the prose. --reverse is required — the arc
walk in Step 2 is oldest-first, and the default git log order (newest-first) would invert
the timeline and let stale, overturned notes win. (Drop the deliberate no-note commits.)
Also fold in any PR comment carrying the <!-- reflect-note --> marker — those are
author-side reflection routed to a comment because the commit was already pushed, not
critique (see ../_shared/commit-trailers.md).
<!-- reflect-note --> marker.Reuse /docs:assess-comments if available (it already collects, role-tags, and splits
open/resolved across tools); otherwise read them directly
(gh api repos/<o>/<r>/pulls/<n>/comments, .../issues/<n>/comments,
.../pulls/<n>/reviews). Note which findings were *fixed* vs *dismissed* vs *still open*.
Walk the arc in commit/comment order and produce the end state:
the diff, don't trust the note.
settled on a rule becomes one Constraint:; an approach the review killed becomes
Rejected:. Copy what survived, not the conversation.
bury it in a trailer.
Same split /reflect Step 3 makes, now over the reconciled set:
squash commit.
standing gotcha, a project-wide constraint) → propose an edit to the relevant learning
skill or a memory file. A commit can't guarantee it'll be seen. **Propose, don't apply
silently** — show the user the exact addition and target file.
Write the distilled message per the shared shape: a DOC-XXXX subject, a tight body paragraph
(the durable narrative for humans + the history bot), then the reconciled trailer block. Far
fewer trailers than the inputs held — distillation is compression, not collection.
Keep the subject and the body-file separate: the body-file holds the body paragraph
and the trailer block only — no subject line. The subject is passed via --subject in
Step 5, so repeating it in the file would duplicate the title into the commit body.
This repo squashes with squash_merge_commit_message = COMMIT_MESSAGES (verified
2026-06-25), so the default squash body is every WIP commit message concatenated — exactly
the raw dump we're avoiding. Override it explicitly; never rely on the default:
gh pr merge <n> --squash \
--subject "DOC-XXXX <summary>" \
--body-file <distilled-message-file>
Present three things and stop:
gh pr merge … --body-file command.The human triggers the merge. Reconciliation is judgment-heavy and a squash-merge is hard to
reverse, so finalize *prepares*; it does not press the button. (An opt-in auto-merge could be
added later, but it inherits all the risk below.)
main is not. When the arcis ambiguous, drop the trailer and leave the nuance in body prose. Bias to fewer, certain
trailers.
/reflectand the reviewers serialised. Garbage in stays garbage; verify contradictions against the
actual diff.
— least context, no human, and under COMMIT_MESSAGES it must *also* override --body-file
or it concatenates raw. Keep it phase 2, and conservative.
the always-loaded tier starves. It is not optional.
Take redis/finalize from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.