Say the recap workflow once, not three times

Medium completed
2026-07-27 agentics refactor Medium effort

Extract the shared recap workflow from eng-recap, team-recap, and product-doc into a single references/recap-core.md, reducing each command to the framing that actually differs: audience, sections, and republish key.

At a glance

Three artifact-tools recap commands say the same thing three times — eng-recap and team-recap alone share 1,568 identical words. Pulling the shared workflow into one reference file leaves each command as a short framing brief, and we will know it worked when the three commands share fewer than 50 identical lines while each still publishes to its own artifact URL key.

Implement Read and implement all steps in the plan at docs/plans/extract-recap-command-core.md — Say the recap workflow once, not three times. Verify against the plan's Tests, Verification, and Acceptance Criteria before reporting done. If everything passed, mark completion in docs/plans/extract-recap-command-core.md — tick each step's [x] marker and each criterion's - [x], set status: completed — and re-render the HTML from the spec. If any check failed, leave status: in-progress and say which.
More ways to run this plan — goal & workflow prompts, file path
Pursue as goal — optimize for the outcome, in parallel
Achieve this goal: Say the recap workflow once, not three times. The plan at docs/plans/extract-recap-command-core.md describes one approach — use it as reference, but optimize for the outcome. Fan out across parallel subagents where that serves the outcome. Verify against the plan's Tests, Verification, and Acceptance Criteria before reporting done. If everything passed, mark completion in docs/plans/extract-recap-command-core.md — tick each step's [x] marker and each criterion's - [x], set status: completed — and re-render the HTML from the spec. If any check failed, leave status: in-progress and say which.
Run as workflow — launch parallel subagents
Run a workflow to implement the plan at docs/plans/extract-recap-command-core.md — Say the recap workflow once, not three times. Brief subagents with the plan file at docs/plans/extract-recap-command-core.md. Reserve a final verification phase for the lead agent, not a subagent. Verify against the plan's Tests, Verification, and Acceptance Criteria before reporting done. If everything passed, mark completion in docs/plans/extract-recap-command-core.md — tick each step's [x] marker and each criterion's - [x], set status: completed — and re-render the HTML from the spec. If any check failed, leave status: in-progress and say which.
File extract-recap-command-core.html
Path docs/plans/extract-recap-command-core.html
Spec docs/plans/extract-recap-command-core.md
Definition of done 9 / 9 done

Context

The story behind this plan — what prompted the work and why it matters now.

The Claude 5 context-engineering guidance names redundancy across context
layers as an anti-pattern: say a thing once, in the place that owns it. The
three artifact-tools recap commands violate this at scale. Measured across
the three files:

- eng-recap and team-recap share 168 identical lines / 1,568 words
- all three share 68 identical lines / 417 words
- combined they are 6,190 words

They are three framings of one workflow — gather the session or PR, scrub it,
build the page, publish, record the republish URL. Only the audience, the
section list, the plain-language rule, and the republish frontmatter key
genuinely differ.

The republish keys are the sharp edge. Four distinct keys live on the same
shared session record, and each of the three commands owns exactly one of
them: session-artifact owns artifact-url:, eng-recap writes
eng-artifact-url:, team-recap writes team-artifact-url:, and
product-doc writes product-artifact-url:. All three command files also
name artifact-url: in a prohibition — product-doc.md says "Never write
artifact-url:" precisely because that key belongs to session-artifact.

Collapsing the commands must not collapse the keys: two commands writing the
same key would silently overwrite each other's published artifact, and any
command reassigned to artifact-url: would clobber the reviewer-first session
recap. Step 3 pins each command's own key explicitly, and Step 4 verifies the
assignments rather than merely counting key names — a distinction that
matters because the prohibition text mentions keys a command must not write.

artifact-tools is the only plugin touched, so exactly one
marketplace.json version bump applies (minor — behavior preserved, structure
changed).

Files that change

Every file this plan touches, and what happens to each one.

agentics/
  • kit/plugins/artifact-tools/references/recap-core.md new the shared gather/scrub/build/publish workflow
  • kit/plugins/artifact-tools/commands/
    • eng-recap.md modified reduce to engineer framing + eng-artifact-url:
    • team-recap.md modified reduce to whole-team framing + its key
    • product-doc.md modified reduce to product framing + product-artifact-url:
  • .claude-plugin/marketplace.json modified bump artifact-tools minor version
  • kit/plugins/artifact-tools/CHANGELOG.md modified record the refactor
  • tests/plugins/test-recap-command-dedupe.sh new objective test
  • .github/workflows/check-plugin-versions.yml modified wire the new test

Steps

The step-by-step work, in order — each step says what to do, why it matters, and how to check it worked.

1
done Diff the three command files pairwise and write the shared-line inventory to a scratch file, separating lines that are genuinely shared workflow from lines that only look identical (shared section headings whose content differs per audience).
Why
collapsing a line that reads the same but means something different per audience is how a refactor silently changes behavior.
Verify
the scratch file classifies every one of the 68 all-three shared lines as either "shared workflow" or "coincidental match".
2
done Write kit/plugins/artifact-tools/references/recap-core.md containing only the Step 1 "shared workflow" lines — PR and session gathering including the 20-file diff cap and --name-only fallback, the blocking security-scrub gate, page build, publish, local-HTML fallback, and the republish-record protocol parameterised by key name.
Why
one file that owns the workflow means a fix to the scrub gate lands in all three commands at once instead of one-third of the time.
Verify
recap-core.md exists and contains no audience-specific words (engineer, stakeholder, glossary).
3
done Rewrite each of the three commands to state its audience, its section list, its plain-language posture, and its republish key explicitly, then delegate the workflow to references/recap-core.md.
Why
the differences are the whole reason three commands exist, so they are what the command file should contain.
Verify
each command file is under 500 words, names its own republish key, and links references/recap-core.md.
4
done Confirm each command still writes its own republish key and still carries the artifact-url: prohibition, checking assignments per file rather than counting key names across files.
Why
a shared key silently overwrites another command's published artifact, and a command reassigned to artifact-url: would clobber the session recap.
Verify
run the three per-file greps below — each prints its own key and nothing else — then confirm all three files still match Never write .artifact-url:; note that a bare grep -o ... commands/*.md | sort -u cannot prove this, because grep prefixes each match with its filename and the prohibition text names keys the command must not write (that form returns 9 lines today, not 3).
5
done Bump artifact-tools to the next minor version in .claude-plugin/marketplace.json and add a kit/plugins/artifact-tools/CHANGELOG.md entry describing the extraction.
Why
any edit under kit/plugins/<name>/ requires a version bump higher than main, per repo convention.
Verify
BASE_REF=main node scripts/check-plugin-versions.mjs exits 0.
6
done Write tests/plugins/test-recap-command-dedupe.sh asserting the three commands share fewer than 50 identical lines, each is under 500 words, references/recap-core.md exists, and — per file, not across files — that eng-recap.md writes eng-artifact-url:, team-recap.md writes team-artifact-url:, product-doc.md writes product-artifact-url:, and none of the three assigns artifact-url: to itself.
Why
without a check, the next feature added to all three commands re-introduces the duplication.
Verify
bash tests/plugins/test-recap-command-dedupe.sh exits 0; pasting 60 lines of recap-core back into two commands makes it exit 1.
7
done Add the new test to .github/workflows/check-plugin-versions.yml.
Why
local-only tests stop running.
Verify
the workflow names test-recap-command-dedupe.sh and parses as valid YAML.

Tests

The tests that prove the change does what it promises.

Tier 1 — This plan changes application code
Objective: the three recap commands no longer duplicate the workflow, and each still targets its own artifact URL. File: tests/plugins/test-recap-command-dedupe.sh; Type: smoke; Asserts: pairwise identical lines across the three commands are under 50, each command is under 500 words, references/recap-core.md exists, and each command file writes its own key (eng-artifact-url: / team-artifact-url: / product-artifact-url:) with none assigning artifact-url: to itself; Run: bash tests/plugins/test-recap-command-dedupe.sh
Integration: each command still produces a publishable recap. File: manual per Verification; Targets: /artifact-tools:eng-recap, :team-recap, :product-doc; Key cases: run each against the same merged PR and confirm three distinct artifacts with audience-appropriate sections

Definition of done

The plan counts as done when every statement below is true — check each one off as you verify it.

Final check

One last pass to confirm the whole change works end to end.

Run bash tests/plugins/test-recap-command-dedupe.sh and confirm exit 0, then
paste 60 lines of recap-core.md into two of the commands, re-run, and confirm
exit 1 before reverting — proving the check detects regression rather than
passing unconditionally.

The behavioral check is the one that matters: pick one merged PR and run all
three of /artifact-tools:eng-recap, /artifact-tools:team-recap, and
/artifact-tools:product-doc against it. Confirm three separate artifacts
publish, that each carries the sections its audience expects (eng-recap has
architecture and no glossary; team-recap has a glossary and diagrams;
product-doc has features and known gaps), and that each writes its own
republish key on the shared session record without clobbering the others. Then
re-run one of the three and confirm it republishes to the same URL rather
than minting a new one.

Finally run git diff --stat and confirm no plugin outside artifact-tools
appears, other than the shared test and workflow files.

Wrapping up

Three gates that must all pass before this plan is marked completed.

Required

Completion Report

Integration test
the behavioral run named in Verification (run all three commands against one merged PR and confirm three artifacts publish) was not performed — it publishes three pages to an external service, which is not something to trigger unprompted. Verified structurally instead: each command names a distinct favicon (🔧 / 🧭 / 📋), a distinct inbox stem, and a distinct republish key; the audience-appropriate sections the Verification section names all survive (eng-recap has Architecture and code paths and no Glossary; team-recap has Glossary and the diagram section; product-doc has Features and Known gaps); and the read-key-before-publishing protocol is intact in recap-core.md. Every acceptance criterion was verified directly. This one Verification item was not.
tests/plugins/test-artifact-tools.sh (modified, not in the plan's Files list)
checks 8, 8b, and 9 asserted the gh preflight, the PR gather block, and the 20-file diff cap lived inside commands/*.md, and check 8 required found >= 3. Extracting the workflow made all three fail. They now assert the same contracts against references/recap-core.md — the file that owns them after this change — and additionally assert that each command loads the core, that none keeps a second gather block, and that exactly one command (eng-recap) opts in to the diff budget. Retargeted, not weakened.
tests/plugins/test-remaining-skill-splits.sh (modified, not in the plan's Files list)
its orphaned-reference check globbed only skills/*/SKILL.md as linkers, so recap-core.md — the first reference read by commands rather than skills — was reported as orphaned. The check now globs commands too. Confirmed the widened logic still flags a genuinely unlinked reference.
product-doc gained the page-build requirements and the SVG-inlining destination
it had neither before, because they lived only in the two siblings. Inheriting them from the shared workflow is the extraction working as intended, and it needed a favicon (📋) to publish under, which it also lacked.
kit/plugins/artifact-tools/README.md (modified, not in the plan's Files list)
one line added to the structure tree for recap-core.md. The tree was already stale by the six references added in 1.9.0; that pre-existing gap was left alone as out of scope.

Next steps

Follow-up ideas that came up along the way — none of them are required to finish this plan.

Apply the same extraction to the share-* skill family

social-media-tools has eleven share-* skills that repeat an eleven-line template-locating shell block verbatim; the same core-plus-framing shape fits.

Paste this prompt into Claude to execute this follow-up:

In the agentics repo, the social-media-tools plugin has eleven share-* skills
that each repeat the same TEMPLATES_DIR locating block (the find ~/.claude
-path "*/social-media-tools/templates" sequence) verbatim. Extract it to
kit/plugins/social-media-tools/references/locate-templates.md and have each
share-* SKILL.md reference it instead. Bump the social-media-tools minor
version in .claude-plugin/marketplace.json and add a CHANGELOG entry. Verify
by grepping for the literal find command across the skills and confirming it
appears exactly once, then running one share-* skill end-to-end to confirm it
still resolves its template directory.