Skip to content

feat(.claude): pr-roadmap-link skill — lock slug at PR-create time#129

Merged
ntatschner merged 1 commit into
nextfrom
feat/pr-roadmap-link-skill
May 28, 2026
Merged

feat(.claude): pr-roadmap-link skill — lock slug at PR-create time#129
ntatschner merged 1 commit into
nextfrom
feat/pr-roadmap-link-skill

Conversation

@ntatschner
Copy link
Copy Markdown
Collaborator

Summary

  • New .claude/skills/pr-roadmap-link/SKILL.md — fires at PR-create time, asks "which roadmap item does this PR ship work for?", injects Roadmap-Item: <slug> trailer + roadmap/<slug> label per spec §3.3.
  • Companion to the existing roadmap-tracking skill (PR feat(roadmap): operator tooling — drafts publisher, project normalizer, auto-publish, tracking skill #113) which picks the slug at feature START. This new skill confirms / locks the slug at PR-CREATE time so the slug travels with the PR for later release attribution.
  • Pure metadata skill — no code, tests, or runtime configs touched.

Why

After we deleted vars.ROADMAP_ITEM_SLUG (placeholder was stale; see memory entry release-slug-per-release), the convention became: pass --roadmap-item-slug <slug> to release-promote.mjs per release. But the slug decision was being deferred to release time, which makes the operator reconstruct "which roadmap item did this release ship?" from git history at release time — fragile, especially across reviewer hand-offs.

Locking the slug onto the PR via the spec-defined trailer + label means the operator just reads it off the PR being shipped. Two-sentence summary at release time becomes one curl.

Trigger semantics

Fires when an agent is about to gh pr create --base next (or --base main), unless:

  • Branch is docs/ / chore/ / style/ / refactor/ / release/ / revert/
  • All commits are Conventional Commits chore: / docs: / style: / test: / ci:
  • User explicitly says "no roadmap" / "skip tracking" / "untracked"
  • PR body already carries Roadmap-Item: trailer (idempotent re-runs)

Interaction map

  • AFTER superpowers:brainstorming (feature scoping)
  • AFTER roadmap-tracking (slug at feature start — writes .claude/session-roadmap-slug)
  • AFTER implementation work
  • BEFORE commit-commands:commit-push-pr / superpowers:finishing-a-development-branch / manual gh pr create

Test plan

  • Skill markdown parses (124 lines, valid frontmatter)
  • name field matches dir name (pr-roadmap-link)
  • description: includes explicit trigger + skip phrases
  • After merge: fresh session, type "let me open a PR for this branch" — confirm skill activates and asks the slug question
  • After merge: fresh session, type "no roadmap, just open the PR" — confirm skill respects skip
  • After merge: PR body verifies trailer format is Roadmap-Item: <slug> exactly (two newlines before, nothing on the trailer line)

No runtime impact; safe to merge before / after the rest of the in-flight PRs (#128 etc.). Only affects how future PRs are framed.

Companion to the `roadmap-tracking` skill (which picks a slug at
feature START). This new skill fires at PR-CREATE time and lays
down two attribution mechanisms per spec §3.3:

1. `Roadmap-Item: <slug>` trailer at the bottom of the PR body
2. `roadmap/<slug>` label on the PR

So when the operator later runs `release-promote.mjs --roadmap-item-slug`
for the release that ships the PR, they can read the slug off the
PR directly instead of reconstructing it from git history.

Triggers on "open PR" / "create PR" / "gh pr create" phrases or
when an agent is about to invoke `gh pr create --base next` /
`--base main`. Skips for docs/chore/refactor/style branches,
when the user says "no roadmap" / "untracked", or when the PR
body already carries the trailer.

Skill flow:
1. Gathers context in parallel (branch name, commits, diff,
   public roadmap items, .claude/session-roadmap-slug if the
   `roadmap-tracking` skill stashed one).
2. Synthesizes a recommendation in priority order (session-slug
   stash, branch-name substring match, commit-message hint, no
   recommendation).
3. AskUserQuestion with recommendation first (link existing /
   pick from list / create new / skip).
4. On link: injects trailer + creates `roadmap/<slug>` label if
   missing + adds label to `gh pr create --label` args.
5. After PR creation, surfaces a one-line operator instruction:
   "When you cut the release, pass `--roadmap-item-slug <slug>`."

Wiring assumptions: gh CLI authenticated, repo is
TheCodeSaiyan/StarStats (hardcoded for now), `Roadmap-Item:`
trailer format matches release-promote.mjs's regex.

Pairs with memory entry [[release-slug-per-release]] and the
existing [[roadmap-tracking]] skill.
@ntatschner ntatschner merged commit 28658ea into next May 28, 2026
9 checks passed
@ntatschner ntatschner deleted the feat/pr-roadmap-link-skill branch May 28, 2026 02:59
ntatschner pushed a commit that referenced this pull request May 28, 2026
The roadmap-tracking chain ends with the operator typing
`--roadmap-item-slug <slug>` on the release-promote command. That's
the last manual step — reconstruct "which roadmap item did this
release ship work for?" from human memory at release time, after
the fact. Fragile across reviewer hand-offs.

Replace that manual lookup with discovery: scan PRs merged between
the previous track tag and the release's target SHA, extract their
`roadmap/<slug>` labels (planted by the `pr-roadmap-link` skill at
PR-create time), and use the unique slug as the tag annotation
automatically.

Resolution priority (printed at promote time so the operator sees
the chosen path):
  1. Explicit `--roadmap-item-slug <slug>` — wins, no discovery.
  2. `--no-auto-slug` flag — skip discovery; unsigned tag.
  3. Auto-discover (default):
     - 0 slugs in range → no annotation (status quo)
     - 1 slug → use it; log the discovery source
     - >1 slugs → fail loud; demand explicit choice

Discovery details:
  - Finds the previous track tag via `previousTrackTagBelow`
    (channel-agnostic within the same track; rc.2 finds beta.2,
    new minor's alpha.1 finds previous live).
  - `gh pr list --state merged --base next --search merged:>{date}
    --json number,mergeCommit,labels` — coarse date filter, then
    narrows by `git merge-base --is-ancestor` to PRs whose merge
    commit is in (prevTag, targetSha].
  - `parseSlugsFromPrLabels` extracts unique `roadmap/<slug>`
    suffixes from the PR labels.
  - Network/auth failures degrade gracefully: any gh error skips
    discovery (returns null), and the caller treats that the same
    as "no slug to annotate" — release never blocks on the
    discovery hop.

Two new pure functions exported for testing:
  - `parseSlugsFromPrLabels(prs)` — dedupes slugs across PRs;
    tolerates both gh's object-label shape and string-label shape.
  - `previousTrackTagBelow(tagList, track, currentVersion)` —
    track-scoped, channel-agnostic; returns the highest tag of the
    same track strictly below currentVersion.

13 new tests cover the pure parts; the IO glue is exercised via
the existing live release flow.

Pairs with the `pr-roadmap-link` skill (PR #129). Together they
close the loop: pick slug at feature start → confirm at PR time →
auto-flow at release time → server records the channel transition.
The operator's only remaining decision is "is this PR tracked, and
which slug?" — exactly the kind of intent only a human knows.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant