* feat(#3841): assert gsd-tools identity before any state-mutating verb only this package publishes. The path-based branches — a project-local install, a runtime config directory — had no such guarantee; they trusted their configured location. This closes them. Mechanism: once resolution finishes, and before any verb runs, the preamble probes the tool it picked with `runtime-identity --raw` and matches the answer with a shell `case` pattern ANCHORED to the start of the compact payload (`{"packageName":"@opengsd/gsd-core"`). An unanchored substring match accepts the decoy `{"packageName":"get-shit-done-cc","note":"@opengsd/gsd-core"}`, which any colliding package could publish. The outcome is exported as the two-valued `GSD_IDENTITY_STATUS` (`ok`/`unverified`), so the gate is asserted on a VALUE rather than on warning prose. Rollout is warn-then-fail per the #3146 ruling: `unverified` prints one line naming BOTH causes and continues, because `no_identity_verb` cannot tell a foreign package from an `@opengsd/gsd-core` older than the verb, and at rollout the old-version case is the common one. The blocker was byte budget, not design. The preamble is inlined into 112 shipped files and several sat within single-digit bytes of frozen ceilings (`gsd-verifier.md` 16 bytes, `gsd-executor.md` 33, `execute-phase.md` 234); a first attempt broke five of them. What made room was collapsing the resolver's twenty near-identical `elif [ -f … ]` arms into one candidate-list helper (`_gsd_at`), which buys far more than the assertion costs. The preamble is now 2,624 bytes against 4,500 — a net 1,876 bytes SMALLER per inlined file, so every capped file moved away from its ceiling rather than toward it. No cap raised, no size-budget exception added, no override token emitted. Resolution order, every runtime-home probe, the `unset -f gsd_run` re-source fix, the fail-closed `exit 1`, and the `CLAUDE_ENV_FILE` persistence are all preserved byte-for-byte in substring terms; the snippet still begins with `_GSD_SHIM_NAME=` and still ends with `fi`, which the parity extractors anchor on. `gsd-core/references/gsd-run-resolver.md` is re-synced byte-equal. Also fixes two stale claims found in passing: CONTEXT.md and FEATURES.md both described an `[ -x ]` guard as the load-bearing re-source defense. That guard was tried and REMOVED in #3831 — it rejected the bare function name, fell through every branch, and hit `exit 1`, which kills a sourced caller's shell. `unset -f gsd_run` is the actual mechanism. Refs #3841 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(#3841): pair the anchor's brace by requiring a closed identity payload The matrix went red on `tests/new-project-mvp-prompt.test.cjs` — "new-project.md has unbalanced braces: net depth 2" — plus a knock-on report from its parent `bug #1516` describe, which is the same failure counted once at the child and once at the block. Root cause: that guard (:182-189, mirroring #3784 bd53925f) walks characters and increments on `{`, decrements on `}`, with no awareness of shell quoting. It scans `new-project.md` PLUS every `new-project/steps/*.md`, and both `new-project.md` and `steps/auto-mode-config.md` carry one inlined preamble copy — hence net 2 from a snippet that was off by exactly one. The unpaired brace was the `{` inside the single-quoted `case` pattern of the identity anchor, which is correct shell and invisible to a text scanner. Fix in the snippet, not the guard. The pattern now anchors at BOTH ends: `'{"packageName":"@opengsd/gsd-core"'*'}'`. That balances 51/51 with a brace that does real work rather than a cosmetic pair — a truncated payload whose prefix matches now fails too, where before it verified. Safe for any future additive field: a JSON object's own closing brace is always the last character, whatever type the last value has, which is pinned by two negative-space tests (a nested object and an array-valued last key must both still verify). Cost: +3 bytes, against the 1,873 the resolver fold already gave back. The alternative considered and rejected was dropping the literal `{` for a `?` glob. It balances too, but weakens the anchor from "must be an opening brace" to "must be any one character", and the anchor is the entire point. Two guards added so this cannot recur silently: - runtime-launcher-parity (F0) pins brace balance at the SNIPPET, so the next edit to that pattern fails on the file it broke instead of surfacing three files downstream in a test whose name mentions neither the launcher nor this issue. It also asserts depth never goes negative, since a `}` preceding its `{` nets to zero while being unbalanced at every prefix. - runtime-identity gains behavioral truncated-payload and trailing-garbage fixtures, so the added `}` is proven load-bearing rather than merely present. Verified: snippet 51/51 braces; new-project combined net depth 0; the seven other preamble-bearing files with nonzero depth are unchanged from merged next (their own prose, not the preamble, and not in any guard's scan set); all 112 inlined copies and the resolver reference re-synced byte-equal; sync:launcher idempotent on the second run. Refs #3841 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(#3841): backfill changeset PR number Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: sim <sim@local> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
17 KiB
<required_reading> Before starting, read these project files to understand the review criteria:
.github/ISSUE_TEMPLATE/feature_request.yml— required fields for feature issues.github/ISSUE_TEMPLATE/enhancement.yml— required fields for enhancement issues.github/ISSUE_TEMPLATE/chore.yml— required fields for chore issues.github/ISSUE_TEMPLATE/bug_report.yml— required fields for bug reports.github/PULL_REQUEST_TEMPLATE/feature.md— required checklist for feature PRs.github/PULL_REQUEST_TEMPLATE/enhancement.md— required checklist for enhancement PRs.github/PULL_REQUEST_TEMPLATE/fix.md— required checklist for fix PRsCONTRIBUTING.md— the issue-first rule and approval gates </required_reading>
If response_language is set: All user-facing questions, prompts, and explanations in this workflow MUST be presented in {response_language}. Technical terms, code, file paths, and subagent prompts stay in English — only user-facing output is translated.
-
ghCLI available and authenticated?which gh && gh auth status 2>&1If not available: print setup instructions and exit.
-
Detect repository: If
--repoflag provided, use that. Otherwise:gh repo view --json nameWithOwner -q '.nameWithOwner' 2>/dev/nullIf no repo detected: error — must be in a git repo with a GitHub remote.
-
Parse flags:
--issues→ set REVIEW_ISSUES=true, REVIEW_PRS=false--prs→ set REVIEW_ISSUES=false, REVIEW_PRS=true--label→ set AUTO_LABEL=true--close-incomplete→ set AUTO_CLOSE=true- Default (no flags): review both issues and PRs, report only (no auto-actions)
Fetch all open issues:
gh issue list --state open --json number,title,labels,body,author,createdAt,updatedAt --limit 100
For each issue, classify by labels and body content:
| Label/Pattern | Type | Template |
|---|---|---|
feature-request |
Feature | feature_request.yml |
enhancement |
Enhancement | enhancement.yml |
bug |
Bug | bug_report.yml |
type: chore |
Chore | chore.yml |
| No matching label | Unknown | Flag for manual triage |
If an issue has no type label, attempt to classify from the body content:
- Contains "### Feature name" → likely Feature
- Contains "### What existing feature" → likely Enhancement
- Contains "### What happened?" → likely Bug
- Contains "### What is the maintenance task?" → likely Chore
- Cannot determine → mark as
needs-triage
For each classified issue, review against its template requirements.
Feature Request Review Checklist:
- Pre-submission checklist present (4 checkboxes)
- Feature name provided
- Type of addition selected
- Problem statement filled (not placeholder text)
- What is being added described with examples
- Full scope of changes listed (files created/modified/systems)
- User stories present (minimum 2)
- Acceptance criteria present (testable conditions)
- Applicable runtimes selected
- Breaking changes assessment present
- Maintenance burden described
- Alternatives considered (not empty)
- Label check: Has
needs-reviewlabel? Hasapproved-featurelabel? - Gate check: If PR exists linking this issue, does issue have
approved-feature?
Enhancement Review Checklist:
- Pre-submission checklist present (4 checkboxes)
- What is being improved identified
- Current behavior described with examples
- Proposed behavior described with examples
- Reason and benefit articulated (not vague)
- Scope of changes listed
- Breaking changes assessed
- Alternatives considered
- Area affected selected
- Label check: Has
needs-reviewlabel? Hasapproved-enhancementlabel? - Gate check: If PR exists linking this issue, does issue have
approved-enhancement?
Bug Report Review Checklist:
- GSD Version provided
- Runtime selected
- OS selected
- Node.js version provided
- Description of what happened
- Expected behavior described
- Steps to reproduce provided
- Frequency selected
- Severity/impact selected
- PII checklist confirmed
- Label check: Has
needs-triageorconfirmed-buglabel?
Chore Review Checklist:
- Pre-submission checklist confirmed (no user-facing changes)
- Maintenance task described
- Type of maintenance selected
- Current state described with specifics
- Proposed work listed
- Acceptance criteria present
- Area affected selected
- Label check: Has
needs-triagelabel?
Scoring: For each issue, calculate a completeness percentage:
- Count required fields present vs. total required fields
- Score = (present / total) * 100
- Status: COMPLETE (100%), MOSTLY COMPLETE (75-99%), INCOMPLETE (50-74%), REJECT (<50%)
Fetch all open PRs:
gh pr list --state open --json number,title,labels,body,author,headRefName,baseRefName,isDraft,createdAt,reviewDecision,statusCheckRollup --limit 100
For each PR, classify by body content and linked issue:
| Body Pattern | Type | Template |
|---|---|---|
| Contains "## Feature PR" or "## Feature summary" | Feature PR | feature.md |
| Contains "## Enhancement PR" or "## What this enhancement improves" | Enhancement PR | enhancement.md |
| Contains "## Fix PR" or "## What was broken" | Fix PR | fix.md |
| Uses default template | Wrong Template | Flag — must use typed template |
| Cannot determine | Unknown | Flag for manual review |
Also check for linked issues:
gh pr view {number} --json body -q '.body' | grep -oE '(Closes|Fixes|Resolves) #[0-9]+'
For each classified PR, review against its template requirements.
Feature PR Review Checklist:
- Uses feature PR template (not default)
- Issue linked with
Closes #NNN - Linked issue exists and has
approved-featurelabel - Feature summary present
- New files table filled
- Modified files table filled
- Implementation notes present
- Spec compliance checklist present (acceptance criteria from issue)
- Test coverage described
- Platforms tested checked (macOS, Windows, Linux)
- Runtimes tested checked
- Scope confirmation checked
- Full checklist completed
- Breaking changes section filled
- CI check: All status checks passing?
- Review check: Has review approval?
Enhancement PR Review Checklist:
- Uses enhancement PR template (not default)
- Issue linked with
Closes #NNN - Linked issue exists and has
approved-enhancementlabel - What is improved described
- Before/after provided
- Implementation approach described
- Verification method described
- Platforms tested checked
- Runtimes tested checked
- Scope confirmation checked
- Full checklist completed
- Breaking changes section filled
- CI check: All status checks passing?
Fix PR Review Checklist:
- Uses fix PR template (not default)
- Issue linked with
Fixes #NNN - Linked issue exists and has
confirmed-buglabel - What was broken described
- What the fix does described
- Root cause explained
- Verification method described
- Regression test added (or explained why not)
- Platforms tested checked
- Runtimes tested checked
- Full checklist completed
- Breaking changes section filled
- CI check: All status checks passing?
Cross-cutting PR Checks (all types):
- PR title is descriptive (not just "fix" or "update")
- One concern per PR (not mixing fix + enhancement)
- No unrelated formatting changes visible in diff
.changeset/*.mdfragment added for user-facing changes (orno-changeloglabel applied)- Not using
--no-verifyor skipping hooks
Scoring: Same as issues — completeness percentage per PR.
Cross-reference issues and PRs to enforce the issue-first rule:For each open PR:
- Extract linked issue number from body
- If no linked issue: GATE VIOLATION — PR has no issue
- If linked issue exists, check its labels:
- Feature PR → issue must have
approved-feature - Enhancement PR → issue must have
approved-enhancement - Fix PR → issue must have
confirmed-bug
- Feature PR → issue must have
- If label is missing: GATE VIOLATION — PR opened before approval
Report gate violations prominently — these are the most important findings because the project auto-closes PRs without proper approval gates.
Produce a structured triage report:===================================================================
GSD INBOX TRIAGE — {repo} — {date}
===================================================================
SUMMARY
-------
Open issues: {count} Open PRs: {count}
Features: {n} Feature PRs: {n}
Enhancements:{n} Enhancement PRs: {n}
Bugs: {n} Fix PRs: {n}
Chores: {n} Wrong template: {n}
Unclassified:{n} No linked issue: {n}
GATE VIOLATIONS (action required)
---------------------------------
{For each violation:}
PR #{number}: {title}
Problem: {description — e.g., "No approved-feature label on linked issue #45"}
Action: {what to do — e.g., "Close PR or approve issue #45 first"}
ISSUES NEEDING ATTENTION
------------------------
{For each issue sorted by completeness score, lowest first:}
#{number} [{type}] {title}
Score: {percentage}% complete
Missing: {list of missing required fields}
Labels: {current labels} → Suggested: {recommended labels}
Age: {days since created}
PRS NEEDING ATTENTION
---------------------
{For each PR sorted by completeness score, lowest first:}
#{number} [{type}] {title}
Score: {percentage}% complete
Missing: {list of missing checklist items}
CI: {passing/failing/pending}
Review: {approved/changes_requested/none}
Linked issue: #{issue_number} ({issue_status})
Age: {days since created}
READY TO MERGE
--------------
{PRs that are 100% complete, CI passing, approved:}
#{number} {title} — ready
STALE ITEMS (>30 days, no activity)
------------------------------------
{Issues and PRs with no updates in 30+ days}
===================================================================
Write this report to .planning/INBOX-TRIAGE.md if a .planning/ directory exists,
otherwise print to console only.
If --label: For each issue/PR where labels are missing or incorrect:
gh issue edit {number} --add-label "{label}"
Or:
gh pr edit {number} --add-label "{label}"
Label recommendations:
- Unclassified issues → add
needs-triage - Feature issues without review → add
needs-review - Enhancement issues without review → add
needs-review - Bug reports without triage → add
needs-triage - PRs with gate violations → add
gate-violation
If --close-incomplete: For issues scoring below 50% completeness:
gh issue close {number} --comment "Closed by GSD inbox triage: this issue is missing required fields per the issue template. Missing: {list}. Please reopen with a complete submission. See CONTRIBUTING.md for requirements."
For PRs with gate violations:
gh pr close {number} --comment "Closed by GSD inbox triage: this PR does not meet the issue-first requirement. {specific violation}. See CONTRIBUTING.md for the correct process."
Always confirm with the user before closing anything:
Text mode (workflow.text_mode: true in config or --text flag): Set TEXT_MODE=true if --text is present in $ARGUMENTS OR text_mode from init JSON is true. When TEXT_MODE is active, replace every AskUserQuestion call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where AskUserQuestion is not available.
AskUserQuestion:
question: "Found {N} items to close. Review the list above — proceed with closing?"
options:
- label: "Close all"
description: "Close all {N} non-compliant items with explanation comments"
- label: "Let me pick"
description: "I'll choose which ones to close"
- label: "Skip"
description: "Don't close anything — report only"
Inbox Triage Complete
Reviewed: {issue_count} issues, {pr_count} PRs Gate violations: {violation_count} Ready to merge: {ready_count} Needing attention: {attention_count} Stale (30+ days): {stale_count} {If report saved: "Report saved to .planning/INBOX-TRIAGE.md"}
Next steps:
- Review gate violations first — these block the contribution pipeline
- Address incomplete submissions (comment or close)
- Merge ready PRs
- Triage unclassified issues
</step>
</process>
<offer_next>
After triage:
- /gsd:review — Run cross-AI peer review on a specific phase plan
- /gsd:ship — Create a PR from completed work
- /gsd:progress — See overall project state
- /gsd:inbox --label — Re-run with auto-labeling enabled
</offer_next>
<success_criteria>
- [ ] All open issues fetched and classified by type
- [ ] Each issue reviewed against its template requirements
- [ ] All open PRs fetched and classified by type
- [ ] Each PR reviewed against its template checklist
- [ ] Issue-first gate violations identified
- [ ] Structured report generated with scores and action items
- [ ] Auto-actions executed only when flagged and user-confirmed
</success_criteria>