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
15 changes: 14 additions & 1 deletion .claude/skills/backlog/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,20 @@ Serialise, do not stack:
## Autonomous spend

Exactly one action costs money without asking: firing Stage 2
(`@claude-bot analyze`, ~$0.50–2) on an item entering Analysis that meets the
(`@claude-bot analyze`, ~$0.50–2). **Post it as the PO, never as the
maintainer:**

scripts/gh-agent.sh --as po issue comment <n> --body "@claude-bot analyze"

`issue-analyze.yml`'s actor gate accepts `bess-product-owner` for exactly this
trigger. It did not always, and the mismatch had a real cost: the rule
authorised the PO to spend while the gate accepted only the repo owner, so the
trigger went out as the maintainer — putting their name on comments they never
wrote, and hiding which decisions were the agent's. An automation decision
carries the automation's face. If this ever fails the gate, **that is the
finding** — report it; do not route around it with plain `gh`.

It fires on an item entering Analysis that meets the
tier-1 bar from `Verb: next` directly — labelled `bug`, opened by someone
other than the maintainer, with its debug log attached — **and that has no
prior `@claude-bot analyze` comment already on the issue**. Check this by
Expand Down
21 changes: 20 additions & 1 deletion .github/workflows/issue-analyze.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,27 @@ on:
jobs:
analyze:
name: Deep Analysis
# The Product Owner agent can fire this, not only the repo owner.
#
# `backlog`'s Autonomous spend rule already authorises exactly this: Stage 2
# on an item entering Analysis that is labelled `bug`, opened by someone
# other than the maintainer, carries a debug log, and has no prior analyze
# comment. The rule existed while this gate did not accept the account that
# enforces it, so the only way to obey both was to post the trigger as the
# maintainer -- which put the maintainers name on comments they never wrote
# and hid which decisions were the agents.
#
# Stages 3 and 5 stay OWNER-ONLY and must not copy this. Their spend
# ($1-4 and $2-10) is not authorised by any autonomous rule, so nothing
# would be enforcing the bar on the other side of the gate.
#
# Safe to name here because `bess-product-owner` is a REGISTERED account
# and a collaborator on this repo. Never pre-authorise a username that does
# not exist yet: on a public repo anyone could claim it (see CLAUDE.md on
# `bess-developer`).
if: |
github.event.comment.user.login == github.repository_owner &&
(github.event.comment.user.login == github.repository_owner ||
github.event.comment.user.login == 'bess-product-owner') &&
github.event.issue.pull_request == null &&
contains(github.event.comment.body, '@claude-bot analyze')
runs-on: ubuntu-latest
Expand Down
33 changes: 24 additions & 9 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,30 @@ of `analyzed`.

### General bot rules

- Only the repo owner can trigger bot commands. The one exception is Stage 4:
`pr-review.yml` also accepts `@claude-bot` from the developer automation
identity (currently the `bess-agent` GitHub account; being renamed to
`bess-developer` — see `scripts/gh-agent.sh`), so `implement-issue`'s
Step 11 loop can request its own review. `bess-developer` is added to that
gate only in the same commit that renames the account — never before,
since pre-authorising an unregistered username on a public repo is
exploitable. Stages 1–3 and 5 stay owner-only — those spend money on work
nobody has asked for yet.
- Bot commands are owner-triggered, with **two** exceptions, each matching an
account that a documented rule already makes responsible for that spend:
- **Stage 4** — `pr-review.yml` accepts `@claude-bot` from the developer
identity (currently `bess-agent`; being renamed to `bess-developer` — see
`scripts/gh-agent.sh`), so `implement-issue`'s Step 11 loop can request its
own review.
- **Stage 2** — `issue-analyze.yml` accepts `@claude-bot analyze` from
`bess-product-owner`, because `backlog`'s Autonomous spend rule authorises
exactly that trigger and bounds it (labelled `bug`, external reporter,
debug log attached, no prior analyze). The rule existed before the gate
accepted the account enforcing it, so the only way to satisfy both was to
post as the maintainer — putting their name on comments they never wrote
and hiding which decisions were the agent's. An automation decision should
carry the automation's face.

**Stages 1, 3 and 5 stay owner-only, and must not copy this.** Their spend
($1–4 and $2–10) is authorised by no autonomous rule, so nothing would be
enforcing a bar on the other side of the gate.

Only ever name a **registered** account in a gate. `bess-developer` is added
in the same commit that renames the account, never before: on a public repo
anyone can claim an unregistered username, so pre-authorising one is
exploitable. `bess-product-owner` is named above because it exists today and
is a collaborator.
- Automation writes carry a **role** identity, and role is the axis:
`bess-product-owner` (intake, backlog, board, reporter comments),
`bess-developer` (analyze, fix, PR authorship, requesting review),
Expand Down
Loading