您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

17KB

Spec Driven Development — Chapter-20: 9 - Deleting a Task: The Spotlight on tasks and analyze

  • Source: /library/Spec Driven Development/source-file.pdf
  • PDF pages: 328–339
  • Pages without text: none

9 - Deleting a Task: The Spotlight on tasks and analyze The fourth loop starts: 004-excluir-tarefa The main branch is stable with three features: create and list, complete and reopen, filter by state. The fourth starts like all the others: you leave main instead of touching it. You create the branch 004-excluir-tarefa and run the whole cycle inside it, until the merge hands the result back to the main line. The map is the usual one. Open the figure again for a second, just long enough to place yourself on it:

It is the same diagram from Chapter 5; what changes is only where you stand on it. This time the spotlight falls on tasks and analyze , the two steps that break the work into steps and check whether the whole still holds together, and that ran in the dark through the three earlier laps. The feature of the moment is deleting a task: the person removes an item they no longer want to see. It is the right showcase for this pair of steps for two reasons. First, deleting is a delicate action, so it matters to break it into small, safe steps, and that is what tasks does. Second, deleting talks to what the previous features already decided, and that is where analyze takes the lead: it reads spec, plan and tasks together and demands coherence between them. The early steps you already have down, so they will run light so the spotlight can turn on where the chapter wants it.

The early steps, at a light pace The specify of deleting is short, because the intent is small: the person wants to remove a task from the list. The spec describes what happens when they do it and stops before saying how. clarify ties off three loose ends. The first is the most important for the rest of the chapter: is the deletion reversible or final? The clarified answer chooses the simplest path the constitution approves (YAGNI): final, with no trash bin nor undo. The task disappears from the list and from storage; if one day reversibility is missed, it becomes its own feature, with its own cycle. The second end is a minimal safeguard: because it is a destructive action with no way back, the interface asks for a simple confirmation before removing. Notice that this is a minor decision, resolved in passing in clarify and detailed in plan , far from the chapter's central subject: just the basic care of not deleting on the first click. The third end closes the scope: you can delete a task in any state, open or completed, and the deletion does not look at that state. plan inherits the usual stack (React with TypeScript, Zustand, localStorage behind a repository) and decides the how, which here is lean: the repository gains a remove operation, a DeleteTask use case orchestrates the removal treating the nonexistent task as error as value, the store gains a deleteTask action, and the view gains a button with that confirmation. The checklist passes without finding a completeness hole: each view of the deletion is declared, and the check that the artifacts talk to each other is left for analyze , right ahead. Between one step and the next, the usual /clear : each step restarts by reading the approved artifact, and the chapter reaches tasks with a solid base instead of a vague memory.

tasks : the plan turns into a list of steps tasks reads the plan and turns it into an ordered, verifiable list. It invents no new decision: it takes the construction design and breaks it into the concrete steps that lead to the code. The result is an artifact with the look of an executable checklist, and it is the first place where the work stops being prose and becomes a sequence of actions. What makes a good task? Two qualities, and they go together. The first: it is small, a single step, the size an agent executes at once without getting lost. The second: it is checkable, it ends in a state you can look at and say “this is done”, instead of “this is more or less on track”. A task that touches half the system fails on both: it is too big to run safely and too vague to confirm. Decomposition exists so that each line of the list is a step you can take and check. This is the real list tasks generated for the deletion, grouped by phase: Phase 1 - Repository (data layer) T001 test of remove(id) in the repository (test, fails first) T002 declare remove(id) in the repository interface (mechanical: contrac t) T003 implement remove(id) until T001 passes T004 remove(id) in the in-memory fake (mechanical: parity) Phase 2 - Domain (use case) T005 type DeleteTaskFailure = { kind: “task-not-found” } (mechanical: ty pe) T006 test of DeleteTask (test, fails first) T007 implement makeDeleteTask until T006 passes Phase 3 - Presentation T008 test of deleteTask in the store (test, fails first) T009 deleteTask action in the store until T008 passes T010 “delete” button with confirmation in TaskList (mechanical, view)

Phase 4 - Validation T011 npm test (green) and npm run build (clean type-check) T012 manual validation of the Success Criteria Look at the size of each item. None says “implement the deletion”. Each names a file and a step: a test that fails, a function that makes that test pass, a type, a button. Twelve steps where “delete task” would fit in a single line, and that is the point: the difference between asking an agent (or yourself a week from now) “do the whole thing” and asking “take this step, then confirm, then the next one”. Why this order: TDD and layers The list is not in just any order; it obeys two forces, and seeing them is what lets you audit a task list instead of just trusting it. The first force is TDD: the test of a behavior comes before the implementation that satisfies it. Notice the T001/T003 pair: the test of remove (T001) appears before the implementation of remove (T003), with the annotation “fails first”. The same pattern repeats in T006/T007 and T008/T009. That order guarantees that each piece of behavior is born from a test that first fails and then passes, instead of a test written to confirm code that already exists. The second force is layered architecture. The list rises from the inside out: first the repository (the data layer, Phase 1), then the domain (the use case, Phase 2), last the presentation (store and view, Phase 3). That direction follows the dependencies, not taste. The DeleteTask use case needs the repository to already know remove ; the store needs the use case to already exist. Building from the bottom up makes each step rest on something already standing. Why the dependency runs that way, and what

happens to a project when it flips, belongs to FOCUS Architecture (2026, https://books.kodel.com.br/en/books/focus/), the second volume in this trilogy, which answers where each rule lives and why dependencies point inward. You do not need it to read this list: the practical rule is enough, that nobody builds the upper floor before the one below it. With these two forces in mind, you can audit a task list. Three questions are enough. Is any step too big, the kind that hides several decisions in a single line? Is any out of order, depending on something not yet built? Did any behavior get an implementation without getting a test first? Passing the deletion list through this sieve approves it: the steps are tiny, they rise in the order of the layers, and every real behavior has its test ahead of it. A caveat, so the sieve does not turn into dogma. Not every task needs a test of its own. Look at the ones marked “mechanical”: declaring the DeleteTaskFailure type (T005), exposing remove in the repository interface (T002), composing the button in the view (T010). These are steps with no decision logic, covered by the surrounding behavior tests or by the manual validation. Demanding a unit test for each of them would bloat the list without increasing safety. TDD orders tests where there is behavior to fix, not where there is only mechanical wiring to connect. Deep dive: TDD orders by behavior, not by file. The practical rule is not “a test for each file”, but “a test before each decision that could be wrong”. Removing an item from a list is a decision (and it gets a test); declaring that the repository has a remove method is a signature (and it does not). When the list distinguishes the two, it stays lean without opening holes: the points where a bug could fit are

all fenced in by a test that fails first, and the points where no logic fits stay free of ceremony. Confusing the two is what makes suites swell with tests that only confirm that a line of wiring is still that line. analyze : reading the three artifacts together With the task list ready, the cycle reaches the last check before the code: analyze . It does something no earlier step did: it reads the three core artifacts (spec, plan and tasks) at the same time and looks for mismatches between them. Its question stops being about each artifact in isolation and becomes whether they tell the same story. analyze classifies what it finds into three kinds of finding. A conflict is a contradiction: two artifacts state things that do not fit together. A gap is a lack: a spec requirement that no task covers, for example. An excess is the opposite: a task that matches no requirement, work that showed up unasked. The three answer the same question from different angles: is the chain spec → plan → tasks intact, with no loose link nor extra link? The result comes as a consistency report: a table that lists each finding with its identifier, category, severity, the place where it lives and the recommendation. It is the first time this format appears, and it is deliberately dry, made for you to glance at and know what to halt before going on. It is worth separating analyze from checklist , because the two check things and can be confused. The checklist , from the previous chapter, looks at one artifact at a time and asks whether it is complete and clear: is any decision missing, does any sentence admit two readings? analyze looks at the three together and asks whether they are consistent with each other:

does what the plan promises match what the spec asked, and do the tasks cover exactly that? Completeness and clarity of each piece are the checklist ‘s job; coherence of the whole is analyze ‘s job. Different questions, different moments. Deep dive: gap and excess, the other two findings. The conflict is the finding this chapter stages, but the other two show up just as often in practice. A gap is an orphan requirement: the spec says “the deletion asks for confirmation”, but no task creates the confirmation step. An excess is an orphan task: the list includes “export tasks to CSV” that no requirement supports, a sign of scope that leaked. analyze finds all three the same way, crossing the three sources; what changes is only which side of the correspondence was left without a pair. A real conflict: the deletion against “completing” In the filter of the previous chapter, analyze passed almost blank, because the clean plan left no mismatch. In the deletion, it catches a real conflict, and not one invented for the occasion: the new feature is on a collision course with a decision made back in Chapter 7. Remember what the deletion fixed in clarify : it is final, the task disappears from the list and from storage. Now remember what “completing”, in Chapter 7, left written in its spec: a completed task stays on the list, in the same position, shown as completed. analyze reads the deletion's artifacts together with those of the already integrated features, and the two sentences clash head-on. One says a completed task can disappear for good; the other says a completed task stays on the list. Which of the two holds?

The report logs the clash like this: ID Category Sev. Location Summary C1 Conflict between artifacts HIGH delete/spec.md FR-002 × complete/spec.md FR-006 “final deletion: the task disappears from the list” clashes with “a completed task stays on the list”. Resolve at the source. The wrong instinct, faced with this, is to ask “which of the two features is wrong?” and start touching the one that seems weaker. analyze pushes toward a better question: which wording is ambiguous? And here the diagnosis defuses the conflict. When “completing” said “the task stays on the list”, what was it talking about? A specific case: completing a task does not remove it on its own, it stays there, struck through, instead of disappearing the instant you mark it. It was a promise against self-removal on completing, not a promise of eternal permanence against any future action. Read that way, the conflict evaporates: deleting can go on being final, because deleting is a deliberate, separate act, not the side effect of completing. The two features always fit together; what was missing was one of the specs saying so in full. Fix at the source, and the cascade it triggers Once the conflict is diagnosed, it remains to fix it, and the where matters as much as the what. The temptation is to patch it downstream: touch a task so the report stops complaining, or

adjust the code at implementation time. analyze recommends the opposite: fix it at the source, in the artifact where the truth is born. Conflict C1 is a wording matter in the spec, so it is in the deletion's spec that the correction goes. It gains a new clarification, which distinguishes the two acts at once: The deletion MUST be final: the task disappears from the list and from storage. “Final” qualifies the act of deleting, distinct from completing: completing marks and keeps the task on the list (002 FR-006); deleting removes it for good. The two do not contradict each other. Notice why the source is the right place. If the fix were in a task, or worse, in a line of code, the contradiction would stay alive in the document that describes the problem: the spec would still say “disappears for good” and the completing one would still say “stays”, and the next person to read the two would trip on the same clash, now without analyze around to flag it. Fixing at the source is what makes the fix hold for all the downstream artifacts at once, instead of covering the symptom at one point and letting it leak at another. The correction in the spec triggers a cascade, and this time it is short. With the spec adjusted, you re-evaluate the artifacts that descend from it: the plan already modeled the deletion as a removal that holds for any state, without depending on the ambiguous sentence, so it stays valid; the tasks already covered deleting a completed task (the DeleteTask test in T006 includes exactly that case), so they stay valid. You run analyze again, and it passes clean: zero conflicts. The cascade was short precisely because the problem was in the wording, not in a construction decision. If the conflict were about a decision (if the plan had

chosen, say, “mark as deleted” instead of removing), the cascade would descend deeper, realigning plan, tasks and maybe tests. analyze does not decide the size of the fix; it only guarantees that the fix goes down the whole chain, instead of stopping at the first file. That is analyze ‘s role in the cycle: the last cheap safety net before implement . Everything it catches now costs a rewritten sentence; the same conflict, discovered with the code done and two features fighting in production, would cost a whole investigation. From the branch to the merge, and the bridge to Ch. 10 With the report clean, implement executes the list in following mode. There is no surprise here, and that is the point: tasks made the steps so tiny and analyze made the whole so coherent that implementing is going down the list. The remove test fails and then passes; the DeleteTask use case is born from its test; the store gains deleteTask ; the view gains the button with the confirmation. In the end, npm test closes in the green, with the new deletion tests added to those of the previous features, and npm run build confirms the clean type-check. The feature that began as a sentence now runs. From there the path is the known one. You commit the core artifacts, one by one, and merge the 004-excluir-tarefa branch back into main . The main line is stable again, now with four capabilities where there were three. tasks broke the work down; analyze caught the clash with “completing” while it was still text. The focus moves once more, and this time to the last lap. The fifth and final feature in the backlog is editing a task, changing the title or the description of something that already exists.

There the specification and planning work is familiar, and the weight falls on making the change happen end to end without breaking what already works. That is why, in Chapter 10, the spotlight shifts to implement , the step that turns the task list into code that passes the tests, and that closes the app.

Powered by TurnKey Linux.