Enrich the task template for new cards (task 07)

Merge the highest-value slots from a fuller development-task template
into bench's lean one: an optional Depends on header line, an Affected
areas line in Context, Out of scope bullets in What to build, edge-case
and Given/When/Then guidance in Acceptance, and a Risks slot in Notes.
Each slot says who consumes it and is marked deletable — empty
boilerplate is worse than absence.

The load-bearing contracts are unchanged: exact Status values, the Open
questions heading the NOT READY gate keys off, Acceptance as the review
target, and the template staying off the board. Organisation process
(staging, sign-offs, approvals, contacts) is deliberately absent; a new
contract test pins both the gained slots and what must not grow back.
CLAUDE.md's Task file format section documents the Depends on line.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
istos
2026-07-29 22:37:36 +02:00
co-authored by Claude Fable 5
parent 59e84f3070
commit 9d2dbb017d
3 changed files with 133 additions and 4 deletions
+10
View File
@@ -413,5 +413,15 @@ Type is orthogonal to status. A discovery task — research, scoping, spiking an
approach — moves through the same five stages as everything else; "discovery"
describes the work, not where it sits on the board.
An optional **Depends on** line can name what must land first — task numbers
or external preconditions — so sequencing lives in the header instead of
prose asides:
```markdown
**Depends on:** 03, 05
```
The board does not enforce it; it informs whoever picks the next card.
The rest of the file is freeform — description, research findings, approach,
open questions, whatever is relevant to the current stage.
+38 -4
View File
@@ -3,6 +3,15 @@ Copy this file into backlog/ as NN-short-kebab-title.md — numbers are
allocated in creation order and stay with the task for life. The board never
lists this template (it only reads the stage directories). The **PR:** line
is added by the board itself when the task reaches review/ with a branch.
Anything below marked *optional* is deletable, and deleting beats leaving
it hollow — an empty boilerplate section reads as thinking that never
happened. Board process (review, PR, CI, merge) and the project-wide
definition of done stay off the card: the board does the former
mechanically and the repo CLAUDE.md owns the latter.
This template lives in tasks/, which updates never touch — improvements to
it ship only with fresh installs, so local edits are yours to keep.
-->
# NN — Imperative title: what changes when this is done
@@ -10,6 +19,8 @@ is added by the board itself when the task reaches review/ with a branch.
**Status:** Backlog
**Priority:** Medium — one clause on why it sits at this level
**Type:** Feature
**Depends on:** 03, 05 — task numbers or external preconditions that must
land first, for whoever sequences the board; delete when nothing blocks this
One paragraph for someone — human or agent — who has the codebase but not
the conversation: what this task changes, and why it is worth doing.
@@ -18,7 +29,12 @@ the conversation: what this task changes, and why it is worth doing.
What exists today and why it falls short. Point at real places rather than
describing from memory: packages and modules (`packages/domain/...`), prior
tasks (`../done/...`), reference documents (`../../reference/...`).
tasks (`../done/...`), plan files (`../../plans/...`) and reference
documents (`../../reference/...`) — a link outlives a summary.
**Affected areas:** the modules or layers this touches, one line in the
repo CLAUDE.md's module-map vocabulary — telling reviewers where to look
and agents where to stop. Optional: delete when the title already says it.
## What to build
@@ -29,13 +45,24 @@ convenience.
- First piece
- Second piece
**Out of scope** — the adjacent changes this task deliberately does not
make. This is what bounds the work agent's brief; scope creep is the
classic headless failure. Optional, but cheap insurance on any task with
tempting neighbours — delete rather than leave empty.
- Not this, even though it is nearby
## Acceptance
Observable outcomes, not implementation steps. The repo's definition of done
(tests pass, `lint-imports` clean, new behaviour covered) applies on top.
Observable outcomes, not implementation steps — review agents judge the
diff against exactly this list. The repo's definition of done (tests pass,
`lint-imports` clean, new behaviour covered) applies on top; don't restate
it. Given/When/Then phrasing is welcome where it sharpens a criterion, and
edge cases belong here too — boundaries, empty inputs, failure paths.
- [ ] Something a reviewer can check without reading the diff
- [ ] Another one
- [ ] Given <a state>, when <the action>, then <the observable result>
- [ ] Edge case: the boundary that would embarrass this feature if missed
## Open questions
@@ -51,3 +78,10 @@ it) when the task is ready to action.
Freeform: research findings, links, decisions taken along the way. The
board's relevance checks and PR reviews append their reports below this
line as the task moves.
**Risks** — known hazards and blockers: what could sink the approach, what
the change might break, what must hold for it to work. For the author
weighing the task and the reviewer double-checking it. Optional: delete
when there is nothing real to name.
- None worth naming yet.
+85
View File
@@ -0,0 +1,85 @@
"""The task template's load-bearing contract. Its headings are machinery,
not prose convention: the body becomes the work agent's brief verbatim,
Acceptance is what review agents judge against, and the Open questions
heading is what the NOT READY gate keys off so the template must keep
them, exactly, in order. Task 07 enriched the template; these tests pin
both what it gained and what it must never grow.
python3 -m unittest discover -s tests -v
"""
from __future__ import annotations
import re
import unittest
from pathlib import Path
TEMPLATE = (Path(__file__).resolve().parents[1]
/ "tasks" / "task-template.md")
LOAD_BEARING_HEADINGS = [
"Context", "What to build", "Acceptance", "Open questions", "Notes",
]
# Organisation process the board either does mechanically or doesn't own —
# deliberately absent from the template, and it must not grow back.
ORG_PROCESS_TERMS = (
"staging", "sign-off", "signoff", "qa ", "product owner",
"browser", "device matrix", "contact",
)
# The slots task 07 added, each of which earns its keep on the card.
ENRICHED_SLOTS = (
"**Depends on:**",
"**Affected areas:**",
"**Out of scope**",
"**Risks**",
"Given <",
"Edge case",
)
class TemplateContract(unittest.TestCase):
def setUp(self):
self.text = TEMPLATE.read_text()
def test_load_bearing_headings_survive_in_order(self):
headings = re.findall(r"^## (.+)$", self.text, re.MULTILINE)
self.assertEqual(headings, LOAD_BEARING_HEADINGS)
def test_status_is_an_exact_board_value(self):
self.assertIn("**Status:** Backlog\n", self.text)
def test_open_questions_section_names_the_gate(self):
section = self.text.split("## Open questions", 1)[1]
section = section.split("## ", 1)[0]
self.assertIn("`NOT READY`", section)
def test_enriched_slots_are_present(self):
for slot in ENRICHED_SLOTS:
with self.subTest(slot=slot):
self.assertIn(slot, self.text)
def test_new_slots_say_they_are_deletable(self):
# "delete" appears with each optional slot, so authors trim rather
# than leave hollow sections behind.
for slot in ("**Depends on:**", "**Affected areas:**",
"**Out of scope**", "**Risks**"):
with self.subTest(slot=slot):
paragraph = self.text.split(slot, 1)[1].split("\n\n", 1)[0]
self.assertIn("delete", paragraph.lower())
def test_no_organisation_process_creeps_in(self):
lowered = self.text.lower()
for term in ORG_PROCESS_TERMS:
with self.subTest(term=term):
self.assertNotIn(term, lowered)
def test_template_sits_outside_every_stage_directory(self):
# The board lists stage directories only; the template must stay a
# sibling of them, not a card.
self.assertEqual(TEMPLATE.parent.name, "tasks")
if __name__ == "__main__":
unittest.main()