Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions plugins/stewards/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`");
Expand Down
15 changes: 15 additions & 0 deletions plugins/stewards/skill.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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=");
});
});
21 changes: 21 additions & 0 deletions plugins/stewards/skills/stewards/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 20 additions & 3 deletions plugins/stewards/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
Loading