diff --git a/plugins/stewards/config.test.ts b/plugins/stewards/config.test.ts index bceaf3e..d771947 100644 --- a/plugins/stewards/config.test.ts +++ b/plugins/stewards/config.test.ts @@ -516,9 +516,16 @@ describe("the GitHub issue playbook (BB-234)", () => { expect(parsed.value).toMatchObject({ kind: "ticket", source: "github" }); expect(Object.keys(parsed.value.phases)).toEqual(["open", "status:todo", "status:in-progress", "status:in-review"]); expect(parsed.value.terminal).toEqual(["closed", "closed (completed)", "closed (not planned)", "closed (duplicate)"]); - // No sub-issue handling: create-sub-issue is not in `may`, and there is no promote rule. - expect(parsed.value.may).not.toContain("create-sub-issue"); - expect(parsed.value.promote).toBeNull(); + // GitHub sub-issues (BB-250): create-sub-issue is in `may`, the promote rule is set, + // and the status:todo phase carries the attach commands. + expect(parsed.value.may).toContain("create-sub-issue"); + expect(parsed.value.promote).toBe("A sub-issue that needs more than one pull request, its own approval, or work in a repo the parent does not touch."); + const todo = phaseInstructions(parsed.value, "status:todo")!; + expect(todo).toContain("sub_issues"); + // The child is assigned to this server's login, so a later `bb-managed` promotion can adopt it, + // and its number is printed so the steward can name it and re-attach it later. + expect(todo).toContain("--assignee @me"); + expect(todo).toContain('echo "filed child #$n"'); expect(phaseInstructions(parsed.value, "status:in-review")).toMatch(/gh issue\s+close/u); expect(phaseInstructions(parsed.value, "open")).toMatch(/Add `status:todo`/u); expect(phaseInstructions(parsed.value, "status:in-progress")).toContain("no `Fixes #N`"); diff --git a/plugins/stewards/skill.test.ts b/plugins/stewards/skill.test.ts index 339f8ac..a2a0915 100644 --- a/plugins/stewards/skill.test.ts +++ b/plugins/stewards/skill.test.ts @@ -46,3 +46,18 @@ describe("the shipped pr-reviewer skill's product ack (BB-207)", () => { expect(skill).toMatch(/product ack is of the pull request too/u); }); }); + +// BB-250: a GitHub ticket steward files a child as a native sub-issue, so the +// create command assigns it to this server's login (a GitHub issue is adopted +// only when the login is an assignee) and the attach uses the REST endpoint. +const stewardsSkill = readFileSync(fileURLToPath(new URL("./skills/stewards/SKILL.md", import.meta.url)), "utf8"); + +describe("the shipped stewards skill's GitHub child filing (BB-250)", () => { + it("carries the create-and-attach commands and assigns the child to this server's login", () => { + expect(stewardsSkill).toContain("### Filing a child on GitHub"); + expect(stewardsSkill).toContain("repos/{owner}/{repo}/issues/{parent}/sub_issues"); + expect(stewardsSkill).toContain("--assignee @me"); + expect(stewardsSkill).toContain('echo "filed child #$n"'); + expect(stewardsSkill).toContain("-F sub_issue_id="); + }); +}); diff --git a/plugins/stewards/skills/stewards/SKILL.md b/plugins/stewards/skills/stewards/SKILL.md index bcc4527..dd940ab 100644 --- a/plugins/stewards/skills/stewards/SKILL.md +++ b/plugins/stewards/skills/stewards/SKILL.md @@ -318,6 +318,27 @@ Two tools are yours; everything else you do with the tools every thread has. reaches it. A report that needs nobody is marked read, so the sidebar's unread dot means "needs you"; a report that needs you is left unread. +### Filing a child on GitHub + +A GitHub child is attached as a native sub-issue of its parent in the same +action, so the parent's snapshot lists it. Create the child assigned to this +server's login (so a later `bb-managed` promotion can adopt it), keep its +number, and attach it, keeping a `Part of #{parent}` line in the body for a +reader: + +``` +n=$(gh issue create --repo {owner}/{repo} --title "…" --body "Part of #{parent} + +…" --assignee @me | grep -oE '[0-9]+$') +echo "filed child #$n" +gh api -X POST repos/{owner}/{repo}/issues/{parent}/sub_issues -F sub_issue_id=$(gh api repos/{owner}/{repo}/issues/$n --jq .id) +``` + +`echo` prints the child's number. `-F` sends the numeric id as an integer. A +child missing from the parent's next wake under Sub-issues is re-attached +with the attach command, using the child's number in place of `$n`, not +re-filed. + ## If you are an ops steward You watch one production target; the wake is the plugin's look at it (for diff --git a/plugins/stewards/templates.ts b/plugins/stewards/templates.ts index 48ef44b..cfdae58 100644 --- a/plugins/stewards/templates.ts +++ b/plugins/stewards/templates.ts @@ -178,7 +178,8 @@ ready: - public API or contract change - estimate >= 3 approval: project -may: [comment, edit-description, set-state, set-labels, open-pr, request-review, spawn-worker] +promote: "A sub-issue that needs more than one pull request, its own approval, or work in a repo the parent does not touch." +may: [comment, edit-description, set-state, set-labels, create-sub-issue, open-pr, request-review, spawn-worker] models: steward: claude/claude-sonnet-5-5/medium worker: claude/claude-opus-5-5/high @@ -244,8 +245,24 @@ move the issue to \`status:in-progress\` (remove \`status:todo\`, add \`status:in-progress\`). A new spec needs approval again, and is asked for the same way. -A GitHub issue has no sub-issues here: do the work in this binding, and open -one pull request per slice if it needs several. +A GitHub issue can have native sub-issues, so file a child as a real +sub-issue of its parent in the same action, keeping a \`Part of #{parent}\` +line in the body for a reader. Create the child assigned to this server's +login (so a later \`bb-managed\` promotion can adopt it), keep its number, and +attach it: + +\`\`\` +n=$(gh issue create --repo {owner}/{repo} --title "…" --body "Part of #{parent} + +…" --assignee @me | grep -oE '[0-9]+$') +echo "filed child #$n" +gh api -X POST repos/{owner}/{repo}/issues/{parent}/sub_issues -F sub_issue_id=$(gh api repos/{owner}/{repo}/issues/$n --jq .id) +\`\`\` + +\`echo\` prints the child's number. \`-F\` sends the numeric id as an +integer. A child missing from the parent's next wake under Sub-issues is +re-attached with the attach command, using the child's number in place of +\`$n\`, not re-filed. ## status:in-progress