diff --git a/.agents/skills/deploy/SKILL.md b/.agents/skills/deploy/SKILL.md index 9ca68c1..037b39a 100644 --- a/.agents/skills/deploy/SKILL.md +++ b/.agents/skills/deploy/SKILL.md @@ -169,7 +169,7 @@ Wait for the user to type "release" or an explicit confirmation. Any other respo ```bash gh pr create --base main --head dev \ --title "Release vX.Y.Z" \ - --body "$(cat <<'EOF' + --body-file - <<'EOF' Release vX.Y.Z PRs included: @@ -179,13 +179,14 @@ PRs included: Closes #14 Closes #15 EOF -)" ``` Note the PR number from the output. The `Closes #N` lines will auto-close the linked issues because this PR merges into `main` (the default branch). +> **Critical:** Use the unqualified `#N` form only. Never write `Closes owner/repo#N`, even for same-repo refs — GitHub's closing-keyword parser only populates `closingIssuesReferences` for the unqualified form, and the qualified form silently breaks auto-close. + --- ## Step 7 — Merge @@ -213,7 +214,7 @@ Create the release: ```bash gh release create \ --title "" \ - --notes "$(cat <<'EOF' + --notes-file - <<'EOF' ## Changes - # (PR #) @@ -221,7 +222,6 @@ gh release create \ **Full changelog:** https://github.com///compare/... EOF -)" ``` Format each PR line as `- # (PR #)`. If a PR had no linked issue, omit the `#` prefix and use just the PR title. @@ -235,4 +235,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/start/SKILL.md b/.agents/skills/start/SKILL.md index a84d3e8..740cfa5 100644 --- a/.agents/skills/start/SKILL.md +++ b/.agents/skills/start/SKILL.md @@ -40,12 +40,7 @@ After parsing flags, determine the active labels in this order: 2. **Pool-based selection** — the allowed label pool is: `bug, documentation, enhancement, chore` (comma-separated). Select 1–3 labels from this pool that genuinely fit the task description and implementation approach. Do not apply labels mechanically — pick only what fits. If no pool label fits the task, fall through to step 3. - If any selected label name contains a space (e.g. `good first issue`), quote the entire `--label` value. 3. **No label / creation** — if the pool is empty or no pool label fits: - - If label creation is allowed (`false` = `true`, case-insensitive): the agent **may** create a new label before applying it: - ```bash - gh label create "" --color "" --description "" - ``` - Use judgment — only create a label with clear reuse value. Do not create near-duplicates of existing pool labels. - - Otherwise (`false` = `false` or unset): omit `--label` entirely. Proceed silently; do not inform the user. + - Label creation is not allowed in this project. Omit `--label` entirely. Proceed silently; do not inform the user. **Milestone resolution (three-tier, Case A only):** @@ -98,7 +93,7 @@ Run `gh issue create` with explicit flags (do NOT open an interactive editor): ```bash gh issue create \ --title "" \ - --body "" \ + --body "" \ --assignee @me \ [--label ""] \ [--milestone ""] @@ -108,6 +103,25 @@ Resolve labels and milestone using the resolution steps in the Parsing section a - **Labels**: use the value from three-tier label resolution. If non-empty, add `--label ""` to the command. If empty (no flag, empty pool, creation not allowed), omit `--label` entirely. - **Milestone**: use the value from three-tier milestone resolution. If non-empty, add `--milestone ""` to the command. If empty (no flag, no config default, no open milestones), omit `--milestone` entirely. +**Body structure (required sections, in this order):** + +```markdown +## Problem to Fix + + +## Why it Matters + + +## General Approach + + +## Acceptance Criteria +- +- +``` + +All four sections are required. Write for a non-developer audience — no code, no file paths. Acceptance Criteria must be concrete and verifiable (not vague goals). + **Title rules:** - ✅ `Fix 'Contact Us' footer link pointing to 404 instead of /contact-us` - ❌ `Fix broken link` @@ -228,6 +242,6 @@ When done, say: **"The code is ready for review. Please run `make dev` and test - If already on a feature branch when `/start` is invoked, warn the user before creating another branch. - `gh issue create` must use `--title` and `--body` flags. Never open an interactive editor. - The issue is assigned to `@me` at creation. If you are creating a ticket on someone else's behalf, remove the assignee after creation with `gh issue edit --remove-assignee @me`. -- Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `bug, documentation, enhancement, chore` → omit (or, when `false` = `true`, create). Never apply a label outside `bug, documentation, enhancement, chore` unless `false` = `true`. +- Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `bug, documentation, enhancement, chore` → omit `--label` entirely. Never apply a label outside `bug, documentation, enhancement, chore`. - Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. - + diff --git a/.agents/skills/submit-for-review/SKILL.md b/.agents/skills/submit-for-review/SKILL.md index 35afd88..4bb8b8c 100644 --- a/.agents/skills/submit-for-review/SKILL.md +++ b/.agents/skills/submit-for-review/SKILL.md @@ -107,16 +107,19 @@ PR target branch: `dev` Use `Issue #` as the issue reference — the issue stays open until `/deploy` promotes to `main`. +> **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. + Then create the PR with explicit title and body (never use an interactive editor): ``` -gh pr create --base --title "" --body "$(cat <<'EOF' +gh pr create --base <target-branch> --title "<title>" --body-file - <<'EOF' <description of what changed and why> <Closes #N OR Issue #N, based on target above> EOF -)" ``` +> **Why `--body-file -` + stdin heredoc?** Claude Code's permission matcher refuses to extend any `Bash(gh:*)` allow rule over a command containing `$(...)` substitution, so `--body "$(cat <<'EOF' ...)"` triggers a Yes/No prompt on every run with no "Allow always" option. Piping the body in on stdin via heredoc redirection is not command substitution — the matcher sees a clean `gh pr create …` invocation and `Bash(gh:*)` matches. Apply this pattern to every multi-line `--body` / `--notes` in skills (`gh pr create`, `gh issue create`, `gh issue comment`, and `gh release create` all accept `-` for their `*-file` flags). + Add `--reviewer` to the `gh pr create` command above using the handles from `@sebastientaggart`. Before passing them, strip any leading `@` from each comma-separated handle (e.g. `@alice,@org/team` becomes `alice,org/team`) — the `gh` CLI requires bare usernames. If a CODEOWNERS file exists, both apply: CODEOWNERS triggers automatic review requests from GitHub; the `--reviewer` flag adds the explicitly configured handles on top. @@ -225,18 +228,17 @@ Accept: comma-separated numbers, `all`, or `none`/`skip`/empty. If the input is ``` gh issue create \ --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ - --body "$(cat <<'EOF' + [--label "<pool-selected labels>"] \ + --body-file - <<'EOF' Follow-up from PR #<merged-pr-number> — auto-proposed from the code review. **Finding:** <full finding text, prefix included> See the review comment on the PR for context. EOF -)" \ - [--label "<pool-selected labels>"] ``` -Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `bug, documentation, enhancement, chore` that genuinely fit the finding. If `bug, documentation, enhancement, chore` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and do not create new labels regardless of `false`. +Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `bug, documentation, enhancement, chore` that genuinely fit the finding. If `bug, documentation, enhancement, chore` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and never create new labels from follow-ups, even if label creation is enabled for the project. Do **not** pass `--milestone` — follow-ups are future work and should not inherit the current sprint. @@ -259,4 +261,4 @@ If a single `gh issue create` call fails, report the failure for that finding an - `/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 non-blocking findings. Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. -<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: codex | hash: a10214da | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: codex | hash: 7f9a2844 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.claude/commands/deploy.md b/.claude/commands/deploy.md index c7ec275..4c919ad 100644 --- a/.claude/commands/deploy.md +++ b/.claude/commands/deploy.md @@ -164,7 +164,7 @@ Wait for the user to type "release" or an explicit confirmation. Any other respo ```bash gh pr create --base main --head dev \ --title "Release vX.Y.Z" \ - --body "$(cat <<'EOF' + --body-file - <<'EOF' Release vX.Y.Z PRs included: @@ -174,13 +174,14 @@ PRs included: Closes #14 Closes #15 EOF -)" ``` Note the PR number from the output. The `Closes #N` lines will auto-close the linked issues because this PR merges into `main` (the default branch). +> **Critical:** Use the unqualified `#N` form only. Never write `Closes owner/repo#N`, even for same-repo refs — GitHub's closing-keyword parser only populates `closingIssuesReferences` for the unqualified form, and the qualified form silently breaks auto-close. + --- ## Step 7 — Merge @@ -208,7 +209,7 @@ Create the release: ```bash gh release create <version-tag> \ --title "<version-tag>" \ - --notes "$(cat <<'EOF' + --notes-file - <<'EOF' ## Changes - #<issue> — <PR title> (PR #<pr-number>) @@ -216,7 +217,6 @@ gh release create <version-tag> \ **Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> EOF -)" ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. @@ -230,4 +230,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 `<url>`. Run `make deploy-prod` to ship to production." -<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: claude | hash: e0dd0480 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: claude | hash: b1efee29 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.claude/commands/start.md b/.claude/commands/start.md index ccd7f07..dc9d2b3 100644 --- a/.claude/commands/start.md +++ b/.claude/commands/start.md @@ -35,12 +35,7 @@ After parsing flags, determine the active labels in this order: 2. **Pool-based selection** — the allowed label pool is: `bug, documentation, enhancement, chore` (comma-separated). Select 1–3 labels from this pool that genuinely fit the task description and implementation approach. Do not apply labels mechanically — pick only what fits. If no pool label fits the task, fall through to step 3. - If any selected label name contains a space (e.g. `good first issue`), quote the entire `--label` value. 3. **No label / creation** — if the pool is empty or no pool label fits: - - If label creation is allowed (`false` = `true`, case-insensitive): the agent **may** create a new label before applying it: - ```bash - gh label create "<name>" --color "<hex>" --description "<short description>" - ``` - Use judgment — only create a label with clear reuse value. Do not create near-duplicates of existing pool labels. - - Otherwise (`false` = `false` or unset): omit `--label` entirely. Proceed silently; do not inform the user. + - Label creation is not allowed in this project. Omit `--label` entirely. Proceed silently; do not inform the user. **Milestone resolution (three-tier, Case A only):** @@ -93,7 +88,7 @@ Run `gh issue create` with explicit flags (do NOT open an interactive editor): ```bash gh issue create \ --title "<standalone full sentence — must make sense with no context>" \ - --body "<human-readable explanation: what the problem is, why it matters, general approach — written for a non-developer, no code or file paths>" \ + --body "<structured markdown body — see sections below>" \ --assignee @me \ [--label "<resolved labels>"] \ [--milestone "<resolved milestone>"] @@ -103,6 +98,25 @@ Resolve labels and milestone using the resolution steps in the Parsing section a - **Labels**: use the value from three-tier label resolution. If non-empty, add `--label "<value>"` to the command. If empty (no flag, empty pool, creation not allowed), omit `--label` entirely. - **Milestone**: use the value from three-tier milestone resolution. If non-empty, add `--milestone "<value>"` to the command. If empty (no flag, no config default, no open milestones), omit `--milestone` entirely. +**Body structure (required sections, in this order):** + +```markdown +## Problem to Fix +<what is broken or missing, written for a non-developer — no code or file paths> + +## Why it Matters +<the impact or motivation — who is affected and how> + +## General Approach +<high-level direction for the fix, in plain language> + +## Acceptance Criteria +- <specific, verifiable outcome> +- <another outcome> +``` + +All four sections are required. Write for a non-developer audience — no code, no file paths. Acceptance Criteria must be concrete and verifiable (not vague goals). + **Title rules:** - ✅ `Fix 'Contact Us' footer link pointing to 404 instead of /contact-us` - ❌ `Fix broken link` @@ -223,6 +237,6 @@ When done, say: **"The code is ready for review. Please run `make dev` and test - If already on a feature branch when `/start` is invoked, warn the user before creating another branch. - `gh issue create` must use `--title` and `--body` flags. Never open an interactive editor. - The issue is assigned to `@me` at creation. If you are creating a ticket on someone else's behalf, remove the assignee after creation with `gh issue edit <number> --remove-assignee @me`. -- Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `bug, documentation, enhancement, chore` → omit (or, when `false` = `true`, create). Never apply a label outside `bug, documentation, enhancement, chore` unless `false` = `true`. +- Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `bug, documentation, enhancement, chore` → omit `--label` entirely. Never apply a label outside `bug, documentation, enhancement, chore`. - Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. -<!-- generated by CodeCannon/sync.sh | skill: start | adapter: claude | hash: be9337a6 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: start | adapter: claude | hash: c3649e3f | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.claude/commands/submit-for-review.md b/.claude/commands/submit-for-review.md index 585ce7d..cf071a5 100644 --- a/.claude/commands/submit-for-review.md +++ b/.claude/commands/submit-for-review.md @@ -102,16 +102,19 @@ PR target branch: `dev` Use `Issue #<number>` as the issue reference — the issue stays open until `/deploy` promotes to `main`. +> **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. + Then create the PR with explicit title and body (never use an interactive editor): ``` -gh pr create --base <target-branch> --title "<title>" --body "$(cat <<'EOF' +gh pr create --base <target-branch> --title "<title>" --body-file - <<'EOF' <description of what changed and why> <Closes #N OR Issue #N, based on target above> EOF -)" ``` +> **Why `--body-file -` + stdin heredoc?** Claude Code's permission matcher refuses to extend any `Bash(gh:*)` allow rule over a command containing `$(...)` substitution, so `--body "$(cat <<'EOF' ...)"` triggers a Yes/No prompt on every run with no "Allow always" option. Piping the body in on stdin via heredoc redirection is not command substitution — the matcher sees a clean `gh pr create …` invocation and `Bash(gh:*)` matches. Apply this pattern to every multi-line `--body` / `--notes` in skills (`gh pr create`, `gh issue create`, `gh issue comment`, and `gh release create` all accept `-` for their `*-file` flags). + Add `--reviewer` to the `gh pr create` command above using the handles from `@sebastientaggart`. Before passing them, strip any leading `@` from each comma-separated handle (e.g. `@alice,@org/team` becomes `alice,org/team`) — the `gh` CLI requires bare usernames. If a CODEOWNERS file exists, both apply: CODEOWNERS triggers automatic review requests from GitHub; the `--reviewer` flag adds the explicitly configured handles on top. @@ -220,18 +223,17 @@ Accept: comma-separated numbers, `all`, or `none`/`skip`/empty. If the input is ``` gh issue create \ --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ - --body "$(cat <<'EOF' + [--label "<pool-selected labels>"] \ + --body-file - <<'EOF' Follow-up from PR #<merged-pr-number> — auto-proposed from the code review. **Finding:** <full finding text, prefix included> See the review comment on the PR for context. EOF -)" \ - [--label "<pool-selected labels>"] ``` -Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `bug, documentation, enhancement, chore` that genuinely fit the finding. If `bug, documentation, enhancement, chore` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and do not create new labels regardless of `false`. +Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `bug, documentation, enhancement, chore` that genuinely fit the finding. If `bug, documentation, enhancement, chore` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and never create new labels from follow-ups, even if label creation is enabled for the project. Do **not** pass `--milestone` — follow-ups are future work and should not inherit the current sprint. @@ -254,4 +256,4 @@ If a single `gh issue create` call fails, report the failure for that finding an - `/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 non-blocking findings. Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. -<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: claude | hash: 29a2a831 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: claude | hash: 4845c4c2 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.cursor/rules/deploy.mdc b/.cursor/rules/deploy.mdc index 578379c..a34de2c 100644 --- a/.cursor/rules/deploy.mdc +++ b/.cursor/rules/deploy.mdc @@ -170,7 +170,7 @@ Wait for the user to type "release" or an explicit confirmation. Any other respo ```bash gh pr create --base main --head dev \ --title "Release vX.Y.Z" \ - --body "$(cat <<'EOF' + --body-file - <<'EOF' Release vX.Y.Z PRs included: @@ -180,13 +180,14 @@ PRs included: Closes #14 Closes #15 EOF -)" ``` Note the PR number from the output. The `Closes #N` lines will auto-close the linked issues because this PR merges into `main` (the default branch). +> **Critical:** Use the unqualified `#N` form only. Never write `Closes owner/repo#N`, even for same-repo refs — GitHub's closing-keyword parser only populates `closingIssuesReferences` for the unqualified form, and the qualified form silently breaks auto-close. + --- ## Step 7 — Merge @@ -214,7 +215,7 @@ Create the release: ```bash gh release create <version-tag> \ --title "<version-tag>" \ - --notes "$(cat <<'EOF' + --notes-file - <<'EOF' ## Changes - #<issue> — <PR title> (PR #<pr-number>) @@ -222,7 +223,6 @@ gh release create <version-tag> \ **Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> EOF -)" ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. @@ -236,4 +236,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 `<url>`. Run `make deploy-prod` to ship to production." -<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: cursor | hash: 95958d2d | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: cursor | hash: 96c1037f | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.cursor/rules/start.mdc b/.cursor/rules/start.mdc index 7e2f615..56b6728 100644 --- a/.cursor/rules/start.mdc +++ b/.cursor/rules/start.mdc @@ -41,12 +41,7 @@ After parsing flags, determine the active labels in this order: 2. **Pool-based selection** — the allowed label pool is: `bug, documentation, enhancement, chore` (comma-separated). Select 1–3 labels from this pool that genuinely fit the task description and implementation approach. Do not apply labels mechanically — pick only what fits. If no pool label fits the task, fall through to step 3. - If any selected label name contains a space (e.g. `good first issue`), quote the entire `--label` value. 3. **No label / creation** — if the pool is empty or no pool label fits: - - If label creation is allowed (`false` = `true`, case-insensitive): the agent **may** create a new label before applying it: - ```bash - gh label create "<name>" --color "<hex>" --description "<short description>" - ``` - Use judgment — only create a label with clear reuse value. Do not create near-duplicates of existing pool labels. - - Otherwise (`false` = `false` or unset): omit `--label` entirely. Proceed silently; do not inform the user. + - Label creation is not allowed in this project. Omit `--label` entirely. Proceed silently; do not inform the user. **Milestone resolution (three-tier, Case A only):** @@ -99,7 +94,7 @@ Run `gh issue create` with explicit flags (do NOT open an interactive editor): ```bash gh issue create \ --title "<standalone full sentence — must make sense with no context>" \ - --body "<human-readable explanation: what the problem is, why it matters, general approach — written for a non-developer, no code or file paths>" \ + --body "<structured markdown body — see sections below>" \ --assignee @me \ [--label "<resolved labels>"] \ [--milestone "<resolved milestone>"] @@ -109,6 +104,25 @@ Resolve labels and milestone using the resolution steps in the Parsing section a - **Labels**: use the value from three-tier label resolution. If non-empty, add `--label "<value>"` to the command. If empty (no flag, empty pool, creation not allowed), omit `--label` entirely. - **Milestone**: use the value from three-tier milestone resolution. If non-empty, add `--milestone "<value>"` to the command. If empty (no flag, no config default, no open milestones), omit `--milestone` entirely. +**Body structure (required sections, in this order):** + +```markdown +## Problem to Fix +<what is broken or missing, written for a non-developer — no code or file paths> + +## Why it Matters +<the impact or motivation — who is affected and how> + +## General Approach +<high-level direction for the fix, in plain language> + +## Acceptance Criteria +- <specific, verifiable outcome> +- <another outcome> +``` + +All four sections are required. Write for a non-developer audience — no code, no file paths. Acceptance Criteria must be concrete and verifiable (not vague goals). + **Title rules:** - ✅ `Fix 'Contact Us' footer link pointing to 404 instead of /contact-us` - ❌ `Fix broken link` @@ -229,6 +243,6 @@ When done, say: **"The code is ready for review. Please run `make dev` and test - If already on a feature branch when `/start` is invoked, warn the user before creating another branch. - `gh issue create` must use `--title` and `--body` flags. Never open an interactive editor. - The issue is assigned to `@me` at creation. If you are creating a ticket on someone else's behalf, remove the assignee after creation with `gh issue edit <number> --remove-assignee @me`. -- Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `bug, documentation, enhancement, chore` → omit (or, when `false` = `true`, create). Never apply a label outside `bug, documentation, enhancement, chore` unless `false` = `true`. +- Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `bug, documentation, enhancement, chore` → omit `--label` entirely. Never apply a label outside `bug, documentation, enhancement, chore`. - Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. -<!-- generated by CodeCannon/sync.sh | skill: start | adapter: cursor | hash: 6fab1f6f | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: start | adapter: cursor | hash: 7481a231 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.cursor/rules/submit-for-review.mdc b/.cursor/rules/submit-for-review.mdc index 0645d89..7ac52cf 100644 --- a/.cursor/rules/submit-for-review.mdc +++ b/.cursor/rules/submit-for-review.mdc @@ -108,16 +108,19 @@ PR target branch: `dev` Use `Issue #<number>` as the issue reference — the issue stays open until `/deploy` promotes to `main`. +> **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. + Then create the PR with explicit title and body (never use an interactive editor): ``` -gh pr create --base <target-branch> --title "<title>" --body "$(cat <<'EOF' +gh pr create --base <target-branch> --title "<title>" --body-file - <<'EOF' <description of what changed and why> <Closes #N OR Issue #N, based on target above> EOF -)" ``` +> **Why `--body-file -` + stdin heredoc?** Claude Code's permission matcher refuses to extend any `Bash(gh:*)` allow rule over a command containing `$(...)` substitution, so `--body "$(cat <<'EOF' ...)"` triggers a Yes/No prompt on every run with no "Allow always" option. Piping the body in on stdin via heredoc redirection is not command substitution — the matcher sees a clean `gh pr create …` invocation and `Bash(gh:*)` matches. Apply this pattern to every multi-line `--body` / `--notes` in skills (`gh pr create`, `gh issue create`, `gh issue comment`, and `gh release create` all accept `-` for their `*-file` flags). + Add `--reviewer` to the `gh pr create` command above using the handles from `@sebastientaggart`. Before passing them, strip any leading `@` from each comma-separated handle (e.g. `@alice,@org/team` becomes `alice,org/team`) — the `gh` CLI requires bare usernames. If a CODEOWNERS file exists, both apply: CODEOWNERS triggers automatic review requests from GitHub; the `--reviewer` flag adds the explicitly configured handles on top. @@ -226,18 +229,17 @@ Accept: comma-separated numbers, `all`, or `none`/`skip`/empty. If the input is ``` gh issue create \ --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ - --body "$(cat <<'EOF' + [--label "<pool-selected labels>"] \ + --body-file - <<'EOF' Follow-up from PR #<merged-pr-number> — auto-proposed from the code review. **Finding:** <full finding text, prefix included> See the review comment on the PR for context. EOF -)" \ - [--label "<pool-selected labels>"] ``` -Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `bug, documentation, enhancement, chore` that genuinely fit the finding. If `bug, documentation, enhancement, chore` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and do not create new labels regardless of `false`. +Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `bug, documentation, enhancement, chore` that genuinely fit the finding. If `bug, documentation, enhancement, chore` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and never create new labels from follow-ups, even if label creation is enabled for the project. Do **not** pass `--milestone` — follow-ups are future work and should not inherit the current sprint. @@ -260,4 +262,4 @@ If a single `gh issue create` call fails, report the failure for that finding an - `/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 non-blocking findings. Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. -<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: cursor | hash: ba8cc572 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: cursor | hash: 3499993e | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.gemini/skills/deploy/SKILL.md b/.gemini/skills/deploy/SKILL.md index 820a5e7..9146f22 100644 --- a/.gemini/skills/deploy/SKILL.md +++ b/.gemini/skills/deploy/SKILL.md @@ -169,7 +169,7 @@ Wait for the user to type "release" or an explicit confirmation. Any other respo ```bash gh pr create --base main --head dev \ --title "Release vX.Y.Z" \ - --body "$(cat <<'EOF' + --body-file - <<'EOF' Release vX.Y.Z PRs included: @@ -179,13 +179,14 @@ PRs included: Closes #14 Closes #15 EOF -)" ``` Note the PR number from the output. The `Closes #N` lines will auto-close the linked issues because this PR merges into `main` (the default branch). +> **Critical:** Use the unqualified `#N` form only. Never write `Closes owner/repo#N`, even for same-repo refs — GitHub's closing-keyword parser only populates `closingIssuesReferences` for the unqualified form, and the qualified form silently breaks auto-close. + --- ## Step 7 — Merge @@ -213,7 +214,7 @@ Create the release: ```bash gh release create <version-tag> \ --title "<version-tag>" \ - --notes "$(cat <<'EOF' + --notes-file - <<'EOF' ## Changes - #<issue> — <PR title> (PR #<pr-number>) @@ -221,7 +222,6 @@ gh release create <version-tag> \ **Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> EOF -)" ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. @@ -235,4 +235,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 `<url>`. Run `make deploy-prod` to ship to production." -<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: gemini | hash: 83be1fd6 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: gemini | hash: cde00382 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.gemini/skills/start/SKILL.md b/.gemini/skills/start/SKILL.md index 74c21a3..5ad7329 100644 --- a/.gemini/skills/start/SKILL.md +++ b/.gemini/skills/start/SKILL.md @@ -40,12 +40,7 @@ After parsing flags, determine the active labels in this order: 2. **Pool-based selection** — the allowed label pool is: `bug, documentation, enhancement, chore` (comma-separated). Select 1–3 labels from this pool that genuinely fit the task description and implementation approach. Do not apply labels mechanically — pick only what fits. If no pool label fits the task, fall through to step 3. - If any selected label name contains a space (e.g. `good first issue`), quote the entire `--label` value. 3. **No label / creation** — if the pool is empty or no pool label fits: - - If label creation is allowed (`false` = `true`, case-insensitive): the agent **may** create a new label before applying it: - ```bash - gh label create "<name>" --color "<hex>" --description "<short description>" - ``` - Use judgment — only create a label with clear reuse value. Do not create near-duplicates of existing pool labels. - - Otherwise (`false` = `false` or unset): omit `--label` entirely. Proceed silently; do not inform the user. + - Label creation is not allowed in this project. Omit `--label` entirely. Proceed silently; do not inform the user. **Milestone resolution (three-tier, Case A only):** @@ -98,7 +93,7 @@ Run `gh issue create` with explicit flags (do NOT open an interactive editor): ```bash gh issue create \ --title "<standalone full sentence — must make sense with no context>" \ - --body "<human-readable explanation: what the problem is, why it matters, general approach — written for a non-developer, no code or file paths>" \ + --body "<structured markdown body — see sections below>" \ --assignee @me \ [--label "<resolved labels>"] \ [--milestone "<resolved milestone>"] @@ -108,6 +103,25 @@ Resolve labels and milestone using the resolution steps in the Parsing section a - **Labels**: use the value from three-tier label resolution. If non-empty, add `--label "<value>"` to the command. If empty (no flag, empty pool, creation not allowed), omit `--label` entirely. - **Milestone**: use the value from three-tier milestone resolution. If non-empty, add `--milestone "<value>"` to the command. If empty (no flag, no config default, no open milestones), omit `--milestone` entirely. +**Body structure (required sections, in this order):** + +```markdown +## Problem to Fix +<what is broken or missing, written for a non-developer — no code or file paths> + +## Why it Matters +<the impact or motivation — who is affected and how> + +## General Approach +<high-level direction for the fix, in plain language> + +## Acceptance Criteria +- <specific, verifiable outcome> +- <another outcome> +``` + +All four sections are required. Write for a non-developer audience — no code, no file paths. Acceptance Criteria must be concrete and verifiable (not vague goals). + **Title rules:** - ✅ `Fix 'Contact Us' footer link pointing to 404 instead of /contact-us` - ❌ `Fix broken link` @@ -228,6 +242,6 @@ When done, say: **"The code is ready for review. Please run `make dev` and test - If already on a feature branch when `/start` is invoked, warn the user before creating another branch. - `gh issue create` must use `--title` and `--body` flags. Never open an interactive editor. - The issue is assigned to `@me` at creation. If you are creating a ticket on someone else's behalf, remove the assignee after creation with `gh issue edit <number> --remove-assignee @me`. -- Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `bug, documentation, enhancement, chore` → omit (or, when `false` = `true`, create). Never apply a label outside `bug, documentation, enhancement, chore` unless `false` = `true`. +- Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `bug, documentation, enhancement, chore` → omit `--label` entirely. Never apply a label outside `bug, documentation, enhancement, chore`. - Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. -<!-- generated by CodeCannon/sync.sh | skill: start | adapter: gemini | hash: d535ea99 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: start | adapter: gemini | hash: d632050d | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.gemini/skills/submit-for-review/SKILL.md b/.gemini/skills/submit-for-review/SKILL.md index 7d074ba..fa2e5d5 100644 --- a/.gemini/skills/submit-for-review/SKILL.md +++ b/.gemini/skills/submit-for-review/SKILL.md @@ -107,16 +107,19 @@ PR target branch: `dev` Use `Issue #<number>` as the issue reference — the issue stays open until `/deploy` promotes to `main`. +> **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. + Then create the PR with explicit title and body (never use an interactive editor): ``` -gh pr create --base <target-branch> --title "<title>" --body "$(cat <<'EOF' +gh pr create --base <target-branch> --title "<title>" --body-file - <<'EOF' <description of what changed and why> <Closes #N OR Issue #N, based on target above> EOF -)" ``` +> **Why `--body-file -` + stdin heredoc?** Claude Code's permission matcher refuses to extend any `Bash(gh:*)` allow rule over a command containing `$(...)` substitution, so `--body "$(cat <<'EOF' ...)"` triggers a Yes/No prompt on every run with no "Allow always" option. Piping the body in on stdin via heredoc redirection is not command substitution — the matcher sees a clean `gh pr create …` invocation and `Bash(gh:*)` matches. Apply this pattern to every multi-line `--body` / `--notes` in skills (`gh pr create`, `gh issue create`, `gh issue comment`, and `gh release create` all accept `-` for their `*-file` flags). + Add `--reviewer` to the `gh pr create` command above using the handles from `@sebastientaggart`. Before passing them, strip any leading `@` from each comma-separated handle (e.g. `@alice,@org/team` becomes `alice,org/team`) — the `gh` CLI requires bare usernames. If a CODEOWNERS file exists, both apply: CODEOWNERS triggers automatic review requests from GitHub; the `--reviewer` flag adds the explicitly configured handles on top. @@ -225,18 +228,17 @@ Accept: comma-separated numbers, `all`, or `none`/`skip`/empty. If the input is ``` gh issue create \ --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ - --body "$(cat <<'EOF' + [--label "<pool-selected labels>"] \ + --body-file - <<'EOF' Follow-up from PR #<merged-pr-number> — auto-proposed from the code review. **Finding:** <full finding text, prefix included> See the review comment on the PR for context. EOF -)" \ - [--label "<pool-selected labels>"] ``` -Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `bug, documentation, enhancement, chore` that genuinely fit the finding. If `bug, documentation, enhancement, chore` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and do not create new labels regardless of `false`. +Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `bug, documentation, enhancement, chore` that genuinely fit the finding. If `bug, documentation, enhancement, chore` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and never create new labels from follow-ups, even if label creation is enabled for the project. Do **not** pass `--milestone` — follow-ups are future work and should not inherit the current sprint. @@ -259,4 +261,4 @@ If a single `gh issue create` call fails, report the failure for that finding an - `/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 non-blocking findings. Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. -<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: gemini | hash: ad336e6c | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: gemini | hash: 56d02482 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/VERSION b/VERSION index 2080591..1d0ba9e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.11 +0.4.0 diff --git a/skills/deploy.md b/skills/deploy.md index de71d27..a0c21c4 100644 --- a/skills/deploy.md +++ b/skills/deploy.md @@ -242,7 +242,7 @@ The version tag and PR/issue list are already known. If no previous tag exists, ```bash gh release create <version-tag> \ --title "<version-tag>" \ - --notes "$(cat <<'EOF' + --notes-file - <<'EOF' ## Changes - #<issue> — <PR title> (PR #<pr-number>) @@ -250,7 +250,6 @@ gh release create <version-tag> \ **Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> EOF -)" ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, use just the PR title. @@ -272,7 +271,7 @@ Tell the user: ```bash gh pr create --base {{BRANCH_PROD}} --head {{BRANCH_DEV}} \ --title "Release vX.Y.Z" \ - --body "$(cat <<'EOF' + --body-file - <<'EOF' Release vX.Y.Z PRs included: @@ -282,13 +281,14 @@ PRs included: Closes #14 Closes #15 EOF -)" ``` Note the PR number from the output. The `Closes #N` lines will auto-close the linked issues because this PR merges into `{{BRANCH_PROD}}` (the default branch). +> **Critical:** Use the unqualified `#N` form only. Never write `Closes owner/repo#N`, even for same-repo refs — GitHub's closing-keyword parser only populates `closingIssuesReferences` for the unqualified form, and the qualified form silently breaks auto-close. + --- ## Step 7 — Merge @@ -316,7 +316,7 @@ Create the release: ```bash gh release create <version-tag> \ --title "<version-tag>" \ - --notes "$(cat <<'EOF' + --notes-file - <<'EOF' ## Changes - #<issue> — <PR title> (PR #<pr-number>) @@ -324,7 +324,6 @@ gh release create <version-tag> \ **Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> EOF -)" ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. @@ -345,7 +344,7 @@ Tell the user: ```bash gh pr create --base {{BRANCH_PROD}} --head {{BRANCH_TEST}} \ --title "Release vX.Y.Z" \ - --body "$(cat <<'EOF' + --body-file - <<'EOF' Release vX.Y.Z PRs included: @@ -355,13 +354,14 @@ PRs included: Closes #14 Closes #15 EOF -)" ``` Note the PR number from the output. The `Closes #N` lines will auto-close the linked issues because this PR merges into `{{BRANCH_PROD}}` (the default branch). +> **Critical:** Use the unqualified `#N` form only. Never write `Closes owner/repo#N`, even for same-repo refs — GitHub's closing-keyword parser only populates `closingIssuesReferences` for the unqualified form, and the qualified form silently breaks auto-close. + --- ## Step 7 — Merge @@ -389,7 +389,7 @@ Create the release: ```bash gh release create <version-tag> \ --title "<version-tag>" \ - --notes "$(cat <<'EOF' + --notes-file - <<'EOF' ## Changes - #<issue> — <PR title> (PR #<pr-number>) @@ -397,7 +397,6 @@ gh release create <version-tag> \ **Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> EOF -)" ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. diff --git a/skills/start.md b/skills/start.md index 3003ca3..0f72dc6 100644 --- a/skills/start.md +++ b/skills/start.md @@ -48,12 +48,16 @@ After parsing flags, determine the active labels in this order: 2. **Pool-based selection** — no label pool is configured. Fall through to step 3. {{/if}} 3. **No label / creation** — if the pool is empty or no pool label fits: - - If label creation is allowed (`{{TICKET_LABEL_CREATION_ALLOWED}}` = `true`, case-insensitive): the agent **may** create a new label before applying it: +{{#if TICKET_LABEL_CREATION_ALLOWED}} + - Label creation is allowed in this project. The agent **may** create a new label before applying it: ```bash gh label create "<name>" --color "<hex>" --description "<short description>" ``` Use judgment — only create a label with clear reuse value. Do not create near-duplicates of existing pool labels. - - Otherwise (`{{TICKET_LABEL_CREATION_ALLOWED}}` = `false` or unset): omit `--label` entirely. Proceed silently; do not inform the user. +{{/if}} +{{#if !TICKET_LABEL_CREATION_ALLOWED}} + - Label creation is not allowed in this project. Omit `--label` entirely. Proceed silently; do not inform the user. +{{/if}} {{#if !TICKET_LABELS}} > **Tip:** Run `/setup` to populate TICKET_LABELS from your repo's existing GitHub labels. @@ -132,7 +136,7 @@ Run `gh issue create` with explicit flags (do NOT open an interactive editor): ```bash gh issue create \ --title "<standalone full sentence — must make sense with no context>" \ - --body "<human-readable explanation: what the problem is, why it matters, general approach — written for a non-developer, no code or file paths>" \ + --body "<structured markdown body — see sections below>" \ --assignee @me \ [--label "<resolved labels>"] \ [--milestone "<resolved milestone>"] @@ -142,6 +146,25 @@ Resolve labels and milestone using the resolution steps in the Parsing section a - **Labels**: use the value from three-tier label resolution. If non-empty, add `--label "<value>"` to the command. If empty (no flag, empty pool, creation not allowed), omit `--label` entirely. - **Milestone**: use the value from three-tier milestone resolution. If non-empty, add `--milestone "<value>"` to the command. If empty (no flag, no config default, no open milestones), omit `--milestone` entirely. +**Body structure (required sections, in this order):** + +```markdown +## Problem to Fix +<what is broken or missing, written for a non-developer — no code or file paths> + +## Why it Matters +<the impact or motivation — who is affected and how> + +## General Approach +<high-level direction for the fix, in plain language> + +## Acceptance Criteria +- <specific, verifiable outcome> +- <another outcome> +``` + +All four sections are required. Write for a non-developer audience — no code, no file paths. Acceptance Criteria must be concrete and verifiable (not vague goals). + **Title rules:** - ✅ `Fix 'Contact Us' footer link pointing to 404 instead of /contact-us` - ❌ `Fix broken link` @@ -295,7 +318,12 @@ When done, say: **"The code is ready for review. Please run `{{DEV_CMD}}` and te - `gh issue create` must use `--title` and `--body` flags. Never open an interactive editor. - The issue is assigned to `@me` at creation. If you are creating a ticket on someone else's behalf, remove the assignee after creation with `gh issue edit <number> --remove-assignee @me`. {{#if TICKET_LABELS}} -- Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `{{TICKET_LABELS}}` → omit (or, when `{{TICKET_LABEL_CREATION_ALLOWED}}` = `true`, create). Never apply a label outside `{{TICKET_LABELS}}` unless `{{TICKET_LABEL_CREATION_ALLOWED}}` = `true`. +{{#if TICKET_LABEL_CREATION_ALLOWED}} +- Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `{{TICKET_LABELS}}` → create a new label when nothing in the pool fits. Labels outside `{{TICKET_LABELS}}` may only be created when no pool label is a good fit. +{{/if}} +{{#if !TICKET_LABEL_CREATION_ALLOWED}} +- Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `{{TICKET_LABELS}}` → omit `--label` entirely. Never apply a label outside `{{TICKET_LABELS}}`. +{{/if}} {{/if}} {{#if !TICKET_LABELS}} - Apply labels only when explicitly provided via `--label`. No label pool is configured. diff --git a/skills/submit-for-review.md b/skills/submit-for-review.md index 92a8ce4..d598aec 100644 --- a/skills/submit-for-review.md +++ b/skills/submit-for-review.md @@ -124,16 +124,19 @@ PR target branch: `{{BRANCH_PROD}}` (trunk mode) Use `Closes #<number>` as the issue reference — merging to the default branch will auto-close the issue. {{/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. + Then create the PR with explicit title and body (never use an interactive editor): ``` -gh pr create --base <target-branch> --title "<title>" --body "$(cat <<'EOF' +gh pr create --base <target-branch> --title "<title>" --body-file - <<'EOF' <description of what changed and why> <Closes #N OR Issue #N, based on target above> EOF -)" ``` +> **Why `--body-file -` + stdin heredoc?** Claude Code's permission matcher refuses to extend any `Bash(gh:*)` allow rule over a command containing `$(...)` substitution, so `--body "$(cat <<'EOF' ...)"` triggers a Yes/No prompt on every run with no "Allow always" option. Piping the body in on stdin via heredoc redirection is not command substitution — the matcher sees a clean `gh pr create …` invocation and `Bash(gh:*)` matches. Apply this pattern to every multi-line `--body` / `--notes` in skills (`gh pr create`, `gh issue create`, `gh issue comment`, and `gh release create` all accept `-` for their `*-file` flags). + {{#if DEFAULT_REVIEWERS}} Add `--reviewer` to the `gh pr create` command above using the handles from `{{DEFAULT_REVIEWERS}}`. Before passing them, strip any leading `@` from each comma-separated handle (e.g. `@alice,@org/team` becomes `alice,org/team`) — the `gh` CLI requires bare usernames. @@ -265,18 +268,17 @@ Accept: comma-separated numbers, `all`, or `none`/`skip`/empty. If the input is ``` gh issue create \ --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ - --body "$(cat <<'EOF' + [--label "<pool-selected labels>"] \ + --body-file - <<'EOF' Follow-up from PR #<merged-pr-number> — auto-proposed from the code review. **Finding:** <full finding text, prefix included> See the review comment on the PR for context. EOF -)" \ - [--label "<pool-selected labels>"] ``` -Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `{{TICKET_LABELS}}` that genuinely fit the finding. If `{{TICKET_LABELS}}` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and do not create new labels regardless of `{{TICKET_LABEL_CREATION_ALLOWED}}`. +Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `{{TICKET_LABELS}}` that genuinely fit the finding. If `{{TICKET_LABELS}}` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and never create new labels from follow-ups, even if label creation is enabled for the project. Do **not** pass `--milestone` — follow-ups are future work and should not inherit the current sprint. diff --git a/sync.sh b/sync.sh index 253ddb8..af6e906 100755 --- a/sync.sh +++ b/sync.sh @@ -146,7 +146,11 @@ def apply_conditionals(text, values): m = _IF_OPEN.match(lines[open_idx]) negated = m.group(1) == '!' key = m.group(2) - truthy = bool(values.get(key, '').strip()) + raw_value = values.get(key, '').strip() + # Empty string and boolean-like falsy strings ('false', 'no', '0') + # are treated as falsy so config values like 'false' behave intuitively + # in conditional blocks. + truthy = bool(raw_value) and raw_value.lower() not in ('false', 'no', '0') keep = (not truthy) if negated else truthy if keep: @@ -347,10 +351,11 @@ def validate_placeholders(skill_files, project_config): for skill_path in skill_files: raw = skill_path.read_text() fm, body = parse_frontmatter(raw) - # Check body and frontmatter description - text_to_check = body + # Evaluate conditionals first — placeholders inside stripped blocks + # are not part of the final output and should not be reported. + text_to_check = apply_conditionals(body, project_config) if fm.get('description'): - text_to_check += '\n' + fm['description'] + text_to_check += '\n' + apply_conditionals(fm['description'], project_config) missing = [p for p in find_unresolved(text_to_check) if p not in project_config] for p in missing: errors.append(f" {skill_path.name}: {{{{{p}}}}} not defined in config")