Each draft PBI (up to WIP cap 12 total refined):
a. Break into implementation-ready items as vertical slices: each
item is one user experience or one capability extension, cut
end-to-end through every layer it needs (UI + API + persistence
together). NEVER split a single user experience into component
items ("frontend PBI / API PBI / DB PBI") — component splits let
per-layer work drift apart; in a target project they produced
large late-stage inconsistencies and dead branches between
components, discovered only at integration testing.
Walking skeleton first (per feature epic). When a draft PBI
is a large feature addition/overhaul that refines into multiple
items, the FIRST item of the group is a walking skeleton: the
minimal user experience that already runs end-to-end through the
whole system, exercisable locally. Later items of the group flesh
it out one experience unit at a time; give each of them
depends_on_pbi_ids naming the skeleton (step 3.e) and give the
skeleton the lowest priority number of the group (step 3.f) so
sprint-planning's dependency rule (FR-008) lands it in an earlier
Sprint.
a1. Vertical-slice inspection checklist (mandatory, per item).
- Demonstrable alone? Merged by itself, can this item show a
user-observable behavior locally?
- Value-worded? Do title/description name a user outcome, not a
layer or component? ("implement API for X" fails; "user can X"
passes.)
- No sibling-coupled value? If NO user-visible behavior exists
until a sibling item of the same group also lands, it is a
component split — merge the items or recut them vertically.
A split you cannot confidently settle is a PO decision, not a
guess: raise [<pbi-id>] PO_DECISION_REQUEST kind=pbi_split options=[...] with the proposed vertical recut as the
recommendation (same decision shape as sprint-planning Step 5;
human mode: ask the user per § PO seat resolution).
a2. Approach & prior-art clarity gate (mandatory, before AC).
The goal of refinement is to hand the Developer a PBI whose
solution approach/method is settled — not one where the
designer must guess the method or reverse-engineer intent. Before
writing AC, decide per item whether the approach is determinable
from requirements.md + docs/requirements-benchmark.md:
- Reuse
docs/requirements-benchmark.md first. It already
holds prior-art dispositions (adopt/adapt/reject) from
Requirement Definition. Do NOT re-search what it already
answers.
- Is there a known prior-art / similar-case pattern for this
feature, and is the technical direction (algorithm / protocol /
data model / integration style) determinable, or genuinely
open?
If a gap remains that a targeted search can close (prior-art or
tech-direction not settled at Requirement Definition),
delegate a bounded web search to an Opus sub-agent (mirrors
the Requirement Definition benchmark pattern). Record the resolved
direction into the PBI description, and name the doc it will
shape in design_doc_paths (step 3d):
Agent({
subagent_type: "general-purpose",
model: "opus",
description: "Refinement approach/prior-art research",
prompt: <<<EOF
Given one PBI (title, description, draft AC) plus excerpts of
requirements.md and requirements-benchmark.md, close the
*approach/method* gap for this PBI.
Rules:
1. Reuse requirements-benchmark.md first; only search for what
it does NOT already answer.
2. Run >=3 distinct WebSearch queries on prior art / similar
solutions / the accepted method for this feature, then
WebFetch the most relevant sources. Ground every claim in a
source read this session — never from memory.
3. Return the settled approach, the sources, the design docs
it should shape, and any residual question that ONLY the PO
can answer (business rule / scope boundary / ordering /
threshold / acceptance semantics).
Scope boundary — do NOT duplicate the Design stage. Detailed
per-library API selection and the S-070 technology specs are
the pbi-designer's mandatory library web search at Design
time. Here, settle *direction/method* only; do not pre-empt
library-level choices.
Output JSON:
{
"approach": "<settled method, 1-3 sentences>",
"sources": ["<url>", ...],
"shapes_docs": ["docs/design/specs/....md", ...],
"po_questions": ["<spec question only the PO can answer>", ...]
}
If WebSearch is unavailable or fails at the harness level (not
a "no results" content outcome), return
{"harness_incident": "websearch_unavailable"}
and do NOT fabricate an approach from memory.
EOF
})
SM main loop reads the JSON: fold approach into the PBI
description, merge shapes_docs into design_doc_paths, and
carry every po_questions entry into step 3.a3. On
harness_incident treat it as a harness incident, not a
fallback (mirrors requirement-definition step 5): surface per
the PO seat (human → tell the user and wait; agent → append to
.scrum/po/attention.md) and do not fabricate an approach.
Boundary restated: this step settles approach/method
direction only. Per-library API selection + S-070 specs stay
with the Design stage — do not duplicate them here.
a3. PO clarification for residual spec ambiguity (do not pass
ambiguity downstream). After research, resolve now — not by
deferring to the Developer/designer — any remaining unknown that
is PO-only: a business rule, scope boundary, ordering /
threshold, or acceptance semantics. Apply the escalate-vs-guess
filter in ../../rules/scrum-context.md § When you don't know:
- Escalate (route to the PO seat) when guessing wrong would change
observable behavior or break a contract. See § PO seat
resolution above for the human / agent routing; fold the
answer/ruling into the PBI
description / AC before refining.
- Do not escalate purely reversible unknowns (local naming,
internal decomposition, test-fixture values) — leave those to
the pipeline. Over-escalation defeats the point.
Never guess PO intent. An item is not eligible for refined
while a PO-only question about it is open (human mode: unanswered;
agent mode: no matching PO_DECISION / parked in attention.md).
b. Fill acceptance_criteria (Definition of Ready). Every string MUST
be independently verifiable — either:
- Given/When/Then form, or
- a measurable assertion: observable input/action → expected
observable outcome. Numeric thresholds are numbers, not adjectives.
Reject vague adjectives without a measurable condition: "robust",
"intuitive", "fast", "user-friendly", "error handling is robust",
"good performance". Rewrite as a concrete check, or split into
multiple criteria that each name an observable check.
AC array order is significant. The 1-based index of each
string is the AC's id used by downstream artifacts: the design
doc's Acceptance Criteria Mapping section and the UT
ac-coverage-r{n}.json map both reference it by index. Do not
reorder a PBI's acceptance_criteria after refinement without
a Change Process update — downstream references go stale.
Opus override for AC verifiability + scenario coverage
(mandatory). AC quality failures recurred 5 times across 3
target projects. Each failure traces to AC that pass the surface
check above (Given/When/Then or measurable assertion) but miss
one of: (i) scenario coverage (normal / failure / edge), (ii)
mandatory grep-zero on deleted config variables, (iii) parity
with a reference implementation when one exists. The SM main
loop runs on Sonnet; pinning these rules in skill text has not
been sufficient. Delegate the per-AC verifiability + coverage
audit to an Opus-backed sub-agent via the Agent tool, on each
PBI's draft AC list, before setting status: refined:
Agent({
subagent_type: "general-purpose",
model: "opus",
description: "AC verifiability + kind audit",
prompt: <<<EOF
Audit the acceptance_criteria of one PBI before refinement,
AND classify it as kind=code or kind=docs.
Inputs:
- PBI id, title, description, ux_change
- demo_plan (string; null when not yet set)
- Draft acceptance_criteria (string array)
- catalog_targets (string array; may be empty)
- PBI type signal: derive from description keywords
(impl / refactor / config-removal / order-engine /
schema-version / docs-only / audit-follow-up)
Checks per AC string:
1. Verifiability: Given/When/Then form OR observable
input/action -> observable outcome. Reject vague
adjectives without a measurable condition.
2. Scenario coverage across the AC array as a whole:
(a) normal-path AC present
(b) failure-mode AC present (when impl PBI)
(c) edge-case AC present (concurrency / null / boundary)
3. Type-specific mandatory clauses:
- config-removal PBI -> "grep <removed-symbol> returns
zero across docs/ and CLAUDE.md" must appear
- SCHEMA_VERSION bump PBI -> "requirements.md and the
schema doc reflect the new version" must appear
- order-engine PBI -> "price parity with backtest
reference implementation" must appear
- audit-follow-up PBI -> UT scenarios named explicitly
per AC
4. AC ordering: normal-path first, failure / edge after.
Kind classification (3-axis OR rule, lean toward `code` on
ambiguity — false-negative `code` is harmless, false-positive
`docs` skips UT/coverage gates and lets code slip through):
- Axis A — description markers: "doc-only", "docs-consistency",
"documentation only", "[docs]", "[doc]" anywhere in title or
description.
- Axis B — AC content: every AC describes a doc-shaped change
(a passage exists / a cross-reference is correct /
frontmatter / revision_history / spec text). NO AC names a
runtime behaviour, API contract, UI interaction, or DB
mutation.
- Axis C — catalog_targets non-empty AND all elements end in
`.md`.
Decision: kind = "docs" iff (A AND B), OR (B AND C). All other
cases → kind = "code". (Axis A alone is a marker but not
sufficient — title text can lie. Axis C alone happens when a
code PBI also updates a spec — still code.)
Extra Check 5 — grep-shaped AC anti-pattern (applies when
kind == "docs"):
- Reject any AC whose verifiable claim is reducible to
"grep <pattern> in <file> returns N lines" / "<file>
contains <substring>" / "occurrence count == N" without a
semantic read of the content.
- Replace with: "<file> §X states <semantic claim>, verified
by reviewer reading the passage". The Integrity stage's
requirement-conformance reviewer reads passages; grep is
not a substitute for comprehension.
- Rationale: target-project pbi-054 had 6 grep-shaped AC and
the UT author wrapped each grep in a test function. The
tests passed without anyone (human or model) reading the
doc. Never again.
Extra Check 6 — demo_plan locality (applies when
kind == "code"):
- Flag a demo_plan that is null/empty, requires a cloud
deployment ("deploy to ...", a cloud provider name as the
only way to observe behavior), or reduces to "read the
code" / "inspect the diff". A valid plan names a local
start command and an observable check; cloud-only
dependencies name their local substitute (stub / fake /
local container).
Output: JSON
{
"verdict": "pass" | "needs_revision",
"kind": "code" | "docs",
"kind_rationale": "axes A=<true|false> B=<...> C=<...>; decision=...",
"per_ac": [
{ "index": 1, "text": "...", "issues": ["..."],
"rewrite_suggestion": "..." | null }
],
"missing_acs": [ "<concrete AC to add>" ],
"demo_plan_issue": "<why the plan is not locally executable>" | null
}
EOF
})
SM main loop reads the JSON. If verdict == "needs_revision",
apply rewrite_suggestion for flagged AC and append every
missing_acs entry before persisting; a non-null
demo_plan_issue means the demo_plan is not locally executable —
rewrite it per step 3.c2. Do not advance status to
refined until the next audit returns verdict: pass. If an AC
cannot be made verifiable without a PO-only decision (e.g. an
undecided acceptance threshold), route that question through step
3.a3 rather than inventing a value.
Persist kind on every PBI (code or docs — never leave the
field unset, the default is for legacy data only):
.scrum/scripts/set-backlog-item-field.sh "$PBI_ID" kind <code|docs>
Persist the audited AC list via the wrapper:
.scrum/scripts/set-backlog-item-field.sh "$PBI_ID" \
acceptance_criteria '["AC 1 ...","AC 2 ..."]'
The wrapper validates JSON-array-of-strings; status remains
draft until step 4 below.
c. Set ux_change (user-facing changes) via wrapper:
.scrum/scripts/set-backlog-item-field.sh "$PBI_ID" ux_change <true|false>
c2. Set demo_plan (mandatory for kind=code — machine-gated: the
status wrapper refuses refined while it is empty):
.scrum/scripts/set-backlog-item-field.sh "$PBI_ID" demo_plan \
"<start command; URL/CLI steps; what to observe>"
Decide NOW how this PBI will be shown running locally at
Sprint Review:
- A cloud-only dependency (managed DB / queue / auth / storage)
never blocks the demo — name its local substitute: a stub under
tests/stubs/, an in-memory fake, or a local container. Stub
philosophy is canonical in
../integration-tests/references/stub-construction.md (stub
only non-locally-reproducible interfaces; never an if TEST
branch in product code).
ux_change=false → the plan names the observable local check
(CLI invocation / curl / data assertion), not a UI tour.
kind=docs → exempt (the doc is the demo); leave it null.
"Deploy to to see it" and "read the code" are NOT demo
plans. If no local demonstration path exists, the item is not
refinement-ready — route the scope question through step 3.a3.
d. Set design_doc_paths (docs needing creation/update) via wrapper:
.scrum/scripts/set-backlog-item-field.sh "$PBI_ID" \
design_doc_paths '["docs/design/specs/feature-x.md","docs/design/specs/feature-y.md"]'
e. (Optional) Set description / depends_on_pbi_ids via wrapper:
.scrum/scripts/set-backlog-item-field.sh "$PBI_ID" description "..."
.scrum/scripts/set-backlog-item-field.sh "$PBI_ID" \
depends_on_pbi_ids '["pbi-001","pbi-002"]'
f. Assign priority (non-negative integer, lower = higher priority, 1 = highest) via wrapper:
.scrum/scripts/set-backlog-item-field.sh "$PBI_ID" priority <integer>
Integer only. String labels ("high"/"medium"/"low") violate the
schema and break the dashboard PBI Board. The wrapper rejects non-integers.
All field writes above MUST go through
.scrum/scripts/set-backlog-item-field.sh. The PreToolUse guard
blocks raw edits to .scrum/backlog.json; status is the only field
with its own wrapper (update-backlog-status.sh).