Skip to content
Merged
2 changes: 1 addition & 1 deletion plugins/work-items/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "work-items",
"version": "0.24.7",
"version": "0.25.0",
"description": "Manages development work items through a provider-neutral tracker seam that ships with the plugin (bundled dispatcher plus github and local-markdown adapters; seam plugin-dir canonical, adapters consumer-local-first): dashboard, taxonomy-labeled creation, a race-safe assignee-plus-lease claim protocol, recurring-schedule checks, TODO scanning, stale-lease auditing, plan decomposition into vertical-slice items, raw-intake triage (issues and unsolicited PRs through raw, verified, briefed, autonomous-eligible states), plus the two work-items loop lanes of the loop-lane convention: a self-paced autonomous work-loop drain (work-class admission gate, adaptive item cap, PR-only) and an attended attend-queue escalation lane. The re-runnable setup skill binds the provider (.work-item-tracker.json), seeds the recurring-schedule seam (.github/recurring-schedule.json), and remaps canonical role labels.",
"author": {
"name": "Melodic Software",
Expand Down
14 changes: 14 additions & 0 deletions plugins/work-items/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@
All notable changes to the `work-items` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.25.0]

### Added

- **Brief-before-ask requirement in the interactive gates (`#1202`).** `triage`'s interactive
direction gate (both the initial recommendation and each step-4 interview question) and
`attend-queue`'s row-working loop (`[intake]`, `[escalated]`, `[ratify]` rows) now require
restating, before any operator-facing decision question, (1) which item, (2) the decision being
asked, and (3) the consequence of each option **presented** — an open-ended question, which has no
option set to enumerate, states what the answer will determine instead of being narrowed into a
closed list to satisfy the restatement. Previously an operator could be asked to decide with only
option labels and no restated item context, forcing them to halt the pass and ask "what issue are
you looking at."

## [0.24.7]

### Fixed
Expand Down
33 changes: 22 additions & 11 deletions plugins/work-items/skills/attend-queue/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,29 @@ ratifications unblock the waiting worker loop).

## Working the queue

**Brief before asking.** This lane works rows across many items in one pass, so the operator's
context from the previous row never carries over. Before any operator-facing decision question in
this loop — an `[intake]` recommendation, an `[escalated]` question, or a `[ratify]` prompt —
restate (1) which item (number + one-line title), (2) the decision being asked, and (3) the
consequence of each option **you present**, then ask. An open-ended question presents no option set
to enumerate consequences for — state instead what the answer will determine, and never narrow a
genuinely open question into a closed list just to satisfy the restatement. A terse output style
must never compress this restatement away; the row's context is precisely what the operator needs to
answer without stopping the pass to ask which item is in front of them.

- **`[intake]` rows** — run `/work-items:triage <number>`. The operator is present, so triage's
**interactive** direction gate applies: recommend, wait for direction, then mutate. All triage
machinery (states, outcomes, briefs, closing invariant) is owned there.
- **`[escalated]` rows** — read the machine-marked comment for the escalated question, then drive
it to a decision with `/planning:interview` (when the `planning` plugin is installed; otherwise
ask the focused questions inline, one at a time, most load-bearing first — the same fallback
shape triage's interview step uses). **Write the answer back as an issue comment** on the item —
the decision lives on the tracker, never only in the session — replying in the thread of the
escalation comment where the provider supports it.
- **`[ratify]` rows** — present the classification and the intended dispatch from the marker
comment. On operator ratification, record it as a reply comment and flip the item per the rule
below; on decline, leave it human-gated and record the rationale as a comment.
**interactive** direction gate applies: brief before asking, recommend, wait for direction, then
mutate. All triage machinery (states, outcomes, briefs, closing invariant) is owned there.
- **`[escalated]` rows** — read the machine-marked comment for the escalated question, restate the
brief above, then drive it to a decision with `/planning:interview` (when the `planning` plugin is
installed; otherwise ask the focused questions inline, one at a time, most load-bearing first — the
same fallback shape triage's interview step uses). **Write the answer back as an issue comment** on
the item — the decision lives on the tracker, never only in the session — replying in the thread of
the escalation comment where the provider supports it.
- **`[ratify]` rows** — restate the brief above, then present the classification and the intended
dispatch from the marker comment and the consequence of ratifying versus declining. On operator
ratification, record it as a reply comment and flip the item per the rule below; on decline, leave
it human-gated and record the rationale as a comment.
- **Flip to agent-ready.** When an answer or ratification removes the human blocker, apply the
autonomous-eligible role label and remove the human-gated role label **in the same edit** (both
resolved from `config.role_labels`, never literals) — an item wearing both roles is a
Expand Down
15 changes: 15 additions & 0 deletions plugins/work-items/skills/attend-queue/evals/evals.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@
"Prefixes agent-authored comments with the AI disclaimer",
"Never merges a PR and never executes a work item from this lane"
]
},
{
"id": 3,
"name": "attend-queue-briefs-before-asking-each-row",
"prompt": "/work-items:attend-queue\n\nThe operator works two rows back to back in the same pass.\n\nRow 1 — [escalated] #418 \"Uploader retries transient S3 5xx forever\". The worker's escalation marker asks which retry strategy to keep: (a) capped exponential backoff, 5 attempts then dead-letter — bounded worst-case latency, but a long outage drops uploads to the dead-letter queue; (b) unbounded backoff with a 30-minute ceiling — no upload is ever dropped, but a sustained outage holds worker slots indefinitely. The operator answers: \"take (a), the capped backoff.\"\n\nRow 2 — [ratify] #503 \"Digest CLI crashes on an empty transcript file\". The marker classifies it C3 bug-fix-shaped and states the intended dispatch: the worker loop picks it up autonomously on the next cycle and opens a PR. Ratifying admits it to the autonomous lane; declining leaves it human-gated with the rationale recorded. The operator answers: \"ratified.\"",
"expected_output": "Before posing the escalated question, restates which item it concerns (#418 plus its one-line title), the decision being asked (which retry strategy to keep), and the consequence of each of the two presented options as the marker states them — then poses the question and waits, rather than answering it itself. Only after the operator's supplied answer arrives does it write that answer back and move on. Moving to the very next row, the same restatement happens again for the ratify prompt — #503 plus its title, that the decision is whether to ratify the C3 admission, and what ratifying versus declining causes — the operator's context from the escalated row is never assumed to carry over, even though it was the immediately preceding row in the same pass. Neither restatement is dropped or abbreviated for brevity, and neither invents detail the markers do not supply. The ratification is recorded only after the operator's \"ratified\" response, never presumed from the prompt's framing.",
"files": [],
"expectations": [
"Restates item identity (#418 + title), the decision being asked, and the consequence of each option before posing the escalated question",
"Restates only the consequences the escalation and ratify markers actually supply — no invented options, outcomes, or placeholder text",
"Repeats the same full restatement for the very next row (#503, the ratify prompt, including the intended dispatch and the ratify-versus-decline consequence) rather than assuming context carries over from the prior row in the same pass",
"Does not compress or drop the restatement for either row even under a terse output style",
"Still performs the row's underlying mechanics (interview/write-back for escalated, ratify-or-decline recording for ratify) after the restatement — the brief precedes the ask, it does not replace it",
Comment thread
kyle-sexton marked this conversation as resolved.
"Waits for the operator's answer on #418 before advancing to #503, and for the ratify-or-decline response on #503 before recording it — each supplied answer is what unblocks the next step, never a self-answered question"
]
}
]
}
4 changes: 2 additions & 2 deletions plugins/work-items/skills/triage/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Classify **bug vs enhancement** first — it steers the rest of the flow (bugs g

**Direction gate.** Recommending is read-only; the gate governs *mutation* — labels, comments, closes, item creation — and which side of it you are on is fixed by how triage was invoked:

- **Interactive session** — a human operator is present and no standing lane rules were supplied. Present the recommendation and **wait for the user's explicit direction** before mutating anything. This is the default whenever the invocation carries no autonomous mandate.
- **Interactive session** — a human operator is present and no standing lane rules were supplied. **Brief before asking**: before presenting the recommendation, restate (1) which item (number + one-line title), (2) the decision being asked, and (3) the consequence of each option **you present** — the recommendation and the alternatives you are actually putting to the operator, not every target state the state machine admits — then present the recommendation and **wait for the user's explicit direction** before mutating anything. This is the default whenever the invocation carries no autonomous mandate. The restatement is not optional compression fodder: a terse output style must never drop it, and it applies on every decision question, not only the first one of a pass — the operator working several rows in sequence (e.g. via `/work-items:attend-queue`) cannot be assumed to still be holding a prior item's context.
- **Autonomous lane** — triage is running unattended as a `/loop` or `/schedule` AFK session whose standing rules — the directive supplied with its `/loop` / `/schedule` invocation — already authorize triage mutations. Those standing rules **are** the direction this gate requires: treat the gate as satisfied and proceed through verification and outcome without a human turn, prefixing every comment and item you create with the AI disclaimer. There is no operator turn to wait for, so blocking here would deadlock the lane — the gate is met by the lane's mandate, not skipped.

The autonomous branch is the mode the AI disclaimer already anticipates: a session that mutates without a human turn. The two are one mode, not a contradiction. Formalizing this as the autonomous-mode contract — codifying that standing-lane rules constitute direction — is tracked in #459.
Expand All @@ -116,7 +116,7 @@ Report the result: confirmed (with the observed behavior / code path — the ite

### 4. Interview (if needed)

Only after verification (or for enhancements, where the open question is scope, not fact): when the description is vague or missing acceptance criteria, ask focused questions one at a time — resolve the most load-bearing ambiguity first. Post questions as item comments. Mark `status:needs-info` until the reporter responds.
Only after verification (or for enhancements, where the open question is scope, not fact): when the description is vague or missing acceptance criteria, ask focused questions one at a time — resolve the most load-bearing ambiguity first. Each question is a decision question and carries the same brief-before-ask restatement as the direction gate above: which item it concerns, the decision being asked, and the consequence of each option **you present**. An open-ended question presents no option set to enumerate consequences for — state instead what the answer will determine, and never narrow a genuinely open question into a closed list just to satisfy the restatement. Post questions as item comments. Mark `status:needs-info` until the reporter responds.

### 5. Apply outcome

Expand Down
26 changes: 26 additions & 0 deletions plugins/work-items/skills/triage/evals/evals.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,32 @@
"Prefixes every comment and item it creates with the AI disclaimer during the autonomous session",
"Routes the label/comment writes through the bound adapter's mechanics and any item creation through the seam `create-item` — no inline provider commands"
]
},
{
"id": 7,
"name": "triage-briefs-before-presenting-the-recommendation",
"prompt": "/work-items:triage 64 — running interactively with a human operator present. #64 is titled \"Export job silently skips rows with a null tenant id\" and is filed as an enhancement request whose description is vague on scope. You have completed step 2's category and target-state assessment and are at the direction gate, before verification; your assessment is that the target state is needs-info (the scope is undecided), with briefed-as-delegable the alternative if the operator would rather you pick a scope yourself.",
"expected_output": "Restates which item is under discussion (#64 plus its one-line title), the decision being asked (which target state to apply), and the consequence of each presented option — needs-info parks the item pending the reporter, briefed-as-delegable admits it to the delegable lane on an agent-chosen scope — and only then presents the recommendation and waits for the operator's explicit direction. Nothing is mutated before that direction arrives.",
"files": [],
"expectations": [
"Restates the item identity (number + one-line title) before presenting the recommendation, not after",
"States the decision being asked and the consequence of each presented option before asking for direction",
"Does not compress away the restatement even though the session is running under a terse output style",
"Still waits for the operator's explicit direction before mutating anything — the restatement precedes the question, it does not replace the wait"
]
},
{
"id": 8,
"name": "triage-briefs-before-each-interview-question",
"prompt": "/work-items:triage 64 — running interactively with a human operator present. #64 is an enhancement request titled \"Export job silently skips rows with a null tenant id\", so the open question is scope, not fact. You already briefed and presented your step-2 recommendation and the operator has replied: \"go ahead, park it as needs-info and ask the reporter what you need.\" You are now at step 4 with the scope still undecided, so a follow-up scoping question for the reporter is the next step.",
"expected_output": "Even though the operator has already been briefed once this pass and has just given direction, the follow-up scoping question carries its own restatement: which item it concerns (#64 plus its title), the decision the question resolves, and — because \"what scope do you actually need?\" is open-ended rather than a closed option set — what the answer will determine, rather than a manufactured list of options to enumerate consequences for. The question is posted as an item comment and the item is marked status:needs-info.",
"files": [],
"expectations": [
"Restates the item identity and the decision the question resolves before posting the follow-up scoping question, not only before the earlier recommendation",
"For the open-ended scoping question, states what the answer will determine instead of inventing a closed option set to enumerate consequences for",
"Does not treat the earlier brief in the same pass as license to skip this one",
"Posts the question as an item comment and marks status:needs-info rather than answering the scope question itself"
]
}
]
}
Loading