diff --git a/internal/assets/assets_test.go b/internal/assets/assets_test.go index 6988901..a640ff2 100644 --- a/internal/assets/assets_test.go +++ b/internal/assets/assets_test.go @@ -267,6 +267,51 @@ func TestScaffoldedEntryFileStaysShort(t *testing.T) { // file addresses the set by pattern (`/.md`) instead of listing // nine links. What it still guarantees is the thing that actually breaks: a rule // added to the template set without being announced fails here. +// Every rule is preloaded into every request of the session on a harness that reads +// `rules/` — so a line added here is not paid once, it is paid continuously, and the +// whole set has to stay something an agent can hold rather than skim. +// +// 55 lines is the budget for a new rule. Three predate it and are capped where they +// stand instead, because each is roughly half table and fenced example: cutting them +// to 55 would leave under ten lines of prose across six sections, which is enough to +// state a rule and not enough to say why — and a rule whose reason was deleted is one +// an agent quietly deviates from. They may shrink; they may not grow. +func TestRulesStayShortEnoughToBePreloaded(t *testing.T) { + const budget = 55 + // Capped at what they measured when the budget landed. Lower a number here when a + // rewrite earns it; never raise one. + grandfathered := map[string]int{ + "specs.md": 82, + "knowledge-base.md": 81, + "delivery.md": 62, + } + + for _, f := range Workspace(paths.Claude) { + rules := paths.Claude.Dir + "/" + paths.Claude.RulesSeg + if !strings.HasPrefix(f.Rel, rules+"/") { + continue + } + raw, err := Render(paths.Claude, f) + if err != nil { + t.Fatalf("%s: %v", f.Name, err) + } + name := path.Base(f.Rel) + lines := strings.Count(strings.TrimRight(raw, "\n"), "\n") + 1 + + limit, capped := grandfathered[name] + if !capped { + limit = budget + } + if lines > limit { + t.Errorf("%s is %d lines, over its %d-line limit", name, lines, limit) + } + if capped && lines < limit { + t.Errorf("%s is down to %d lines — lower its cap from %d to lock the gain in", + name, lines, limit) + } + } +} + func TestEntryFileNamesEveryRule(t *testing.T) { for _, h := range paths.Harnesses() { raw, err := Render(h, entryFile(t, h)) diff --git a/internal/assets/templates/rules/delivery.md b/internal/assets/templates/rules/delivery.md index 525552a..037089e 100644 --- a/internal/assets/templates/rules/delivery.md +++ b/internal/assets/templates/rules/delivery.md @@ -1,88 +1,62 @@ # Delivery — branch, worktree, PR -Work does not happen on `main` and does not end with a green test run. It ends with -a pull request. - -## One branch per unit of work, in its own worktree - -Each unit of work — a spec, or a plan's leaf — gets its own branch, developed in its -own **git worktree**: +Work does not happen on `main` and does not end with a green test run. It ends with a +pull request. Each unit of work gets its own branch in its own worktree: ``` git worktree add ../- -b / ``` -The reason is that the user may run several agent sessions at once, one per -feature, and merge them into `main` as they land. A worktree is what makes that -possible: each session gets its own directory, none disturbs the others, and none -touches the checkout the user is sitting in. A shared tree with `git switch` cannot -do this — two sessions would fight over one working directory. +The user may run several sessions at once, one per feature, merging them as they land. +The worktree is what makes that possible: each session gets its own directory and none +touches the checkout the user is in. A shared tree with `git switch` cannot — two +sessions would fight over one working directory. ## Implementation is sequential — you write the code -**There is no implementation subagent and no parallel task dispatch.** This was -designed the other way first and rejected: +**There is no implementation subagent and no parallel task dispatch.** Designed the +other way first, and rejected: - Delegating implementation puts the cheaper model on the hardest work while the - orchestrator keeps the part that needs the least capability. That is backwards. -- Every fresh agent re-pays for discovery. Within one spec your accumulated context - is the asset: you use the right parser in task 1.2 because you wrote task 1.1. + orchestrator keeps the part needing the least capability. That is backwards. +- Every fresh agent re-pays for discovery. Within one spec your accumulated context is + the asset: you use the right parser in 1.2 because you wrote 1.1. - **File-disjointness is not independence, and a clean merge hides the difference.** - Two tasks touching no common file both need a `Money` type that does not exist - yet. Each creates its own, with different semantics. The merge is clean and - nothing signals that anything went wrong. Sequential execution cannot produce - this, because the later task sees the earlier task's code. - -Feature-level parallelism does not have that problem and is supported: the *human* -picks the split, each session has a full context, and two features a person -deliberately separated are unlikely to collide. - -## What running several sessions still costs + Two tasks touching no common file both need a `Money` type that does not exist yet. + Each invents its own, with different semantics, and the merge is clean. Sequential + execution cannot produce this: the later task sees the earlier task's code. -Worktrees isolate files, not the world outside them. Say these out loud rather than -discovering them: - -- **Shared external resources.** Two suites running at once fight over a fixed port, - one test database, a shared temp path. Either the suite namespaces them per - worktree, or the runs are serialized. -- **Cross-feature breakage.** Two features green on their own branches can be broken - together. Only CI on `main` after the merge sees that. +Feature-level parallelism has none of that and is supported — a *human* picks the split +and each session has full context. Worktrees isolate files, not the world: suites +fighting over a fixed port or one test database must be namespaced or serialized, and +two features green separately can still break together, which only CI on `main` sees. ## The delivery sequence Once the last task is done: -1. **Full suite + lint** on the integrated branch. The per-task scoped runs cannot - see breakage between tasks. -2. **`scc validate`** — the artifacts have to be in shape too, and exit `2` is not - done. -3. **`code-review` and `security-review`** subagents on the diff, dispatched - together. Each returns a verdict, a table of what it actually checked, and - findings by severity — you fix from that report, you do not re-review. `blocked` - or any `blocker`/`critical` finding means the PR does not open yet; `major`/`high` - is fixed before merge; `minor`/`low` is your call, and saying "not doing this, and - why" in the PR body is a legitimate answer. A gate reported `not-run` is not a - pass: run it yourself or say in the PR that it was not run. - - The PR should arrive already reviewed: a PR is for the human, and spending their - attention on findings a subagent would have caught is pure waste. Fixing then - re-running the two agents is worth one round; a third round means the finding - needs a person, not another review. +1. **Full suite + lint** on the integrated branch. Per-task scoped runs cannot see + breakage between tasks. +2. **`scc validate`** — the artifacts have to be in shape too, and exit `2` is not done. +3. **`code-review` and `security-review`** subagents on the diff, dispatched together. + Each returns a verdict, what it checked, and findings by severity — you fix from + that report, you do not re-review. `blocked` or any `blocker`/`critical` means the PR + does not open yet; `major`/`high` is fixed before merge; `minor`/`low` is your call, + and "not doing this, and why" in the PR body is a legitimate answer. A gate reported + `not-run` is not a pass. The PR should arrive already reviewed — a human's attention + spent on what a subagent would have caught is waste. One round of fix-and-re-run is + worth it; a third means the finding needs a person. 4. **Commit and push.** Conventional Commits, written from the diff and the spec. 5. **Open the PR.** Body: what changed, which spec or plan, how it was verified. -## Then, CI — the answer you already have - -Use the `ci:` answer recorded at kickoff ([autonomy.md](autonomy.md)); do not ask now. - -- **`wait`** — watch the PR's checks until they settle. Red means fix, push, keep - watching. The work is not finished while CI is failing. -- **`no-wait`** — opening the PR is the finish line. +Then CI, using the `ci:` answer from kickoff ([autonomy.md](autonomy.md)) — do not ask +now. **`wait`** means watch the checks until they settle, fixing and pushing while they +are red: the work is not finished while CI is failing. **`no-wait`** means opening the +PR is the finish line. ## Degrading -- **No remote, or no `gh`** — commit on the branch and stop there, saying so. A - branch the user can push themselves is a real deliverable; silently skipping the - PR is not. -- **Worktrees accumulate.** Remove a worktree once its branch is merged; keep it if - it still holds unmerged work, and say which you did. +**No remote, or no `gh`** — commit on the branch and stop there, saying so. A branch +the user can push themselves is a real deliverable; silently skipping the PR is not. +**Worktrees accumulate** — remove one once its branch is merged, keep it if it holds +unmerged work, and say which you did. diff --git a/internal/assets/templates/rules/knowledge-base.md b/internal/assets/templates/rules/knowledge-base.md index 3220312..3b1c9c9 100644 --- a/internal/assets/templates/rules/knowledge-base.md +++ b/internal/assets/templates/rules/knowledge-base.md @@ -1,8 +1,8 @@ # The knowledge base — `docs/` A spec answers *what this feature does now*. `docs/` answers *why* — the durable -reasoning, the decisions, the material read from outside. Neither replaces the other, -and keeping them separate is what lets a spec stay anchored to one feature. +reasoning, the decisions, the material read from outside. Keeping them separate is +what lets a spec stay anchored to one feature. ``` docs/ @@ -21,67 +21,47 @@ synonyms used where a canonical term belongs, and dependencies missing from ## wiki/ -One page per concept, linked with `[[wikilinks]]`. Every page is reachable from -`index.md` — an unreachable page is an orphan, and an orphan is a page nobody will -ever find again. Record what changed in `changelog.md` when you change the wiki. +One page per concept, linked with `[[wikilinks]]`, every page reachable from +`index.md` — an orphan is a page nobody will ever find again. Record what changed in +`changelog.md`. -**`raw/` is a drop box, not storage.** Material collected from outside goes there to -be read, distilled into a wiki page, and then removed. A file still sitting in -`raw/` is a finding: it was collected and never processed. +**`raw/` is a drop box, not storage.** Outside material goes there to be read, +distilled into a wiki page, and removed. A file still sitting there is a finding: it +was collected and never processed. ## adr/ -One record per decision that is **hard to reverse**. Numbered contiguously from -`0001`, one file per record, cited from anywhere as -`adr:0007-use-sqlite-for-the-cache`: +One record per decision that is **hard to reverse**, numbered contiguously from +`0001`, cited from anywhere as `adr:0007-use-sqlite-for-the-cache`. Frontmatter +`status:` is `proposed | accepted | rejected | superseded`; the body is `## Context`, +`## Decision`, `## Consequences`. -```markdown ---- -status: accepted # proposed | accepted | rejected | superseded ---- - -# 0007 · Use SQLite for the cache - -## Context -## Decision -## Consequences -``` - -**A superseded record is marked, never edited.** The point of an ADR is that it -records what was believed at the time; rewriting one destroys exactly the thing it -exists to preserve. Add the new record, and in the old one set: +**A superseded record is marked, never edited** — an ADR records what was believed at +the time, and rewriting one destroys the thing it exists to preserve. Add the new +record; in the old one set `status: superseded` and `superseded-by: 0012-…`. -```yaml -status: superseded -superseded-by: 0012-move-the-cache-to-redis -``` - -Not every design decision is an ADR. If it is cheap to change, `design.md` is where -it belongs. +Not every design decision is an ADR. If it is cheap to change, it belongs in +`design.md`. ## codewiki/ -Prose that explains code, one page per area, with every section citing the exact -lines it is about: +Prose explaining code, one page per area, every section citing the exact lines it is +about: ```markdown ## How the dispatcher routes [internal/cli/cli.go:48-64]() - -One switch, no registration... ``` A citation that no longer resolves is a finding, so this is the one part of `docs/` -that goes stale loudly rather than quietly. **Every section cites something** — a -section that cites nothing is prose that has drifted free of the code it describes. - -Write it for the parts where reading the code does not tell you why it is like that. -Do not narrate what a reader can see. +that goes stale loudly rather than quietly. **Every section cites something** — one +that cites nothing has drifted free of the code it describes. Write it where reading +the code does not tell you why it is like that; do not narrate what a reader can see. ## glossary.md -One canonical term per concept, and the synonyms to avoid. One entry per line: +One canonical term per concept and the synonyms to avoid, one entry per line: ```markdown - **order total** — the amount charged, in minor units. Avoid: grand total, sum @@ -89,16 +69,13 @@ One canonical term per concept, and the synonyms to avoid. One entry per line: ``` Domain vocabulary drifts by default — three names for one thing appear within a week -of two people working in parallel. Pick one, list the others after `Avoid:`, and use -the canonical term everywhere: in code, in requirements, in the wiki. An avoided -synonym used as a whole word in `docs/` is a finding. +of two people working in parallel. Use the canonical term in code, in requirements, +in the wiki. An avoided synonym used as a whole word in `docs/` is a finding. ## stack.md Every adopted technology, with one line on why. **Technology not listed here is an -open decision, never something adopted silently** — and because a project's -dependency file is structured data rather than source, this is checkable: a direct -dependency declared there and absent from `stack.md` is a finding. - -So adding a dependency is a two-step act: add it, and say here why it earned its +open decision, never something adopted silently** — and because a dependency file is +structured data, this is checkable: a direct dependency declared there and absent +here is a finding. Adding a dependency is two acts: add it, and say why it earned its place. diff --git a/internal/assets/templates/rules/methodology.md b/internal/assets/templates/rules/methodology.md index e9b31b2..6ee75c2 100644 --- a/internal/assets/templates/rules/methodology.md +++ b/internal/assets/templates/rules/methodology.md @@ -1,73 +1,54 @@ # Methodology — per task, Unit by default -Every task is built one of two ways, and the annotation on the task line says -which. The choice is per task, not per project: a feature routinely has both. +Every task is built one of two ways, and the annotation on its line says which. The +choice is per task, not per project: a feature routinely has both. ## Before either cycle — find the tests that already cover this -**Identify the existing tests that exercise the paths this task will change, and -run them, before writing anything.** Name them, in one line, before you start. +**Identify the existing tests that exercise the paths this task will change, and run +them, before writing anything.** Name them, in one line, before you start. -This is not part of Unit or TDD. It precedes both, because it is about not breaking -what works rather than about proving what is new. - -It is here because of a measured result. Giving an agent a test-first *procedure* -while leaving it ignorant of which tests actually cover the code being modified -**increased regressions above doing nothing at all** — roughly 9.9% against ~6.1%. -Identifying the covering tests first brought it to ~1.8%. Procedure without context -was worse than no procedure. - -The failure it prevents is specific: a task adds a case to a function whose -existing tests nobody looked at, the new test passes, and the old behavior silently -changed. +This precedes both cycles, because it is about not breaking what works rather than +proving what is new. It is here because of a measured result: giving an agent a +test-first *procedure* while leaving it ignorant of which tests cover the code being +modified **increased regressions above doing nothing at all** — roughly 9.9% against +~6.1%, where identifying the covering tests first brought it to ~1.8%. The failure it +prevents is specific: a task adds a case to a function whose existing tests nobody +looked at, the new test passes, and the old behavior silently changed. ## Unit — the default -Write the code, then write a unit test for **each function** in it. That is the -whole cycle. **There is no RED/GREEN here** — there is no failing-test step to -observe, because the code already exists. - -Two conditions make this legitimate rather than a shortcut, and both are -load-bearing: +Write the code, then a unit test for **each function** in it. **There is no RED/GREEN +here** — no failing-test step to observe, because the code already exists. Two +conditions make this legitimate rather than a shortcut, and both are load-bearing: - **Immediately, per function — never at the end.** Finish a function, test that - function, move on. The evidence supports test-last only in this iterative form; - nothing supports saving the tests for the end of the feature. -- **The test comes from the requirement, not from the code.** This is where an - agent fails differently from a human: a human writing tests late writes too few, - an agent writes tests that assert *what the code does* — green tests that - faithfully encode the bug. Read the requirement the task cites and assert that. - Reading your own implementation to decide what to assert is the failure mode, not - the method. + function, move on. The evidence supports test-last only in this iterative form. +- **The test comes from the requirement, not from the code.** This is where an agent + fails differently from a human: a human writing tests late writes too few, an agent + writes tests asserting *what the code does* — green tests that faithfully encode the + bug. Read the requirement the task cites and assert that. The default because most code is plumbing: the shape is not in doubt, and tests written straight after are just as binding. Those two conditions are the entire risk -of writing code first, and you are accountable for them — `scc` cannot check either. +of writing code first, and `scc` can check neither — you are accountable for them. ## TDD — RED/GREEN required Write the failing test first, **watch it fail**, then make it pass, then refactor. Skipping RED is not TDD: a test that has never failed has not been shown to test -anything. Say, in the task's notes, that you observed the red. - -RED/GREEN belongs to TDD and only to TDD. Unit is not a lazier TDD and TDD is not a -stricter Unit — they are two different cycles. - -**Mandatory when the cost of being wrong is high:** - -- **money** — any calculation, rounding, split, or conversion involving currency -- **complex algorithms** — anything whose correctness is not obvious by reading it -- **hypothesis / thesis validation** — code written to prove something holds -- **anything else** where the complexity is real and the chance of being wrong is - high - -The trigger is risk, not size. A three-line rounding helper that touches money is -TDD; a two-hundred-line CRUD handler is Unit. +anything. Say, in the task's notes, that you observed the red. RED/GREEN belongs to +TDD and only to TDD — Unit is not a lazier TDD, and TDD is not a stricter Unit. -A task annotated `(TDD)` is also a task to surface before it lands, even in an -automatic run — see [autonomy.md](autonomy.md). +**Mandatory when the cost of being wrong is high:** money, in any calculation, +rounding, split, or conversion involving currency · complex algorithms, whose +correctness is not obvious by reading them · hypothesis validation, code written to +prove something holds · anything else where the complexity is real and the chance of +being wrong is high. -## After the code +The trigger is risk, not size. A three-line rounding helper that touches money is TDD; +a two-hundred-line CRUD handler is Unit. A `(TDD)` task is also one to surface before +it lands, even in an automatic run — see [autonomy.md](autonomy.md). -Neither cycle ends at a passing test. Run the scoped tests **and** the lint before -you call the task done: [verification.md](verification.md). +Neither cycle ends at a passing test. Run the scoped tests **and** the lint before you +call the task done: [verification.md](verification.md). diff --git a/internal/assets/templates/rules/specs.md b/internal/assets/templates/rules/specs.md index 6dc543e..1ca6e77 100644 --- a/internal/assets/templates/rules/specs.md +++ b/internal/assets/templates/rules/specs.md @@ -1,16 +1,15 @@ # Specs — the three artifacts -`specs//` holds exactly three files. `scc spec new ` creates them -from the templates; this is what goes in them. +`specs//` holds exactly three files. `scc spec new ` creates them; +this is what goes in them. ## requirements.md — EARS, all five patterns -Requirements are written in EARS and numbered `R.`, so the later phases -can cite them. This is not ceremony: prose has nothing to validate, an EARS clause -has named parts, and a missing part is a finding. +Numbered `R.` so later phases can cite them. Prose has nothing to +validate; an EARS clause has named parts, and a missing part is a finding. The ruleset: **zero or many preconditions · zero or one trigger · one system name · -one or many responses**, always in that order. +one or many responses**, in that order. | Pattern | Shape | |---|---| @@ -21,51 +20,39 @@ one or many responses**, always in that order. | Unwanted behavior | `If , then the shall ` | | Complex | more than one keyword, in clause order | -**All five are valid.** Do not force everything into `When …`: inventing a trigger -for a requirement that is simply always true makes it worse. `If … then …` — -unwanted behavior — is the pattern most often missing and the one that most often +**All five are valid.** Inventing a trigger for something simply always true makes it +worse. `If … then …` is the pattern most often missing and most often the one that matters. -**Omit, don't fill.** Structured requirements measurably improve generated code, but -the curve is not monotonic: over-specification constrains reasoning and introduces -requirements that conflict with each other, and correctness drops. Specify what the -feature actually decides. A requirements document longer than the decision it records -can produce worse code than a shorter one. +**Omit, don't fill.** Structure improves generated code, but the curve is not +monotonic: over-specification constrains reasoning and introduces requirements that +conflict, and correctness drops. A requirements document longer than the decision it +records can produce worse code than a shorter one. ## design.md — scaled by complexity -**The design must fit the decision being made.** The common failure is inventing -architecture for a change that had no architectural question in it — a component -diagram for a two-function addition, a data-model section for something that touches -no data. +**The design must fit the decision being made.** Inventing architecture for a change +that had no architectural question is worse than verbose — **invented architecture +constrains**: the next session reads it as a decision somebody made, and honors it. -That is worse than verbose. **Invented architecture constrains:** the next session -reads it as a decision somebody made, and honors it. Filler becomes binding. - -So the sections are conditional. **Omit, don't fill:** +So the sections are conditional. Omit, don't fill: | When the change… | design.md carries | |---|---| -| decides nothing structural | what changes, where, and why — a few paragraphs. No components, no diagram. | -| moves a boundary, a data shape, or an external contract | those sections only, for the parts that actually change | -| has real alternatives with trade-offs | the alternatives and why one won — plus an ADR if the decision is hard to reverse | - -A heading filled with "N/A", or with prose written to satisfy the heading, is worse -than an absent heading: it reads as a decision and nobody can tell it apart from -one. Delete the heading instead. - -`scc` checks that the design exists and traces to its requirements. It never checks -that a particular section is present — a required heading is a request for filler. +| decides nothing structural | what changes, where, and why. No components, no diagram. | +| moves a boundary, a data shape, or an external contract | those sections only, for the parts that change | +| has real alternatives with trade-offs | the alternatives and why one won — plus an ADR if it is hard to reverse | -## tasks.md — the grammar +A heading filled with "N/A", or with prose written to satisfy the heading, reads as a +decision nobody can tell apart from a real one. Delete the heading instead. `scc` +never checks that a section is present: a required heading is a request for filler. -See [tasks.md](tasks.md). Exactly one `(Unit)` or `(TDD)` per task, and every task -cites the requirements it satisfies. Traceability runs both ways: every requirement -reaches at least one task, every task cites a requirement that exists. +## tasks.md -## Changing a spec that already exists — deltas, not rewrites +See [tasks.md](tasks.md). Traceability runs both ways: every requirement reaches a +task, every task cites a requirement that exists. -Write the change as a **delta** against the spec, marking each affected requirement: +## Changing an existing spec — deltas, not rewrites ``` - **R2.3** (MODIFIED) When the cart is empty, the checkout shall … @@ -73,32 +60,23 @@ Write the change as a **delta** against the spec, marking each affected requirem - **R1.4** (REMOVED) ``` -Three reasons: +You specify the change, not the system, so adopting this on an existing codebase +never means writing the spec for everything that already works. Deltas scoped to +individual requirements also make concurrent edits safe — two sessions can change one +spec while touching different requirements, where whole-file rewrites collide on +contact — and a reviewer reads intent instead of reconstructing it from a diff. -1. **You specify the change, not the system.** Adopting this practice on an existing - codebase must not require writing the spec for everything that already works. - Specs grow one change at a time. -2. **Deltas scoped to individual requirements make concurrent edits safe.** Two - sessions can change one spec as long as they touch different requirements. - Whole-file rewrites collide on contact. -3. **A reviewer reads intent instead of reconstructing it** from a diff. - -The delta is how a change is proposed and reviewed. Once it lands, fold it into the -spec: the spec stays the current statement of the feature, never an append-only log. +Once the delta lands, fold it in: the spec is the current statement of the feature, +never an append-only log. ## The spec is anchored, not disposable -A spec does not stop being true when its feature merges. **Work that touches an area -a spec covers updates that spec as part of the delivery** — as a delta, in the same -branch, in the same PR as the code. - -Under autonomy the file is the only record of intent, and a record that stops being -maintained stops being a record. A stale requirement read as current is worse than an -absent one, because it is believed. - -Nothing detects this drift mechanically — verifying that a spec matches the code -means understanding the code, which `scc` deliberately does not do. Keeping the spec -current is your obligation, and the reviewer's to check. +**Work touching an area a spec covers updates that spec as part of the delivery** — +as a delta, same branch, same PR. Under autonomy the file is the only record of +intent, and a stale requirement read as current is worse than an absent one because +it is believed. -Anchored means *maintained while the code exists*, not *never removed*. A feature -genuinely deleted takes its spec with it. +Nothing detects this drift mechanically: verifying a spec against code means +understanding the code, which `scc` deliberately does not do. It is your obligation +and the reviewer's to check. Anchored means *maintained while the code exists* — a +feature genuinely deleted takes its spec with it. diff --git a/internal/assets/templates/rules/tasks.md b/internal/assets/templates/rules/tasks.md index 11796fc..5f0f968 100644 --- a/internal/assets/templates/rules/tasks.md +++ b/internal/assets/templates/rules/tasks.md @@ -1,32 +1,28 @@ # Task grammar -One grammar governs every task line, whether it sits in a spec's `tasks.md` or in a -plan's checklist. The methodology is a property of the task, not of the vehicle that -carried it. +One grammar governs every task line, in a spec's `tasks.md` or a plan's checklist. +The methodology is a property of the task, not of the vehicle that carried it. ``` - [ ] 1.1 (Unit) Parse the manifest file — R1.2, R1.4 - [ ] 1.2 (TDD) Calculate the pro-rata split across accounts — R2.1 -- [ ] 1.3 (Unit) Render the summary table — R3.1 ``` - `- [ ]` / `- [x]` — the checkbox is the state. - `1.1` — a unique number, `.`. - `(Unit)` or `(TDD)` — **required, exactly one.** A task with no methodology is a - task where nobody decided, which is the failure this whole practice exists to - prevent. `scc` exits `2` on a task that is missing it. + task where nobody decided, which is the failure this practice exists to prevent. + `scc` exits `2` on a task missing it. - The description, in the imperative. -- `— R1.2, R1.4` — the requirements this task satisfies, after an em dash. Required - in a spec's `tasks.md`; that citation is what makes traceability checkable. +- `— R1.2, R1.4` — the requirements this task satisfies, after an em dash. Required in + a spec's `tasks.md`; that citation is what makes traceability checkable. There is no parallel-dispatch marker. Implementation is sequential — see [delivery.md](delivery.md) for why, and for the parallelism that *is* supported. -## Requirement IDs - -Requirements are numbered `R.` in `requirements.md` and cited by that -ID. It greps cleanly, it never collides with a task's own number, and a reader who -has never seen this document can follow it. +Requirements are numbered `R.` and cited by that ID: it greps cleanly, it +never collides with a task's own number, and a reader who has never seen this document +can follow it. ## How big is a task @@ -36,21 +32,18 @@ has never seen this document can follow it. Granularity is not tidiness; it decides what a failure costs. Agents complete individual steps far more reliably than whole workflows, and structuring work so a -failure can be retried at the subtask level cut retry cost by ~73% against retrying -a whole plan. - -- **Too coarse** and a red result tells you only that a feature is broken. -- **Too fine** and the checklist becomes bookkeeping about work smaller than the act - of recording it. +failure can be retried at the subtask level cut retry cost by ~73% against retrying a +whole plan. Too coarse and a red result tells you only that a feature is broken; too +fine and the checklist becomes bookkeeping about work smaller than recording it. ## Two checklists, one truth -Your harness's own todo list is where you track the task you are on right now. The -file — `specs//tasks.md`, or the checklist in `plans/.md` — is the -durable record: it survives the session, it gets reviewed, it gets committed, and it -is what `scc` validates. +Your harness's todo list tracks the task you are on right now. The file — +`specs//tasks.md`, or the checklist in `plans/.md` — is the durable +record: it survives the session, it gets reviewed, it gets committed, and it is what +`scc` validates. -**Checking an item off in the session means checking the `- [ ]` box in the file -too.** A session that ends with its todo list complete and the file untouched has -lost everything except the code: the next session does not know which tasks were -done, and neither does the reviewer. +**Checking an item off in the session means checking the `- [ ]` box in the file too.** +A session ending with its todo list complete and the file untouched has lost +everything except the code: neither the next session nor the reviewer knows which +tasks were done.