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()