task-brief: track fence character and length so fenced examples cannot end a task early - #2304
mavericksea-ai wants to merge 2 commits into
Conversation
…t end a task early
|
Independent repro from a real plan, on Superpowers 6.4.1 (Claude Code 2.1.282, macOS awk, found by a Claude Opus 5.5 session). Beyond truncation, the same bug also sends content to the wrong task. When a fenced fixture holds a heading like ### Task 1: write a fixture
````markdown
```markdown
### Task 2: heading inside the fixture
```
````
Step 2 of Task 1.
### Task 2: real task two
|
The fence rule accepted only spaces and tabs after a closing fence, so in a plan with CRLF line endings the first fenced block never closed and every later task was reported not found. Allow a trailing carriage return. Add two cases to the SDD workspace test: a fenced heading that carries a real task number, where the next task's brief must start at its real heading (the shape oiler reproduced in obra#2304), and a CRLF plan whose fenced block must close so the next task is found.
|
Thanks @oiler, this is a really useful reproduction. The wrong-task effect is worse than the truncation I described, and a real 7-task plan makes it concrete. Testing your case properly also turned up something my first commit got wrong: in a plan with CRLF line endings, the closing fence never closed, so every task after the first code block came back "not found". The shipped script handles that case, so it was a regression in this PR. 80f8ed1 fixes it and adds your shape as a fixture (Task 2's brief must start at its real heading, with none of Task 1), plus a CRLF plan. The test now has 20 checks, all passing under gawk, mawk and BWK awk. |
Strong fix. The closing-fence rule is correct on both counts: it requires the same character and Answering the question Two things worth knowing before merge: Minor — an unterminated fence now makes later tasks unfindable. Minor — this and #2359 conflict. Both rewrite the same awk block in |
The strict fence parser's close check now allows a trailing carriage return, the form obra#2304 landed. Two test cases make the same-level heading rule explicit: a fenced same-level heading stays in its task (the shape real plans use to quote text for insertion), an unfenced one ends it (a sibling section such as Self-review or Done when).
Who is submitting this PR? (required)
What problem are you trying to solve?
skills/subagent-driven-development/scripts/task-briefextracts one task from a plan for the implementer and the reviewer. Its awk toggles an in-fence flag on any line starting with three backticks, so a~~~fence is not tracked at all, a four-backtick fence containing a three-backtick block toggles the flag back off inside the example, and an indented fence is not seen. In each case a heading such as### Task 99: Example onlyinside a fenced example is read as a real task boundary, extraction stops there, and the script exits 0 with a shorter brief. Anything after the example in that task, including its final requirements, never reaches the implementer or the reviewer.Reproduced with
task-brief PLAN 1 OUTon the shipped script at b36e082 (task-brief is identical ondevat 5940bd8):The 13 existing sdd-workspace checks pass on the current script; none exercises this boundary.
Two more effects came up after this PR opened. The same boundary also sends content to the wrong task: when the fenced heading carries a real task number, the tail of Task 1 lands in Task 2's brief (oiler's reproduction below, on a real 7-task plan). And the first commit of this PR regressed plans with CRLF line endings: its closing-fence check allowed only spaces and tabs, so a fence ended by a CRLF line never closed and every later task was reported not found. The shipped toggle handles a simple fenced block in a CRLF file, and Git for Windows checks files out with CRLF by default.
What does this PR change?
The fence rule now follows the CommonMark definition: it records the opening fence character and length, accepts up to three spaces of indentation, ignores a backtick line whose info string contains a backtick, and closes only on the same character at the same or greater length with nothing else on the line. Fence lines that belong to the task are still printed. Four fixtures are added to
tests/claude-code/test-sdd-workspace.sh: a three-backtick control, a tilde case, a four-backtick fence wrapping a three-backtick block, and an indented three-backtick case, each asserting the line after the example is still in task 1's brief.A second commit, 80f8ed1, allows a trailing carriage return in the closing-fence check and adds two fixtures: a fenced
### Task 2heading inside Task 1, asserting Task 1 keeps its end and Task 2's brief starts at its real heading with none of Task 1; and a CRLF plan whose fenced block must close so Task 2 is found.Is this change appropriate for the core library?
Yes. It is a correctness fix to a shipped core script, no new skill, no project-specific behaviour and no third-party integration.
What alternatives did you consider?
Rejecting tilde fences outright, or changing the task-heading regex to ignore headings inside examples: both leave the nested four-backtick case broken and the second cannot tell an example heading from a real one. A full Markdown parser is out of proportion for a 20-line awk script. Tracking the fence delimiter the way CommonMark defines it is the smallest change that closes every case in the fixtures.
Does this PR contain multiple unrelated changes?
No. One script fix and its tests.
Existing PRs
Environment tested
New harness support (required if this PR adds a new harness)
Not applicable, no harness added.
Evaluation
Rigor
Human review
No CI in the repository;
bash tests/claude-code/test-sdd-workspace.shwas run locally on macOS, all checks passing. The follow-up commit was also run on Linux under gawk, mawk and BWK awk (which macOS's awk is based on), 20 of 20 passing.