Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
74 changes: 58 additions & 16 deletions .claude/skills/backlog/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,28 @@ Open an individual issue only when you are deciding about that issue.

## Prerequisites

Board reads need `PROJECT_NUMBER` set and the board created (deferred —
`scripts/backlog-board-init.sh`). Board writes need `BESS_PO_TOKEN` with
`project` scope (also deferred). Until both exist, a pass fails loudly at the
first board access in `backlog-digest.sh` — that failure is expected, not a
bug to route around.

**One thing to verify the first time a board exists.** No board has ever
existed, so the JSON shape `gh project item-list --format json` uses for a
custom field is unconfirmed; the digest assumes each item carries a top-level
`.priority`. On the first real run, check that `priority` is populated rather
than `null` for an item you have set a priority on. If it is `null`, fix the
jq path in `scripts/backlog-digest.sh` — do not add a fallback that tries
several shapes. A silently-null `priority` disables ranking axis 2 without
any error.
The board exists: **Project #1, "BESS Manager Backlog"**. `PROJECT_NUMBER=1`
lives in the repo's `.env`, which `backlog-digest.sh` does **not** source — so
export it first, or the digest exits with a "board has not been created yet"
message that is misleading rather than wrong:

set -a; . ./.env; set +a; ./scripts/backlog-digest.sh

Board writes need `BESS_PO_TOKEN` with `project` scope. The custom-field JSON
shape is confirmed: `gh project item-list --format json` puts each single-select
value at the item's top level, so `.priority` and `.awaiting` both read directly.

Field options, as they actually exist — do not invent values outside these sets:

| Field | Options |
|---|---|
| `Status` | `Backlog`, `Analysis`, `Ready for Dev`, `In Progress`, `In Review`, `Done` |
| `Priority` | `P1`, `P2`, `P3`, `P4` — **there is no `P0`** |
| `Awaiting` | `reporter`, `discussion`, `upstream`, `analysis` |
| `Source` | `issue`, `TODO` |

The digest's `column` values match `Status` exactly, so reconciling a card is a
string comparison, not a translation.

## When to Use

Expand All @@ -54,6 +62,28 @@ Post as the PO identity: `scripts/gh-agent.sh --as po issue comment ...`.
If a board write fails for missing scope, stop and report
`gh auth refresh -s project`. Never fall back to a file.

## What the digest tells you, and why each field exists

Four fields carry the grooming signal. Each was added because its absence
caused a real misclassification:

| Field | Meaning |
|---|---|
| `awaiting` | The blocking wait. **Outranks `analyzed`** — an item whose scope is unsettled is in *Analysis* no matter how far it got. Comes from the board field when set, else from a blocking label. |
| `awaiting_source` | `board` or `label`. A `label` source with no board value is a triage action: set the field. |
| `awaiting_suggested` | What the labels imply, so an unset field can be reconciled without guessing. |
| `last_comment` | `{author, days, is_reporter, is_bot}`. **The reporter-replied signal.** A comment count and a last-activity date cannot tell "the reporter answered us" from "we posted a nudge", which is why the follow-up chase never fired. |
| `stale_worktree` | The worktree's own branch has already merged, so it is rot, not progress. Hand it to `sweep-prs`. |
| `blocked` | `blocked` label or an unresolved `Blocked by #N`. Fails Ready outright. |

**A human comment is not a wait.** `awaiting: discussion` used to be returned
for any human comment, which pushed items to *Analysis* for ordinary traffic —
thanks, a "me too", a follow-up question. Only a recorded wait or a blocking
label does that now; `last_comment` is what you read to judge the rest.

**`Ready for Dev` requires a `Priority`.** An analysed item with no priority is
un-ranked, so it cannot be "next" — it stays in *Analysis* as a triage action.

## Definition of Ready

Nothing is dispatched that has not crossed this line. A bug is Ready when:
Expand All @@ -64,6 +94,16 @@ Nothing is dispatched that has not crossed this line. A bug is Ready when:
4. An approach is agreed (Stage 2 analysis, or the maintainer's say-so)
5. No unresolved blocker

**Criterion 4 is the one that gets skipped, and the `analyzed` label is not
proof of it.** Stage 2 can diagnose a request correctly and still leave its
design open — #96 was labelled `analyzed`, prioritised `P2`, carried no blocking
label, and was still not implementable, because *how* to build it was undecided.
It reported *Ready*, an implementation session was dispatched at it, and that
session deadlocked on three design questions nobody was there to answer. When
an approach is genuinely undecided, record it: set `Awaiting` and post the open
questions on the issue, so a later attempt can rehydrate them rather than
rediscover them.

An enhancement is Ready when 3–5 hold and the user-visible outcome is stated.
An item failing any criterion stays in Backlog or Analysis and becomes your
follow-up, not a developer's problem.
Expand All @@ -90,8 +130,10 @@ always wins** — never trust a card's current position. Act on each mismatch:

| Mismatch | Action |
|---|---|
| card *In progress*, no worktree, no PR | abandoned — move to *Ready*, report it |
| worktree present, no session, no PR | the session died mid-issue. Report it and offer to relaunch; the branch's commits survive. **Never silently relaunch** — a session that died twice is telling you something |
| card *In Progress*, no worktree, no PR | abandoned — move to *Ready for Dev*, report it |
| `stale_worktree: true` | the branch already merged; the worktree is rot, not work. Hand to `sweep-prs`, and do not read it as progress |
| worktree present, no session, no PR | the session died mid-issue. The branch's commits survive — resuming is `/implement-issue <n>`, whose Step 0 detects the prior work and re-enters at the right step. **Never silently relaunch**: a session that died twice is telling you something, and a background dispatch that reports `working` may have written nothing at all — verify by work product (`git -C <wt> log`, file mtimes), never by session state |
| `last_comment.is_reporter` and `awaiting: reporter` | the reporter answered. Re-check the Definition of Ready — this wait may be satisfied, and it is the transition nothing used to notice |
| PR `CONFLICTING` | hand to `sweep-prs` |
| worktree whose PR merged | prune via `sweep-prs` |
| issue closed, card not *Done* | move the card |
Expand Down
Loading
Loading