Files
msd-core/gsd-core/workflows/inbox.md
Tom Boucher 63abcface9 feat(#3146): resolve gsd_run so workflows cannot reach a foreign gsd-tools (#3831)
* feat(#3146): resolve gsd_run so workflows cannot reach a foreign gsd-tools

The predecessor package get-shit-done-cc publishes a colliding gsd-tools bin whose phases.clear DELETES where this package's ARCHIVES, and both print success-shaped output against a gitignored .planning/ -- which is how #3129 cost a user 43 phase directories with no error and nothing recoverable from git.

The launcher's PATH branch now resolves gsd_run, published only by this package and self-locating via its own symlink chain to the sibling shim, instead of the colliding gsd-tools. A foreign handler becomes unreachable from PATH, and when no gsd_run is reachable the resolver fails closed rather than falling back -- that fallback was the vulnerability. This is smaller than the branch it replaces, which matters: the preamble is inlined into 113 shipped files and agents/gsd-verifier.md sits 2 bytes under a red-line size cap.

unset -f gsd_run leads the preamble so a re-source is idempotent. Without it, command -v finds the shell function, returns a bare name, and the resolver falls through to an exit 1 that kills a sourced caller's shell.

Adds gsd-tools runtime-identity, a manual diagnostic reporting this runtime's package coordinates over the baked package-identity (#498) and readHostVersion, with a strict total classifier: only a JSON object with an exact packageName verifies, since JSON.parse admits 0/"str"/[]/null/true.

An inlined identity assertion was built and reviewed first, then withdrawn -- it breaks five frozen size ceilings and no assertion fits in 2 bytes.

Closes #3146

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(#3146): stop sync:launcher relocating a deliberate preamble placement

Pre-existing defect, surfaced by this PR because sync is a no-op unless the snippet content actually changes. transformFile inserts the preamble into the first block that CALLS gsd_run, but gsd-core/workflows/explore.md deliberately places it in a bootstrap-only block that DEFINES gsd_run without calling it -- its own comment explains why: declining the research offer must not leave Step 5's commit call unbootstrapped. Stripping empties that block of calls, so the preamble migrated forward and broke the define-before-use invariant tests/explore-command.test.cjs pins.

Reproduced on a pristine origin/next checkout with the base snippet and base file, so this was not introduced here. The insertion target now honours a block that already carried the preamble, falling back to the first calling block for files that have none yet. Adds a behavioral regression test over a two-block fixture.

Also updates three runtime-launcher-parity tests that pinned the removed PATH fallback to gsd-tools. Their intent is preserved -- the PATH stub is renamed gsd_run so it is reachable by the new resolver, and the RUNTIME_DIR-wins test still asserts the stub is never invoked. Fixture shebangs move to an absolute /bin/sh, because the fixture PATH is deliberately restricted and #!/usr/bin/env sh could not resolve.

Refs #3146

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore(#3146): backfill changeset PR number

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(#3146): document the FEATURES.md section-numbering practice

The monotonically increasing section number in docs/FEATURES.md is the most frequent merge-conflict source in this repo, and it has TWO conflict cells, not one: the ### N. heading and the hand-maintained table of contents. Two PRs adding differently numbered features still collide on the TOC, so renumbering alone does not make a branch safe. This branch alone was renumbered 165 -> 166 -> 167 -> 168 across successive rebases.

Adds a CONTRIBUTING section stating the practice: allocate the number last, never pre-emptively renumber, take max+1 after a rebase and update the TOC in the same commit, and never renumber someone else's section. Fork contributors are told explicitly they may leave the number to a maintainer at merge rather than chasing the counter. Agents are told to lease the allocation and to include the file in their published touched set.

Records the durable fix as planned rather than pretending it exists: FEATURES.md should be generated from per-feature fragments the way CHANGELOG.md is generated from .changeset/, and the way tests/emitted-drift-acks/ works (#2914).

Also renumbers this branch's own section to 168, leaving 167 to the PR already in flight.

Refs #3146

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: sim <sim@local>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 20:57:16 -04:00

18 KiB

Triage and review all open GitHub issues and PRs against project contribution templates. Produces a structured report showing compliance status for each item, flags missing required fields, identifies label gaps, and optionally takes action (label, comment, close).

<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 PRs
  • CONTRIBUTING.md — the issue-first rule and approval gates </required_reading>
```bash _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif unset -f gsd_run; _G="$(command -v gsd_run)"; then GSD_TOOLS="$_G"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd_run is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi RESPONSE_LANGUAGE=$(gsd_run query config-get response_language --default "" 2>/dev/null || echo "") ```

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.

Verify prerequisites:
  1. gh CLI available and authenticated?

    which gh && gh auth status 2>&1
    

    If not available: print setup instructions and exit.

  2. Detect repository: If --repo flag provided, use that. Otherwise:

    gh repo view --json nameWithOwner -q '.nameWithOwner' 2>/dev/null
    

    If no repo detected: error — must be in a git repo with a GitHub remote.

  3. 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)
Skip if REVIEW_ISSUES=false.

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
Skip if REVIEW_ISSUES=false.

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-review label? Has approved-feature label?
  • 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-review label? Has approved-enhancement label?
  • 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-triage or confirmed-bug label?

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-triage label?

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%)
Skip if REVIEW_PRS=false.

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]+'
Skip if REVIEW_PRS=false.

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-feature label
  • 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-enhancement label
  • 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-bug label
  • 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/*.md fragment added for user-facing changes (or no-changelog label applied)
  • Not using --no-verify or 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:

  1. Extract linked issue number from body
  2. If no linked issue: GATE VIOLATION — PR has no issue
  3. 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
  4. 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.

Only execute if `--label` or `--close-incomplete` flags were set.

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>