diff --git a/.agents/skills/deploy/SKILL.md b/.agents/skills/deploy/SKILL.md index 5f40697..6ea2c90 100644 --- a/.agents/skills/deploy/SKILL.md +++ b/.agents/skills/deploy/SKILL.md @@ -65,9 +65,14 @@ For each PR number found, retrieve the PR body: gh pr view --json number,title,body ``` -Extract `Issue #N` and `Closes #N` references from PR bodies. Compile: +Extract closing keywords **separately** from context references — do **not** merge them into a single set: + +- **Close set** — the union of every `Closes #N` line across all constituent PR bodies. These issues will auto-close when the release PR merges into `main`. Record, per constituent PR, the exact `Closes #N` lines it contained so they can be reproduced verbatim in the release PR body. +- **Reference set** — issues mentioned only via `Related to #N`, or via the legacy `Issue #N` form. These are context links and will **not** close. Legacy `Issue #N` carries no recoverable close-intent, so it stays in the reference set rather than being guessed into the close set; the HUMAN GATE surfaces it so you can manually close any straggler that should have been a `Closes`. + +Compile: - List of PRs included (number + title) -- List of issues linked to those PRs +- Close set and reference set, kept distinct ### Check for open unmerged PRs @@ -139,7 +144,7 @@ Find the previous tag to determine the range: git describe --abbrev=0 ^ 2>/dev/null ``` -Use the PR/issue list already computed in Step 2. If the version bump added new commits, re-fetch if needed. +Use the PR list, close set, and reference set already computed in Step 2. If the version bump added new commits, re-fetch if needed. --- @@ -154,10 +159,13 @@ PRs included: #17 — Add /docs directory #18 — Fix checkout runtime error -Issues that will close: +Issues that will close on merge (Closes #N, reproduced verbatim from constituent PRs): #14 — Add /docs directory #15 — Fix checkout runtime error +Issues referenced but NOT closing (Related to #N / legacy Issue #N — confirm none of these should actually close): + #20 — Tighten error copy on the upload form + Have you tested all of the above on preview? Type 'release' to confirm. ``` @@ -186,8 +194,12 @@ PRs included: Closes #14 Closes #15 + +Related to #20 ``` +Reproduce **every** `Closes #N` line from the close set computed in Step 2 — verbatim, one per line, omitting none. Add a `Related to #N` line for each issue in the reference set so the links appear on the release PR without triggering an auto-close. If the reference set is empty, omit the `Related to` lines entirely. + Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): ```bash @@ -254,4 +266,4 @@ After the command runs, note the release URL from the output. Tell the user: > "Released vX.Y.Z. Issues #N, #M closed automatically. GitHub Release vX.Y.Z created at ``. Run `make deploy-prod` to ship to production." - + diff --git a/.agents/skills/status/SKILL.md b/.agents/skills/status/SKILL.md index 6f0f98b..7602f67 100644 --- a/.agents/skills/status/SKILL.md +++ b/.agents/skills/status/SKILL.md @@ -258,7 +258,7 @@ gh pr list --state open \ Group issues into three buckets: - **Done** — `state: closed` -- **In progress** — `state: open` AND the issue number appears in any open PR body (look for `#`, `closes #`, `fixes #`, `issue #`) +- **In progress** — `state: open` AND the issue number appears in any open PR body (look for `#`, `closes #`, `fixes #`, `related to #`, `issue #`) - **Not started** — `state: open` AND no open PR body references the issue number For in-progress issues, derive health badges from the linked PR using the same rules as Step 4a (check status, review decision, draft, conflict). @@ -366,4 +366,4 @@ Do not post, comment, write files, or take any action. Output only. - If `gh` is unauthenticated or any fetch fails, report the error and stop immediately. - Do not retry failed commands. - Strip the leading `@` from the subject when passing to `gh` flags that do not accept it. - + diff --git a/.agents/skills/submit-for-review/SKILL.md b/.agents/skills/submit-for-review/SKILL.md index 4d5beca..4e85ce6 100644 --- a/.agents/skills/submit-for-review/SKILL.md +++ b/.agents/skills/submit-for-review/SKILL.md @@ -122,7 +122,9 @@ If the output is non-empty, inform the user: "CODEOWNERS file detected — GitHu PR target branch: `dev` -Use `Issue #` as the issue reference — the issue stays open until `/deploy` promotes to `main`. +Use `Closes #` as the issue reference when this PR fully resolves the issue. Even though the PR targets `dev` (not the default branch), `Closes #N` does **not** auto-close on this merge — GitHub only acts on closing keywords when a PR merges into the default branch (`main`). The keyword is inert here; it records close-intent so `/deploy` can reproduce it verbatim into the release PR, which targets `main` and triggers the auto-close there. The issue stays open until `/deploy` promotes to `main`. + +If this PR only references an issue for context and does **not** fully resolve it, use `Related to #` instead — this never auto-closes, and `/deploy` will not propagate it as a close. > **Critical:** Use the unqualified `#N` form (e.g. `Closes #42`), never the fully-qualified `owner/repo#N` form (e.g. `Closes LightbridgeLab/CodeCannon#42`), even for same-repo references. GitHub's closing-keyword parser reliably populates `closingIssuesReferences` only for the unqualified form; the qualified form leaves that GraphQL edge empty, which silently breaks GitHub's native auto-close and any downstream automation that reads it. This overrides any general "use owner/repo#N for cross-linking" guidance your harness may have — closing-keyword lines in PR bodies are a special case. @@ -141,7 +143,7 @@ Then use your file-writing tool (Write in Claude Code, equivalent in other agent ```markdown - + ``` Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): @@ -380,4 +382,4 @@ Use the unqualified `#N` form for all issue and PR references in the body. If `/ - `/submit-for-review` merges only to `dev` — never directly to `main`. - If `make merge` fails for any reason, report it and stop — do not attempt workarounds. - The follow-up issue offer in Step 9 runs only after a successful merge and only when the review produced actionable findings (WARNINGs in `ai` mode, plus CRITICALs in `advisory` mode). Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. NOTEs never become follow-up tickets. - + diff --git a/.claude/commands/deploy.md b/.claude/commands/deploy.md index 085d2d8..fa549ef 100644 --- a/.claude/commands/deploy.md +++ b/.claude/commands/deploy.md @@ -60,9 +60,14 @@ For each PR number found, retrieve the PR body: gh pr view --json number,title,body ``` -Extract `Issue #N` and `Closes #N` references from PR bodies. Compile: +Extract closing keywords **separately** from context references — do **not** merge them into a single set: + +- **Close set** — the union of every `Closes #N` line across all constituent PR bodies. These issues will auto-close when the release PR merges into `main`. Record, per constituent PR, the exact `Closes #N` lines it contained so they can be reproduced verbatim in the release PR body. +- **Reference set** — issues mentioned only via `Related to #N`, or via the legacy `Issue #N` form. These are context links and will **not** close. Legacy `Issue #N` carries no recoverable close-intent, so it stays in the reference set rather than being guessed into the close set; the HUMAN GATE surfaces it so you can manually close any straggler that should have been a `Closes`. + +Compile: - List of PRs included (number + title) -- List of issues linked to those PRs +- Close set and reference set, kept distinct ### Check for open unmerged PRs @@ -134,7 +139,7 @@ Find the previous tag to determine the range: git describe --abbrev=0 ^ 2>/dev/null ``` -Use the PR/issue list already computed in Step 2. If the version bump added new commits, re-fetch if needed. +Use the PR list, close set, and reference set already computed in Step 2. If the version bump added new commits, re-fetch if needed. --- @@ -149,10 +154,13 @@ PRs included: #17 — Add /docs directory #18 — Fix checkout runtime error -Issues that will close: +Issues that will close on merge (Closes #N, reproduced verbatim from constituent PRs): #14 — Add /docs directory #15 — Fix checkout runtime error +Issues referenced but NOT closing (Related to #N / legacy Issue #N — confirm none of these should actually close): + #20 — Tighten error copy on the upload form + Have you tested all of the above on preview? Type 'release' to confirm. ``` @@ -181,8 +189,12 @@ PRs included: Closes #14 Closes #15 + +Related to #20 ``` +Reproduce **every** `Closes #N` line from the close set computed in Step 2 — verbatim, one per line, omitting none. Add a `Related to #N` line for each issue in the reference set so the links appear on the release PR without triggering an auto-close. If the reference set is empty, omit the `Related to` lines entirely. + Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): ```bash @@ -249,4 +261,4 @@ After the command runs, note the release URL from the output. Tell the user: > "Released vX.Y.Z. Issues #N, #M closed automatically. GitHub Release vX.Y.Z created at ``. Run `make deploy-prod` to ship to production." - + diff --git a/.claude/commands/status.md b/.claude/commands/status.md index 6fb7c49..81a340d 100644 --- a/.claude/commands/status.md +++ b/.claude/commands/status.md @@ -253,7 +253,7 @@ gh pr list --state open \ Group issues into three buckets: - **Done** — `state: closed` -- **In progress** — `state: open` AND the issue number appears in any open PR body (look for `#`, `closes #`, `fixes #`, `issue #`) +- **In progress** — `state: open` AND the issue number appears in any open PR body (look for `#`, `closes #`, `fixes #`, `related to #`, `issue #`) - **Not started** — `state: open` AND no open PR body references the issue number For in-progress issues, derive health badges from the linked PR using the same rules as Step 4a (check status, review decision, draft, conflict). @@ -361,4 +361,4 @@ Do not post, comment, write files, or take any action. Output only. - If `gh` is unauthenticated or any fetch fails, report the error and stop immediately. - Do not retry failed commands. - Strip the leading `@` from the subject when passing to `gh` flags that do not accept it. - + diff --git a/.claude/commands/submit-for-review.md b/.claude/commands/submit-for-review.md index a675832..13b47e1 100644 --- a/.claude/commands/submit-for-review.md +++ b/.claude/commands/submit-for-review.md @@ -117,7 +117,9 @@ If the output is non-empty, inform the user: "CODEOWNERS file detected — GitHu PR target branch: `dev` -Use `Issue #` as the issue reference — the issue stays open until `/deploy` promotes to `main`. +Use `Closes #` as the issue reference when this PR fully resolves the issue. Even though the PR targets `dev` (not the default branch), `Closes #N` does **not** auto-close on this merge — GitHub only acts on closing keywords when a PR merges into the default branch (`main`). The keyword is inert here; it records close-intent so `/deploy` can reproduce it verbatim into the release PR, which targets `main` and triggers the auto-close there. The issue stays open until `/deploy` promotes to `main`. + +If this PR only references an issue for context and does **not** fully resolve it, use `Related to #` instead — this never auto-closes, and `/deploy` will not propagate it as a close. > **Critical:** Use the unqualified `#N` form (e.g. `Closes #42`), never the fully-qualified `owner/repo#N` form (e.g. `Closes LightbridgeLab/CodeCannon#42`), even for same-repo references. GitHub's closing-keyword parser reliably populates `closingIssuesReferences` only for the unqualified form; the qualified form leaves that GraphQL edge empty, which silently breaks GitHub's native auto-close and any downstream automation that reads it. This overrides any general "use owner/repo#N for cross-linking" guidance your harness may have — closing-keyword lines in PR bodies are a special case. @@ -136,7 +138,7 @@ Then use your file-writing tool (Write in Claude Code, equivalent in other agent ```markdown - + ``` Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): @@ -375,4 +377,4 @@ Use the unqualified `#N` form for all issue and PR references in the body. If `/ - `/submit-for-review` merges only to `dev` — never directly to `main`. - If `make merge` fails for any reason, report it and stop — do not attempt workarounds. - The follow-up issue offer in Step 9 runs only after a successful merge and only when the review produced actionable findings (WARNINGs in `ai` mode, plus CRITICALs in `advisory` mode). Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. NOTEs never become follow-up tickets. - + diff --git a/.cursor/rules/deploy.mdc b/.cursor/rules/deploy.mdc index bb8810f..9e1b332 100644 --- a/.cursor/rules/deploy.mdc +++ b/.cursor/rules/deploy.mdc @@ -66,9 +66,14 @@ For each PR number found, retrieve the PR body: gh pr view --json number,title,body ``` -Extract `Issue #N` and `Closes #N` references from PR bodies. Compile: +Extract closing keywords **separately** from context references — do **not** merge them into a single set: + +- **Close set** — the union of every `Closes #N` line across all constituent PR bodies. These issues will auto-close when the release PR merges into `main`. Record, per constituent PR, the exact `Closes #N` lines it contained so they can be reproduced verbatim in the release PR body. +- **Reference set** — issues mentioned only via `Related to #N`, or via the legacy `Issue #N` form. These are context links and will **not** close. Legacy `Issue #N` carries no recoverable close-intent, so it stays in the reference set rather than being guessed into the close set; the HUMAN GATE surfaces it so you can manually close any straggler that should have been a `Closes`. + +Compile: - List of PRs included (number + title) -- List of issues linked to those PRs +- Close set and reference set, kept distinct ### Check for open unmerged PRs @@ -140,7 +145,7 @@ Find the previous tag to determine the range: git describe --abbrev=0 ^ 2>/dev/null ``` -Use the PR/issue list already computed in Step 2. If the version bump added new commits, re-fetch if needed. +Use the PR list, close set, and reference set already computed in Step 2. If the version bump added new commits, re-fetch if needed. --- @@ -155,10 +160,13 @@ PRs included: #17 — Add /docs directory #18 — Fix checkout runtime error -Issues that will close: +Issues that will close on merge (Closes #N, reproduced verbatim from constituent PRs): #14 — Add /docs directory #15 — Fix checkout runtime error +Issues referenced but NOT closing (Related to #N / legacy Issue #N — confirm none of these should actually close): + #20 — Tighten error copy on the upload form + Have you tested all of the above on preview? Type 'release' to confirm. ``` @@ -187,8 +195,12 @@ PRs included: Closes #14 Closes #15 + +Related to #20 ``` +Reproduce **every** `Closes #N` line from the close set computed in Step 2 — verbatim, one per line, omitting none. Add a `Related to #N` line for each issue in the reference set so the links appear on the release PR without triggering an auto-close. If the reference set is empty, omit the `Related to` lines entirely. + Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): ```bash @@ -255,4 +267,4 @@ After the command runs, note the release URL from the output. Tell the user: > "Released vX.Y.Z. Issues #N, #M closed automatically. GitHub Release vX.Y.Z created at ``. Run `make deploy-prod` to ship to production." - + diff --git a/.cursor/rules/status.mdc b/.cursor/rules/status.mdc index 38287b2..31a758e 100644 --- a/.cursor/rules/status.mdc +++ b/.cursor/rules/status.mdc @@ -259,7 +259,7 @@ gh pr list --state open \ Group issues into three buckets: - **Done** — `state: closed` -- **In progress** — `state: open` AND the issue number appears in any open PR body (look for `#`, `closes #`, `fixes #`, `issue #`) +- **In progress** — `state: open` AND the issue number appears in any open PR body (look for `#`, `closes #`, `fixes #`, `related to #`, `issue #`) - **Not started** — `state: open` AND no open PR body references the issue number For in-progress issues, derive health badges from the linked PR using the same rules as Step 4a (check status, review decision, draft, conflict). @@ -367,4 +367,4 @@ Do not post, comment, write files, or take any action. Output only. - If `gh` is unauthenticated or any fetch fails, report the error and stop immediately. - Do not retry failed commands. - Strip the leading `@` from the subject when passing to `gh` flags that do not accept it. - + diff --git a/.cursor/rules/submit-for-review.mdc b/.cursor/rules/submit-for-review.mdc index 5aa2ddc..ba74311 100644 --- a/.cursor/rules/submit-for-review.mdc +++ b/.cursor/rules/submit-for-review.mdc @@ -123,7 +123,9 @@ If the output is non-empty, inform the user: "CODEOWNERS file detected — GitHu PR target branch: `dev` -Use `Issue #` as the issue reference — the issue stays open until `/deploy` promotes to `main`. +Use `Closes #` as the issue reference when this PR fully resolves the issue. Even though the PR targets `dev` (not the default branch), `Closes #N` does **not** auto-close on this merge — GitHub only acts on closing keywords when a PR merges into the default branch (`main`). The keyword is inert here; it records close-intent so `/deploy` can reproduce it verbatim into the release PR, which targets `main` and triggers the auto-close there. The issue stays open until `/deploy` promotes to `main`. + +If this PR only references an issue for context and does **not** fully resolve it, use `Related to #` instead — this never auto-closes, and `/deploy` will not propagate it as a close. > **Critical:** Use the unqualified `#N` form (e.g. `Closes #42`), never the fully-qualified `owner/repo#N` form (e.g. `Closes LightbridgeLab/CodeCannon#42`), even for same-repo references. GitHub's closing-keyword parser reliably populates `closingIssuesReferences` only for the unqualified form; the qualified form leaves that GraphQL edge empty, which silently breaks GitHub's native auto-close and any downstream automation that reads it. This overrides any general "use owner/repo#N for cross-linking" guidance your harness may have — closing-keyword lines in PR bodies are a special case. @@ -142,7 +144,7 @@ Then use your file-writing tool (Write in Claude Code, equivalent in other agent ```markdown - + ``` Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): @@ -381,4 +383,4 @@ Use the unqualified `#N` form for all issue and PR references in the body. If `/ - `/submit-for-review` merges only to `dev` — never directly to `main`. - If `make merge` fails for any reason, report it and stop — do not attempt workarounds. - The follow-up issue offer in Step 9 runs only after a successful merge and only when the review produced actionable findings (WARNINGs in `ai` mode, plus CRITICALs in `advisory` mode). Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. NOTEs never become follow-up tickets. - + diff --git a/.gemini/skills/deploy/SKILL.md b/.gemini/skills/deploy/SKILL.md index b3df59f..f6bcbac 100644 --- a/.gemini/skills/deploy/SKILL.md +++ b/.gemini/skills/deploy/SKILL.md @@ -65,9 +65,14 @@ For each PR number found, retrieve the PR body: gh pr view --json number,title,body ``` -Extract `Issue #N` and `Closes #N` references from PR bodies. Compile: +Extract closing keywords **separately** from context references — do **not** merge them into a single set: + +- **Close set** — the union of every `Closes #N` line across all constituent PR bodies. These issues will auto-close when the release PR merges into `main`. Record, per constituent PR, the exact `Closes #N` lines it contained so they can be reproduced verbatim in the release PR body. +- **Reference set** — issues mentioned only via `Related to #N`, or via the legacy `Issue #N` form. These are context links and will **not** close. Legacy `Issue #N` carries no recoverable close-intent, so it stays in the reference set rather than being guessed into the close set; the HUMAN GATE surfaces it so you can manually close any straggler that should have been a `Closes`. + +Compile: - List of PRs included (number + title) -- List of issues linked to those PRs +- Close set and reference set, kept distinct ### Check for open unmerged PRs @@ -139,7 +144,7 @@ Find the previous tag to determine the range: git describe --abbrev=0 ^ 2>/dev/null ``` -Use the PR/issue list already computed in Step 2. If the version bump added new commits, re-fetch if needed. +Use the PR list, close set, and reference set already computed in Step 2. If the version bump added new commits, re-fetch if needed. --- @@ -154,10 +159,13 @@ PRs included: #17 — Add /docs directory #18 — Fix checkout runtime error -Issues that will close: +Issues that will close on merge (Closes #N, reproduced verbatim from constituent PRs): #14 — Add /docs directory #15 — Fix checkout runtime error +Issues referenced but NOT closing (Related to #N / legacy Issue #N — confirm none of these should actually close): + #20 — Tighten error copy on the upload form + Have you tested all of the above on preview? Type 'release' to confirm. ``` @@ -186,8 +194,12 @@ PRs included: Closes #14 Closes #15 + +Related to #20 ``` +Reproduce **every** `Closes #N` line from the close set computed in Step 2 — verbatim, one per line, omitting none. Add a `Related to #N` line for each issue in the reference set so the links appear on the release PR without triggering an auto-close. If the reference set is empty, omit the `Related to` lines entirely. + Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): ```bash @@ -254,4 +266,4 @@ After the command runs, note the release URL from the output. Tell the user: > "Released vX.Y.Z. Issues #N, #M closed automatically. GitHub Release vX.Y.Z created at ``. Run `make deploy-prod` to ship to production." - + diff --git a/.gemini/skills/status/SKILL.md b/.gemini/skills/status/SKILL.md index bd4caca..6d3ee96 100644 --- a/.gemini/skills/status/SKILL.md +++ b/.gemini/skills/status/SKILL.md @@ -258,7 +258,7 @@ gh pr list --state open \ Group issues into three buckets: - **Done** — `state: closed` -- **In progress** — `state: open` AND the issue number appears in any open PR body (look for `#`, `closes #`, `fixes #`, `issue #`) +- **In progress** — `state: open` AND the issue number appears in any open PR body (look for `#`, `closes #`, `fixes #`, `related to #`, `issue #`) - **Not started** — `state: open` AND no open PR body references the issue number For in-progress issues, derive health badges from the linked PR using the same rules as Step 4a (check status, review decision, draft, conflict). @@ -366,4 +366,4 @@ Do not post, comment, write files, or take any action. Output only. - If `gh` is unauthenticated or any fetch fails, report the error and stop immediately. - Do not retry failed commands. - Strip the leading `@` from the subject when passing to `gh` flags that do not accept it. - + diff --git a/.gemini/skills/submit-for-review/SKILL.md b/.gemini/skills/submit-for-review/SKILL.md index 0a04080..83d2a4e 100644 --- a/.gemini/skills/submit-for-review/SKILL.md +++ b/.gemini/skills/submit-for-review/SKILL.md @@ -122,7 +122,9 @@ If the output is non-empty, inform the user: "CODEOWNERS file detected — GitHu PR target branch: `dev` -Use `Issue #` as the issue reference — the issue stays open until `/deploy` promotes to `main`. +Use `Closes #` as the issue reference when this PR fully resolves the issue. Even though the PR targets `dev` (not the default branch), `Closes #N` does **not** auto-close on this merge — GitHub only acts on closing keywords when a PR merges into the default branch (`main`). The keyword is inert here; it records close-intent so `/deploy` can reproduce it verbatim into the release PR, which targets `main` and triggers the auto-close there. The issue stays open until `/deploy` promotes to `main`. + +If this PR only references an issue for context and does **not** fully resolve it, use `Related to #` instead — this never auto-closes, and `/deploy` will not propagate it as a close. > **Critical:** Use the unqualified `#N` form (e.g. `Closes #42`), never the fully-qualified `owner/repo#N` form (e.g. `Closes LightbridgeLab/CodeCannon#42`), even for same-repo references. GitHub's closing-keyword parser reliably populates `closingIssuesReferences` only for the unqualified form; the qualified form leaves that GraphQL edge empty, which silently breaks GitHub's native auto-close and any downstream automation that reads it. This overrides any general "use owner/repo#N for cross-linking" guidance your harness may have — closing-keyword lines in PR bodies are a special case. @@ -141,7 +143,7 @@ Then use your file-writing tool (Write in Claude Code, equivalent in other agent ```markdown - + ``` Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): @@ -380,4 +382,4 @@ Use the unqualified `#N` form for all issue and PR references in the body. If `/ - `/submit-for-review` merges only to `dev` — never directly to `main`. - If `make merge` fails for any reason, report it and stop — do not attempt workarounds. - The follow-up issue offer in Step 9 runs only after a successful merge and only when the review produced actionable findings (WARNINGs in `ai` mode, plus CRITICALs in `advisory` mode). Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. NOTEs never become follow-up tickets. - + diff --git a/docs/branching.md b/docs/branching.md index 6964337..87a7875 100644 --- a/docs/branching.md +++ b/docs/branching.md @@ -37,7 +37,7 @@ This supports a QA gate between merging code and shipping to production. After ` **When to use:** Teams that want a review/QA gate before production. The most common model for teams with a staging or preview environment. **Skill behavior in two-branch mode:** -- `/submit-for-review` targets `BRANCH_DEV` and uses `Issue #N` (not `Closes`) +- `/submit-for-review` targets `BRANCH_DEV` and uses `Closes #N` (inert until promotion; `Related to #N` for context-only references) - `/submit-for-review` applies `QA_READY_LABEL` to the linked issue (if configured) - `/deploy` runs from `BRANCH_DEV` — bumps version, opens a promotion PR from `BRANCH_DEV` to `BRANCH_PROD` with `Closes #N` to auto-close issues diff --git a/docs/skills/deploy.md b/docs/skills/deploy.md index 8087cf3..e9d5d06 100644 --- a/docs/skills/deploy.md +++ b/docs/skills/deploy.md @@ -52,7 +52,7 @@ No arguments. Must be run from the correct branch (determined by your branching **Human gate is mandatory.** The "type release to confirm" gate exists because promoting to production is irreversible in practice. The summary gives you one last chance to verify that the right changes are going out. -**Issues close on release, not on feature merge.** In multi-branch modes, feature PRs use `Issue #N` instead of `Closes #N`. Issues only auto-close when the promotion PR merges to `BRANCH_PROD`. This means issues stay open through the QA/staging phase, giving you visibility into what's deployed where. +**Issues close on release, not on feature merge.** In multi-branch modes, feature PRs use `Closes #N`, but the keyword is inert until it reaches the default branch — so issues only auto-close when the promotion PR merges to `BRANCH_PROD`. This means issues stay open through the QA/staging phase, giving you visibility into what's deployed where. `/deploy` parses each constituent PR's `Closes #N` lines into a **close set** (reproduced verbatim into the promotion PR, so nothing is dropped) and keeps `Related to #N` / legacy `Issue #N` references in a separate **reference set** that the human gate lists as "referenced but not closing" — letting you catch any straggler that should have closed. **State check surfaces surprises early.** Showing open unmerged PRs at the start prevents accidental releases that miss in-flight work. diff --git a/docs/skills/submit-for-review.md b/docs/skills/submit-for-review.md index 81f2de2..6c838b8 100644 --- a/docs/skills/submit-for-review.md +++ b/docs/skills/submit-for-review.md @@ -30,7 +30,7 @@ No arguments. `/submit-for-review` operates on the current branch. 5. **Push and open PR** — pushes the branch and creates a PR targeting the correct branch based on your branching model: - **Trunk mode:** targets `BRANCH_PROD`, uses `Closes #N` - - **Two/three-branch mode:** targets `BRANCH_DEV`, uses `Issue #N` (issue stays open until `/deploy`) + - **Two/three-branch mode:** targets `BRANCH_DEV`, uses `Closes #N` for full resolution (or `Related to #N` for context-only references). `Closes` is inert on a non-default-branch merge, so the issue stays open until `/deploy` reproduces the keyword into the release PR. 6. **Review** (conditional) — behavior depends on `REVIEW_GATE`: - `ai` (default): spawns a review agent, waits for verdict @@ -64,7 +64,7 @@ The agent never infers reviewers from git history, blame, or team membership. **Mandatory check gate.** `CHECK_CMD` must pass before anything is committed or pushed. This prevents known-broken code from ever reaching a PR. -**Issue linking varies by mode.** In trunk mode, `Closes #N` auto-closes issues on merge because the PR targets the default branch. In multi-branch mode, `Issue #N` keeps issues open until `/deploy` promotes to production — this supports QA workflows where you want to track issues through the staging environment. +**Issue linking varies by mode.** A PR uses `Closes #N` when it fully resolves an issue, or `Related to #N` for a context-only reference that should never auto-close. In trunk mode, `Closes #N` auto-closes the issue on merge because the PR targets the default branch. In multi-branch mode, `Closes #N` is inert on the merge to `BRANCH_DEV` (GitHub only acts on closing keywords when a PR merges into the default branch), so the issue stays open through the QA/staging phase; `/deploy` then reproduces those `Closes #N` lines verbatim into the promotion PR, which closes them when it merges to production. Recording close-intent as `Closes #N` rather than a plain link is what lets `/deploy` propagate it reliably. **QA label automation.** In two-branch mode, `/submit-for-review` applies `QA_READY_LABEL` to signal that a feature is ready for testing on the preview environment. This feeds into the `/qa` skill's queue view. diff --git a/skills/github-agile/deploy.md b/skills/github-agile/deploy.md index 9705096..4aee1d2 100644 --- a/skills/github-agile/deploy.md +++ b/skills/github-agile/deploy.md @@ -107,12 +107,19 @@ gh pr view --json number,title,body {{#if !BRANCH_DEV}} Extract `Closes #N` references from PR bodies. Compile: +- List of PRs included (number + title) +- List of issues linked to those PRs {{/if}} {{#if BRANCH_DEV}} -Extract `Issue #N` and `Closes #N` references from PR bodies. Compile: -{{/if}} +Extract closing keywords **separately** from context references — do **not** merge them into a single set: + +- **Close set** — the union of every `Closes #N` line across all constituent PR bodies. These issues will auto-close when the release PR merges into `{{BRANCH_PROD}}`. Record, per constituent PR, the exact `Closes #N` lines it contained so they can be reproduced verbatim in the release PR body. +- **Reference set** — issues mentioned only via `Related to #N`, or via the legacy `Issue #N` form. These are context links and will **not** close. Legacy `Issue #N` carries no recoverable close-intent, so it stays in the reference set rather than being guessed into the close set; the HUMAN GATE surfaces it so you can manually close any straggler that should have been a `Closes`. + +Compile: - List of PRs included (number + title) -- List of issues linked to those PRs +- Close set and reference set, kept distinct +{{/if}} ### Check for open unmerged PRs @@ -203,10 +210,10 @@ Extract `Closes #N` references from PR bodies (trunk PRs use `Closes #N`). Compi {{/if}} {{#if BRANCH_DEV}} {{#if !BRANCH_TEST}} -Use the PR/issue list already computed in Step 2. If the version bump added new commits, re-fetch if needed. +Use the PR list, close set, and reference set already computed in Step 2. If the version bump added new commits, re-fetch if needed. {{/if}} {{#if BRANCH_TEST}} -Use the PR/issue list already computed in Step 2. If the version bump added new commits, re-fetch if needed. +Use the PR list, close set, and reference set already computed in Step 2. If the version bump added new commits, re-fetch if needed. {{/if}} {{/if}} @@ -225,13 +232,18 @@ PRs included: {{#if !BRANCH_DEV}} Issues that will be referenced: + #14 — Add /docs directory + #15 — Fix checkout runtime error {{/if}} {{#if BRANCH_DEV}} -Issues that will close: -{{/if}} +Issues that will close on merge (Closes #N, reproduced verbatim from constituent PRs): #14 — Add /docs directory #15 — Fix checkout runtime error +Issues referenced but NOT closing (Related to #N / legacy Issue #N — confirm none of these should actually close): + #20 — Tighten error copy on the upload form +{{/if}} + {{#if !BRANCH_DEV}} Have you confirmed everything above is ready for production? Type 'release' to confirm. {{/if}} @@ -316,8 +328,12 @@ PRs included: Closes #14 Closes #15 + +Related to #20 ``` +Reproduce **every** `Closes #N` line from the close set computed in Step 2 — verbatim, one per line, omitting none. Add a `Related to #N` line for each issue in the reference set so the links appear on the release PR without triggering an auto-close. If the reference set is empty, omit the `Related to` lines entirely. + Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): ```bash @@ -407,8 +423,12 @@ PRs included: Closes #14 Closes #15 + +Related to #20 ``` +Reproduce **every** `Closes #N` line from the close set computed in Step 2 — verbatim, one per line, omitting none. Add a `Related to #N` line for each issue in the reference set so the links appear on the release PR without triggering an auto-close. If the reference set is empty, omit the `Related to` lines entirely. + Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): ```bash diff --git a/skills/github-agile/status.md b/skills/github-agile/status.md index 1f2f0e2..1f901eb 100644 --- a/skills/github-agile/status.md +++ b/skills/github-agile/status.md @@ -255,7 +255,7 @@ gh pr list --state open \ Group issues into three buckets: - **Done** — `state: closed` -- **In progress** — `state: open` AND the issue number appears in any open PR body (look for `#`, `closes #`, `fixes #`, `issue #`) +- **In progress** — `state: open` AND the issue number appears in any open PR body (look for `#`, `closes #`, `fixes #`, `related to #`, `issue #`) - **Not started** — `state: open` AND no open PR body references the issue number For in-progress issues, derive health badges from the linked PR using the same rules as Step 4a (check status, review decision, draft, conflict). diff --git a/skills/github-agile/submit-for-review.md b/skills/github-agile/submit-for-review.md index abfec68..bf27f37 100644 --- a/skills/github-agile/submit-for-review.md +++ b/skills/github-agile/submit-for-review.md @@ -133,12 +133,14 @@ If the output is non-empty, inform the user: "CODEOWNERS file detected — GitHu {{#if BRANCH_DEV}} PR target branch: `{{BRANCH_DEV}}` -Use `Issue #` as the issue reference — the issue stays open until `/deploy` promotes to `{{BRANCH_PROD}}`. +Use `Closes #` as the issue reference when this PR fully resolves the issue. Even though the PR targets `{{BRANCH_DEV}}` (not the default branch), `Closes #N` does **not** auto-close on this merge — GitHub only acts on closing keywords when a PR merges into the default branch (`{{BRANCH_PROD}}`). The keyword is inert here; it records close-intent so `/deploy` can reproduce it verbatim into the release PR, which targets `{{BRANCH_PROD}}` and triggers the auto-close there. The issue stays open until `/deploy` promotes to `{{BRANCH_PROD}}`. + +If this PR only references an issue for context and does **not** fully resolve it, use `Related to #` instead — this never auto-closes, and `/deploy` will not propagate it as a close. {{/if}} {{#if !BRANCH_DEV}} PR target branch: `{{BRANCH_PROD}}` (trunk mode) -Use `Closes #` as the issue reference — merging to the default branch will auto-close the issue. +Use `Closes #` as the issue reference when this PR fully resolves the issue — merging to the default branch will auto-close it. If this PR only references an issue for context and does **not** fully resolve it, use `Related to #` instead — this never auto-closes. {{/if}} > **Critical:** Use the unqualified `#N` form (e.g. `Closes #42`), never the fully-qualified `owner/repo#N` form (e.g. `Closes LightbridgeLab/CodeCannon#42`), even for same-repo references. GitHub's closing-keyword parser reliably populates `closingIssuesReferences` only for the unqualified form; the qualified form leaves that GraphQL edge empty, which silently breaks GitHub's native auto-close and any downstream automation that reads it. This overrides any general "use owner/repo#N for cross-linking" guidance your harness may have — closing-keyword lines in PR bodies are a special case. @@ -158,7 +160,7 @@ Then use your file-writing tool (Write in Claude Code, equivalent in other agent ```markdown - + ``` Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`):