Skip to content

v1.58.0.0 feat: /pr-prep — pre-PR upstream duplicate audit + /ship Step 1.5 gate - #1696

Open
BenjaminDSmithy wants to merge 20 commits into
garrytan:mainfrom
BenjaminDSmithy:feat/pr-prep-skill
Open

BenjaminDSmithy wants to merge 20 commits into
garrytan:mainfrom
BenjaminDSmithy:feat/pr-prep-skill

Conversation

@BenjaminDSmithy

@BenjaminDSmithy BenjaminDSmithy commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

New /pr-prep skill: a pre-PR upstream duplicate audit. It walks git log base..HEAD, derives search keywords per commit from the subject + changed file
paths, queries the upstream repo's open issues and PRs via gh, and scores each
commit against open work (EXACT_DUP / OVERLAP / SIBLING / CLEAN). EXACT_DUP
refuses to proceed. /ship runs it as a Step 1.5 gate before any merge, test,
or version bump.

Why it helps you, the maintainer: it kills duplicate community PRs before
they're filed — the triage you do most. (Dogfood result below.)

What it does

  • Step 1.4 — reads the upstream CONTRIBUTING.md: pre-push gates, test
    layout, branch/commit conventions, banned patterns. Used inline when writing
    the PR body.
  • Step 4 — title-token + file-overlap Jaccard scoring, state-weighted. The
    bucketing is implemented in bin/gstack-pr-prep-score (pure, unit-tested) and
    referenced by the skill as the canonical scorer.
  • Step 4.4 — optional codex second opinion on CLEAN commits (different model
    family for independent signal; soft-skips if codex is absent).
  • Step 4.5 — per-commit pre-push gate + trigger-path + tests-added annotation.
  • Step 4.6 — commit-message style conformance against the upstream repo's
    convention (sampled from its CONTRIBUTING.md + git log), not the
    contributor's personal house style.
  • /ship Step 1.5 — invokes pr-prep with GSTACK_FROM_SHIP=1; EXACT_DUP aborts
    ship with resolution paths; skips cleanly on solo repos and --skip-pr-prep.

Dogfooded on itself

Ran /pr-prep on this branch against garrytan/gstack@main: CLEAN — 13/13
commits, no EXACT_DUP, no OVERLAP
, in ~30-60s of gh queries. No existing
pr-prep / duplicate-audit skill upstream; the open ship PRs all touch unrelated
parts of the flow. Full audit is in a comment on this PR.

Tests + release

  • test/pr-prep-score.test.ts — 13 deterministic cases pinning the scorer's
    bucket thresholds (gate-tier, free).
  • Registered in AGENTS.md, docs/skills.md, scripts/proactive-suggestions.json,
    and test/skill-coverage-matrix.ts.
  • Ship golden baselines refreshed (Claude / Codex / Factory); ALLOWED_SUBSTEPS
    updated for the new Step 1.5 sub-step.
  • v1.58.0.0 (MINOR) + CHANGELOG entry; VERSION / package.json synced.
  • bun test is green except environment-specific cases (local Swift toolchain,
    gbrain config, local git state) that also fail on a clean main.
  • Note: as a cross-repo fork PR, the eval/E2E CI jobs can't receive base-repo
    secrets, so those will error regardless of code; happy to push the branch to a
    base-repo branch and re-target if you'd prefer green eval CI.

Motivation

Real case (2026-05-26): a contributor branch on garrytan/gbrain had 8 commits
ready for upstream PRs. Without pr-prep, 4 of 8 unverified commits would have been
duplicates of already-open work:

Each would have cost a triage round and a close. pr-prep catches all of them in
one gh query pass before anything is filed.

@BenjaminDSmithy
BenjaminDSmithy force-pushed the feat/pr-prep-skill branch 3 times, most recently from 8d43b46 to 2a5fa08 Compare June 10, 2026 11:57
@BenjaminDSmithy

Copy link
Copy Markdown
Contributor Author

Updated this branch.

Rebased onto main (v1.57.8.0) — clean, no conflicts.

Commit messages now follow the repo convention across every commit: type(scope): subject, plain-prose body, and the Co-Authored-By trailer this repo uses. Also dropped a "+ tests" subject that didn't match its diff.

New — Step 4.6 in /pr-prep: commit-message style conformance. The audit now checks each commit against the upstream repo's own convention (CONTRIBUTING.md commit rules → de-facto shape sampled from git log upstream/<base> → conventional-commit fallback) and flags personal-template bodies, missing or extra trailers, and subjects that promise content they don't contain. Soft warning, never blocks — style isn't a duplicate, but it's the cheapest reviewer-goodwill win in the audit.

Test + registration fixes so the branch is green:

  • Added 1.5 to ALLOWED_SUBSTEPS for the new Step 1.5 ship gate (skill-validation step-numbering).
  • Registered /pr-prep in AGENTS.md and docs/skills.md (doc-inventory cross-check).
  • Added /pr-prep to test/skill-coverage-matrix.ts with a gate-tier test.
  • Regenerated pr-prep/SKILL.md and scripts/proactive-suggestions.json against the current resolvers.
  • Refreshed the three ship golden baselines (test/fixtures/golden/{claude,codex,factory}-ship-SKILL.md) for the Step 1.5 block.

bun test passes on all PR-relevant checks. The only remaining failures are environment-specific (local Swift toolchain, gbrain config, local git state) and reproduce on a clean main.

@BenjaminDSmithy

Copy link
Copy Markdown
Contributor Author

Dogfooded /pr-prep on this PR

Ran the skill's own audit against garrytan/gstack@main (the thing it's built to do — so it seemed only fair to point it at itself).

Result: CLEAN — 11/11 commits, no EXACT_DUP, no OVERLAP.

  • No existing /pr-prep or pre-PR duplicate-audit skill upstream (closest hit, #1343 /plan-status, is an unrelated plan-progress checker).
  • The /ship Step 1.5 gate doesn't collide with the open ship PRs (#1716 SHIP-RECEIPT, #632 ship log, #684 resume-safe reruns, #1862 package-lock sync, #842 stack-aware tests, #338 review-satisfies-ship, #1944 REST updates) — all touch different parts of the ship flow; none add a pre-PR audit step.

So this contribution isn't duplicating open work, and the skill demonstrably runs end-to-end on a real branch.

@github-actions github-actions Bot changed the title feat: /pr-prep skill — pre-PR upstream duplicate audit + /ship Step 1.5 gate + codex review v1.58.0.0 feat: /pr-prep skill — pre-PR upstream duplicate audit + /ship Step 1.5 gate + codex review Jun 10, 2026
@BenjaminDSmithy BenjaminDSmithy changed the title v1.58.0.0 feat: /pr-prep skill — pre-PR upstream duplicate audit + /ship Step 1.5 gate + codex review v1.58.0.0 feat: /pr-prep — pre-PR upstream duplicate audit + /ship Step 1.5 gate Jun 10, 2026
@time-attack

Copy link
Copy Markdown
Contributor

@16francej — recommend closing this PR. Thanks for putting this together. We aren’t accepting new standalone skills right now, so we’re going to pass on this proposal. We can revisit the area later if it becomes part of GStack’s core workflow.

@BenjaminDSmithy

Copy link
Copy Markdown
Contributor Author
TL;DR
  Split      Unrelated bug fix pulled out into #2640. This PR is /pr-prep only.
  Ask        Reconsider the standalone-skill call, or tell me to fold it into
             /ship Step 1.5 with no new catalog entry. Either ships same day.
  Evidence   Three tracker waves in five weeks closed 111+ stale or duplicate
             items. Your own v1.64.0.0 notes: contributors "independently
             fixed the same bug within days of each other".
  Ready      Clean branch off main: 15 files, +2,532/-10, suite green.
  Cost       Standalone version = one catalog entry. Ship-only version = zero.

@time-attack @16francej @garrytan: tagging all three, since this is a policy call rather than a code review.

Understood on the policy, and the reasoning holds: a new skill is a permanent cost to the discovery catalog that every user pays whether they run it or not. test/skill-size-budget.test.ts prices that exactly. If the answer stays no I'll close this.

Two things first.

Split for review

This PR had accumulated work that has nothing to do with /pr-prep. I've pulled the unrelated half out into #2640 (a heredoc deadlock that hangs ./setup --help on any Mac with Homebrew bash on PATH, 11 scripts affected, pure bug fix, no new skill). Judge them separately. #1696 is now only the duplicate audit.

Why this one is worth reconsidering

It isn't a workflow skill competing for catalog space. It reduces your triage load, and your own release notes make the case:

  • v1.64.0.0: "82 contributors are named in this release, several of whom independently fixed the same bug within days of each other. That duplication is the tracker telling us how many people hit the same wall."
  • v1.68.0.0: 90 open PRs and 21 stale issues closed with receipts in one day.
  • v1.67.0.0: ~35 issues closed, 25 community PRs landed with credit.

Three waves in five weeks. A real share of that queue was people solving what someone else had already solved. Each one cost a contributor hours and cost you a triage slot. The data that would have prevented it was public and one gh call away the entire time. It just wasn't in anyone's workflow.

That's the gap. Before a branch becomes a PR, /pr-prep walks git log base..HEAD, queries your tracker, and buckets each commit: EXACT_DUP, OVERLAP, SIBLING, CLEAN. EXACT_DUP blocks and prints the colliding PR, its author, and three ways out (close yours and comment on theirs, cherry-pick the unique parts, or --force if already coordinated). OVERLAP and SIBLING don't block; they ride into the PR body as collapsed context.

Wider point: this isn't a gstack problem. Every project with an active tracker and drive-by contributions has it, and no tooling checks. gstack is already what runs at the moment a PR gets opened, which makes it the one place the check actually fires. Cutting duplicate-PR waste across open source looks on-mission for a workflow tool, and nothing else ships it.

Taking your condition literally

You said you'd revisit it "if it becomes part of GStack's core workflow". So: if the blocker is the catalog entry, I'll ship it as /ship Step 1.5 only. The audit runs inline in ship, bin/gstack-pr-prep-score stays as the tested scorer, nothing new appears in the skill list. Zero discovery-budget cost, and it fires exactly where duplicates get created. Say the word and I'll cut that version.

State of the branch

Already landed here:

  • Brought up to v1.68.1.0.
  • Tracker reads flow through bin/gstack-issue-guard per v1.66.1.0. Upstream titles are attacker-authorable and the model judges them for similarity, so they were exactly the ingress the trust envelope exists for. test/tracker-guard-wiring.test.ts caught it.
  • The ship gate was calling bin/gstack-skill, a binary that has never existed in this repo. It now invokes the skill the way gstack actually invokes skills. Real bug, found by your test suite.
  • Bucketing thresholds live in bin/gstack-pr-prep-score as a pure unit-tested function instead of prose, so behaviour and documentation can't drift.

Ready to swap in: a branch cut straight from main at 15 files, +2,532/-10. Skill, scorer, tests, ship gate, doc entry, nothing else. It also closes a gap I found preparing it: Step 7 documented a machine-readable report and the ship gate branched on it, but nothing ever wrote one. It's now written unconditionally in a single jq -n call, at a per-repo path so two ships in different worktrees can't read each other's verdict.

bun run test:free is green on it except three failures that reproduce identically on a pristine main checkout here: path-validation symlink case, touchfiles detectBaseBranch, and the codex-hardening watchdog (flaky ~2 runs in 3 on stock main).

A PR can't be repointed at another branch, so swapping means opening a replacement and closing this one. Tell me which version you want, standalone skill or ship-only, and it'll be up the same day.

One note: I ran the audit against this PR before writing this. It found #1696.

@BenjaminDSmithy

Copy link
Copy Markdown
Contributor Author

Opened #2642 against this PR's surface: /pr-prep searches upstream issues as duplicate candidates, but the unit of audit is always a commit and the gate is always gh pr create, so there is no mode for auditing an issue you are about to file.

It came out of using this skill in anger today. Filing a Claude Code bug report (anthropics/claude-code#88113) surfaced a near-title-match, anthropics/claude-code#66279, that turned out to be a different bug on a closer read, plus four more siblings worth citing. Without that sweep the report would have gone up with no reference to any of them and could reasonably have been closed as a duplicate. The audit had to be run by hand: Step 3's fetch loop with --repo, keywords typed in from the report, and Steps 4.4 through 4.6 skipped since there is no commit to score.

The issue has a proposed shape that reuses Step 3 and bin/gstack-pr-prep-score unchanged rather than forking them.

I would be glad to build it, folded into this PR or as a follow-up. I would rather wait for a steer on this PR first, though: whether you want /pr-prep in gstack at all, and if so what shape it needs to land. Adding issue mode grows the surface further, and I would rather not keep extending a PR that may not be the direction you want. A "no thanks" is a perfectly good answer and costs me nothing.

@BenjaminDSmithy

Copy link
Copy Markdown
Contributor Author

Dogfooded this skill against its own branch and it found a false positive in its own Step 4 bucketing. Fixed in d1cd1eb, now part of this PR.

The ≥3 OPEN issues OVERLAP clause had no score floor, so it counted raw gh full-text hits. Running the audit over this branch's 24 non-merge commits, three chore(build) commits bucketed OVERLAP off a topScore of 0.05. Verbatim, for chore(build): regenerate SKILL.md after v1.68.1.0 merge (keywords regenerate skill merge):

{"bucket":"OVERLAP","topScore":0.05,"openIssueCount":4,"reasons":["OVERLAP: 4 open issues"]}

The four issues it counted were a triggers-frontmatter bug, a review-bias issue, $CLAUDE_CONFIG_DIR support, and Windows Smart App Control. None relate to regenerating a SKILL.md. On any repo with a busy tracker, generic chore/build keywords will pull three unrelated open issues every time, so those commits would have read OVERLAP forever.

It never false-blocked — OVERLAP is informational and only EXACT_DUP refuses — but a bucket that always fires stops being read, which defeats the point of the audit.

The fix adds RELATED_OPEN_ISSUE_FLOOR = 0.15: the clause now counts only open issues that individually clear the floor. It sits below the single-hit threshold (0.3) so a genuinely crowded topic still trips it when no single issue is a strong match, which is the case the clause exists for. An open issue carries state weight 0.7, so 0.15 requires title Jaccard ≥0.214 — real shared tokens rather than one incidental word. openIssueCount keeps its old meaning and a new relatedOpenIssueCount carries what the clause counted, so the JSON stays additive for consumers. Reason strings now name the floor.

Same dogfood pass after the fix: 4 OVERLAP → 1, and the survivor is 50c474ba at topScore 0.3333, tripping the legitimate single-hit threshold rather than the count clause. 23 CLEAN, 0 EXACT_DUP.

Worth flagging that the existing unit test asserted the old behaviour with three issues at Jaccard 0 — it encoded the noise as signal. I retargeted it at genuinely related issues (each scoring 0.2, between the floor and the single-hit threshold) rather than deleting it, and added a regression case built from the four real issue titles above.

To be clear on scope: this is a fix to code already in this PR, not the issue-mode work in #2642. That one is still parked pending your steer on whether /pr-prep is a direction you want at all.

BenjaminDSmithy and others added 11 commits September 21, 2026 05:10
Walks `git log base..HEAD`, derives search keywords per commit from
subject + changed file paths, queries upstream issues + PRs via `gh`,
scores each commit against upstream collisions (EXACT_DUP / OVERLAP /
SIBLING / CLEAN) on a title-token + file-overlap Jaccard, and refuses
to proceed when EXACT_DUP found. Designed to slot into `/ship` as a
Step 0 hook (env `GSTACK_FROM_SHIP=1` switches to JSON output + skips
interactive prompts).

Motivating case (real, 2026-05-26): a contributor branch had 8 commits
ready for upstream PRs; 4 of 4 unverified commits would have duplicated
already-open upstream issues. pr-prep catches all in ~30-60s of `gh`
queries, before any noise PR or reviewer triage round.

v0.1.0 ships inline bash in SKILL.md (reviewable in one file). Out of
scope: diff-content similarity, cross-repo audit, LLM-judged semantic
dup detection, auto-comment on upstream PRs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`/ship` now invokes `/pr-prep --base $BASE_BRANCH --json` with
`GSTACK_FROM_SHIP=1` before any of: merge base branch, run tests,
version bump, push. If pr-prep returns EXACT_DUP (exit 1), ship
aborts with a pinpoint message naming the upstream PR + resolution
paths (close mine / cherry-pick unique parts / coordinate + retry
with `--skip-pr-prep`).

Skip conditions: no upstream remote (solo-repo case), `--skip-pr-prep`
flag, or pr-prep skill not installed (older gstack — stderr warn +
continue). SIBLING / OVERLAP / CLEAN buckets do not block. The JSON
report is written to `/tmp/ship-pr-prep.json` so PR body assembly can
render upstream context as a collapsed section.

Fails fast before any test run wastes time on a branch that duplicates
already-open upstream work.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… per commit

Step 1.4: fetch `CONTRIBUTING.md` (case-insensitive) via gh api from
the upstream repo, cache to /tmp, extract pre-push commands + test
layout conventions + branch naming rules + banned patterns. The agent
uses these inline when writing PR bodies.

Step 4.5: annotate each CLEAN/OVERLAP/SIBLING commit row with the
required pre-push gate (e.g. `bun run verify`), whether changed files
trigger special test paths (eval-replay for retrieval), and whether
the commit added tests. Soft warning on missing-tests when
not-required is unclear — don't block, let the human decide.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mmits

Before filing each CLEAN commit (not bucketed EXACT_DUP / OVERLAP /
SIBLING), invoke `codex review` for an independent second opinion.
Codex CLI uses a different model family (OpenAI vs Claude), so the
signal is genuinely independent and catches structural bugs the author
missed during write-up.

Optional: if `codex` is not on PATH, emit a soft warning and continue;
never block on tool availability. Severity escalation: P0/P1 findings
bump the commit from CLEAN to OVERLAP (don't file until addressed); P2
stays CLEAN at author discretion (fix-before-file or note in PR body).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The new `## Step 1.5` heading in `ship/SKILL.md.tmpl` (the pr-prep
gate) tripped the step-numbering checks in `skill-validation`, which
only permit a closed set of fractional sub-steps. Add `1.5` to
`ALLOWED_SUBSTEPS` so the gate heading is recognised as intentional.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The doc-inventory cross-check requires every skill directory to appear
in both `AGENTS.md` and `docs/skills.md`. Add a `/pr-prep` row to each
so the new skill is documented and the check passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The committed `pr-prep/SKILL.md` was generated against an older
preamble and had drifted from `bun run gen:skill-docs` output.
Regenerate it so it picks up the current shared resolvers: the
`SESSION_KIND` and `GSTACK_PLAN_MODE` preamble lines, the `/spec`
routing entry, the AskUserQuestion failure fallback, the 5+-option
split rule, and the Boil-the-Ocean rename.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add Step 4.6: a per-commit conformance check that holds the branch's
commit messages to the UPSTREAM repo's convention, not the
contributor's own house style. A fork PR whose commits read in a
different voice than the project reads as a drive-by and burns reviewer
goodwill before the diff is read.

The authoritative style is resolved once (Step 1.4): the upstream
CONTRIBUTING.md commit rules if present, else the de-facto shape sampled
from `git log upstream/$BASE_BRANCH --no-merges`, else the
conventional-commits baseline. Step 4.6 then flags subject-shape drift,
a personal body template (emoji/bullets) where upstream uses prose, a
subject that promises content it lacks (e.g. "+ tests" with no tests),
and a missing-or-extra trailer relative to upstream (e.g. a
`Co-Authored-By:` line upstream carries on every commit). Surfaced as a
soft warning in the report, never a block — style is not a duplicate,
but it is the cheapest goodwill win in the audit and far cheaper to fix
before the PR exists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The golden-file regression check pins the generated per-host ship skill
(Claude, Codex, Factory) against committed snapshots. Adding the Step
1.5 pr-prep gate to `ship/SKILL.md.tmpl` changed all three generated
outputs, so the snapshots no longer matched. Regenerate them via
`gen:skill-docs --host all` and re-capture; the diff is exactly the
Step 1.5 block, identical across hosts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The coverage gate (skill-coverage-matrix + skill-coverage-floor)
requires every skill on disk to have a registry entry with at least one
gate-tier test. The new pr-prep skill had none, failing both checks.
Register it with the structural floor test as its gate-tier minimum,
matching how other audit/report skills (qa-only, investigate) are
covered until a behavioral E2E is written.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Step 4 bucketing (title/file Jaccard, state weighting, EXACT_DUP /
OVERLAP / SIBLING / CLEAN precedence) lived only as inline bash in the
skill, so it had no behavioral coverage. Extract it into a pure,
deterministic CLI, `bin/gstack-pr-prep-score`, and pin every bucket
threshold in `test/pr-prep-score.test.ts` (13 cases, free, gate-tier).
The skill's Step 4 now points at the scorer as the canonical
implementation rather than re-deriving the thresholds inline, and the
coverage matrix gates pr-prep on the new behavioral test. This is the
v0.2.0 extraction the skill flagged, scoped to the scoring core.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BenjaminDSmithy and others added 9 commits September 21, 2026 05:14
Measured the {{PREAMBLE}} block that scripts/gen-skill-docs.ts injects into
every generated SKILL.md: 46KB per file. Deliberately made no code change.
Upstream churns the resolvers and the generated output every release (one
delta was 51 files and 1,469 insertions), so a fork-side trim would
conflict on every merge and would be the wrong place to fix it anyway.

- PREAMBLE-BLOAT-NOTES.md records per-tier preamble sizes, the section
  breakdown, and a top-ten offender table.
- The safe path it documents is an upstream change extending the existing
  sections/ carve-out to move roughly 20.5KB of tier-2 and above
  interaction prose behind on-demand loading, worth about 5,100 tokens per
  invocation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The decision-brief preamble never said whether options were mutually
exclusive, so multiSelect was being set on questions whose options were
bundles of the same underlying items. Scoring those per sub-item produces
a brief that cannot be acted on.

- generate-ask-user-format.ts now states that options are mutually
  exclusive by default, and that multiSelect: true is legal only when
  every option is an independently-selectable atom whose pro, con and
  effort stand alone.
- Bundles and combinations (E1+E3, All three, E1 only, Defer all) are
  mutually exclusive by construction, so they take multiSelect: false and
  score Completeness per option letter rather than per sub-item.
- Names the diagnostic tell: a defer/none option or a do-everything option
  in the list proves the question is single-select.
- Routes five or more independent atoms to the existing split chain
  instead of multiSelect, so the four-option cap is never silently
  exceeded.

The rule lives in the preamble resolver rather than in any one skill, so
every skill that renders a decision brief inherits it from one source. The
44 regenerated SKILL.md files are build output.

Verified with bun test over host-config, skill-validation,
audit-compliance, question-log-hook and gstack-question-preference: 387
pass, 0 fail. The three golden-file regressions were already red at HEAD
and are untouched here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every skill description sits in Claude Code's always-loaded prompt, whether
or not the model can invoke that skill. Skills that are only ever reached
by an explicit slash command were paying that cost for nothing, about
1,050 tokens per request here.

The flag lives in each SKILL.md.tmpl; generated SKILL.md files are
regenerated with gen:skill-docs. The claude host uses denylist frontmatter
mode, so the key passes through untouched.

- Flagged (36): benchmark, benchmark-models, canary, careful, codex, cso,
  design-html, design-shotgun, devex-review, diagram, document-generate,
  document-release, freeze, gstack-upgrade, guard, health, ios-clean,
  ios-design-review, ios-fix, ios-qa, ios-sync, landing-report, make-pdf,
  open-gstack-browser (covering the connect-chrome alias), pair-agent,
  plan-devex-review, plan-tune, pr-prep, retro, scrape,
  setup-browser-cookies, setup-deploy, setup-gbrain, skillify, spec,
  unfreeze.
- Left model-invocable on purpose: every skill named in a CLAUDE.md
  skill-routing table, being autoplan, browse, context-restore,
  context-save, design-consultation, design-review, investigate,
  land-and-deploy, learn, office-hours, plan-ceo-review,
  plan-design-review, plan-eng-review, qa, qa-only, review, ship and
  sync-gbrain, plus the _gstack-command router.
- Tier-1 suites green (733 pass, 0 fail). The browse/test/sidebar-ux.test.ts
  failures reproduce with this change stashed, so they are pre-existing and
  need a live browse daemon.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Upstream issue and PR titles are attacker-authorable text, and the audit
asks a model to judge them for similarity. They were landing in context
raw, which is exactly the ingress the v1.66.1.0 trust envelope exists
for. test/tracker-guard-wiring.test.ts flagged both gh issue list reads.

- Step 3's four fetches now tee raw JSON to a mktemp -d scratch dir as
  mechanical scorer input and pipe the human-readable line through
  bin/gstack-issue-guard. The scanner does not match gh pr list, but
  those are the same class of ingress, so all four are enveloped.
- Documents the envelope as a health signal: "(empty body)" means zero
  matches, while no envelope at all means the pipeline failed and must
  not clear a commit.
- Step 7 now names the report path and its shape, {summary, worst,
  commits[]}, with worst as the highest-severity bucket across all
  commits. That is the field /ship's Step 1.5 gate branches on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Step 1.5 shelled out to ~/.claude/skills/gstack/bin/gstack-skill, a
binary that has never existed in this tree or upstream, so the gate
would have failed at runtime on every ship.
test/setup-claude-skill-assets.test.ts was failing on that dangling
reference.

Replaced with the real invocation: a shell precondition probe, an inline
read-and-follow of pr-prep/SKILL.md, then a shell gate over the JSON
report.

- The probe prints PR_PREP_GATE: run|skip and clears any stale report
  first, so a leftover from an earlier ship cannot decide this one.
- The gate reads .worst via jq, defaulting to CLEAN. A missing report
  warns to stderr and continues rather than hard-failing ship on an
  audit that never ran.
- GSTACK_FROM_SHIP becomes a behavioural contract (suppress pr-prep's
  own confirmations, ship owns the decision) instead of an env var
  handed to a process that never ran.
- Step 19's PR-body read of the report is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The ">=3 OPEN issues" clause in the Step 4 bucketing had no score floor, so
it counted raw gh full-text hits. Dogfooding the audit over this branch's 24
non-merge commits, three chore(build) commits bucketed OVERLAP off a topScore
of 0.05: keywords like "regenerate skill merge" pull three unrelated open
issues on any repo with a busy tracker, so those commits would have read
OVERLAP forever. It never false-blocked, since OVERLAP is informational and
only EXACT_DUP refuses, but a bucket that always fires stops being read.

Add RELATED_OPEN_ISSUE_FLOOR = 0.15 and count only open issues that clear it.
The floor sits below the single-hit OVERLAP threshold (0.3) so a genuinely
crowded topic still trips the clause when no single issue is a strong match,
which is the case the clause exists for. An open issue carries state weight
0.7, so 0.15 requires title Jaccard >=0.214, a real shared-token overlap
rather than one incidental word.

openIssueCount keeps its old meaning and a new relatedOpenIssueCount carries
what the clause counted, so the JSON output stays additive for consumers.
Reason strings now name the floor.

The existing unit test asserted the old behaviour with three issues at
Jaccard 0, encoding the noise as signal. Retarget it at genuinely related
issues, each scoring 0.2, and add a regression case built from the four real
issue titles that caused the false positive.

Same dogfood pass after the fix: 4 OVERLAP to 1, and the survivor trips the
legitimate single-hit threshold rather than the count clause.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Regenerated with `bun run gen:skill-docs` after rebasing onto
upstream/main (v1.87.4.0). Replaces four stale `chore(build)`
checkpoint commits that carried generated output from merges of
older upstream snapshots; those conflicted wholesale against the
current base and were dropped rather than hand-merged.

Also dropped: the commit registering /pr-prep in
scripts/proactive-suggestions.json, since upstream retired that
registry in the v1.64 waves and deleted the file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The branch carried two ceiling commits pinned against an older
upstream. Neither side's numbers survive a rebase onto v1.87.4.0, so
both were dropped and every ceiling is re-pinned from an actual
`bun test test/parity-suite.test.ts` run against this tree.

Attribution checked rather than assumed: every skill's upstream/main
size sits under its existing ceiling (cso 17,986 < 18,000; review
60,940 < 61,500; ship 77,680 < 79,300), so the overage is the fork's
own — +805B of single-select AskUserQuestion doctrine in each
affected preamble, and +3,421B in ship for that plus the Step 1.5
pr-prep gate. Each new ceiling records its measured figure.

Result: 21/21 skills pass parity vs the v1.64.1.0 baseline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both ratchets failed on the rebased branch, and both for the reason
their protocol names as legitimate growth: this branch adds a skill
and a shared preamble paragraph.

catalog-budget: 1,161 token-equivalents against a 1,150 ceiling.
Upstream sat one token under it at 53 skills; /pr-prep is the 54th.
Ceiling raised to 1,200 with the derivation re-measured in place,
keeping roughly the ~4% headroom the previous derivation kept.

context-budget: re-captured with `bun test/helpers/capture-context-budget.ts`
as the ratchet protocol prescribes. alwaysOnTotal was 6,453 against
6,397; health's eager ledger 10,891 against 10,805; pr-prep had no
eager ceiling. Every eager ceiling moves because the single-select
AskUserQuestion paragraph lands in each skill's shared preamble
(about +202 tokens where the full preamble is carried, +8 on small
skills). Ceilings are the tool's own actual x1.05 / x1.10.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

2 participants