{{ message }}
Repository navigation
Conversation
…efix (obra#2405) task-brief built its heading matcher by string-concatenating the requested task number straight into an awk regex: intask = ($0 ~ ("^#+[ \t]+Task[ \t]+" n "([^0-9]|$)")) Two consequences, both silent (exit 0, plausible-looking brief): 1. Prefix match: in a plan numbered 'Task 1.1', 'Task 1.2', ..., 'task-brief PLAN 1' returned every 'Task 1.x' concatenated, because '.' is a non-digit and satisfied the terminator. 2. Unescaped metacharacters: 'task-brief PLAN 2.1' also returned 'Task 201' ('.' is regex any-char); N='2|3' returned both tasks; N='2(' crashed awk with a raw syntax error. N was never validated. Fix: validate the task number up front (digits, optionally dot-separated -- the only shape a real heading can have) and reject anything else with a clear error and exit 4, instead of feeding it into a regex. The awk program itself now extracts each heading's full number and compares it to the requested number as an exact string, so a prefix can never match a longer sibling number. Added three assertions to tests/claude-code/test-sdd-workspace.sh that reproduce the issue's exact repro (confirmed RED against the unmodified script, GREEN after the fix): task '1' no longer absorbs '1.1'/'1.2', task '2.1' no longer absorbs '201', and a metacharacter task number is rejected with exit 4 instead of an awk crash. Fixes obra#2405.
Contributor
Author
rahul05ranjan
left a comment
There was a problem hiding this comment.
Verified end-to-end on a fresh clone, running the old version (from main) and this PR's version side by side against a plan with Task 1.1/1.2/2.1/201 headings:
- All four failure modes from #2405 reproduce against
main:task-brief plan 1returns the1.1+1.2sections concatenated with exit 0;2.1also capturesTask 201(the.matching as any-char);2|3returns both tasks 2 and 3;2(dies with a raw awk regex-compile error. With this PR the first case exits 3 with a clear "task not found" message,2.1extracts onlyTask 2.1, and the metacharacter inputs exit 4 with a clean usage message instead of the awk crash. - The validation glob (
skills/subagent-driven-development/scripts/task-brief:28-33) caught everything adversarial I threw at it:.,..,1.,.1,1..2,a,1a, and empty string all exit 4. Since it runs beforeout="$dir/task-${n}-brief.md"is built, odd inputs also can't leak into a brief filename anymore. tests/claude-code/test-sdd-workspace.shpasses in full on Linux ARM (29/29), including the three new assertions.- Grepped the rest of the repo for other
Task Nheading parsers — this script is the only one, so no sibling carries the same bug, and nothing in the skill docs keys on the script's exit codes, so the new exit 4 doesn't break any caller.
Two non-blocking notes:
- Letter-suffixed headings (
## Task 6b) still match their numeric prefix:match()extracts6and the== ncomparison succeeds. That's identical to pre-PR behavior and the PR body already scopes it to #2176 — just flagging it so nobody reads "exact string match" as covering that case too. - exit 4 is new in this script's vocabulary (2 = usage/no plan, 3 = task not found). Distinct codes are arguably better for the caller, so this is only an observation in case the repo wants a convention for it.
Comparing the heading's own extracted number as a plain string is the right call here — escaping alone wouldn't have fixed the prefix case, as the alternatives section notes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Who is submitting this PR? (required)
What problem are you trying to solve?
skills/subagent-driven-development/scripts/task-briefextracts one task'stext from a plan by string-concatenating the requested task number directly
into an awk regex:
This has two silent failure modes (issue #2405):
Task 1.1,Task 1.2, ...), runningtask-brief PLAN 1returns everyTask 1.xconcatenated together, because
.counts as the "non-digit" terminatorthe regex uses to stop a plain integer match.
escaped before being spliced into the regex:
task-brief PLAN 2.1alsoreturns
Task 201(the.in2.1matches as regex "any character"),a task number like
2|3returns both tasks, and a task number like2(crashes awk with a raw syntax error instead of a clean usage message.
Every failure mode above exits 0 (or an unhandled awk crash) and hands the
implementer a brief that silently contains the wrong text, or more than one
task's text concatenated together — the brief is the implementer's single
source of requirements, so this is a correctness bug in the one thing the
script exists to get right.
What does this PR change?
Validates the task number up front against the only shape a real
Task Nheading can have (digits, optionally dot-separated, e.g.
3or2.1),rejecting anything else with a clear error and exit 4. The awk program
itself no longer interpolates the task number into a regex at all — it
extracts each candidate heading's full number and compares it to the
requested number as an exact string, so a shorter number can never
prefix-match a longer sibling.
Is this change appropriate for the core library?
Yes —
task-briefis a core SDD script shipped with the plugin, used byevery subagent-driven-development run. This is a correctness fix to
existing shipped behavior, not a new feature, integration, or
project-specific addition.
What alternatives did you consider?
Would fix the injection/crash case but not the prefix-match case (a
plain digit search still can't distinguish "1" from "1.1" without also
changing the terminator logic). Doing the comparison as a plain string
equality after extracting the heading's own number fixes both problems
in one change and is simpler to reason about than an escaped,
hand-built regex.
[^0-9.]instead of[^0-9]). This still leavesnunescaped and vulnerable to themetacharacter/injection case, and doesn't handle
2.1vs201cleanlysince the terminator approach never actually captures the heading's own
number for comparison.
Does this PR contain multiple unrelated changes?
No — one script, one behavior (exact task-number matching), with the test
coverage for that behavior in the same commit.
Existing PRs
cause — still admits
.and doesn't touch this prefix/injection bug),task-brief: track fence character and length so fenced examples cannot end a task early #2304 (fence tracking — a different bug in the same script), fix(sdd): stop task-brief leaking trailing plan sections #2359
(trailing-section leakage — a different bug in the same script), fix(sdd): support alphanumeric task IDs #2466
(alphanumeric task IDs — a different, additive feature). None of these
change the number-matching logic this PR fixes; "none found" for this
specific bug.
Environment tested
Evaluation
the session that led to this change? A recurring nightly task to survey
open issues in this repo and fix one small, clearly-specified,
verifiable bug.
full
tests/claude-code/test-sdd-workspace.shsuite (28 assertions,all passing) plus the adjacent
test-executing-plans-scripts.shandtest-worktree-path-policy.shsuites to check for regressions.three new assertions reproducing issue task-brief: task number is an unanchored, unescaped regex prefix, so the wrong task is returned with exit 0 #2405's exact repro all failed
(confirmed RED against the unmodified script — prefix match returned
concatenated tasks,
2.1returned201too, and2(crashed awk).After: all three pass, and no previously-passing assertion in the
suite regressed.
Rigor
superpowers:writing-skillsandcompleted adversarial pressure testing (paste results below)
(the new tests cover the prefix-collision case, the "longer sibling
number" case, and a deliberately malicious/malformed task number)
rationalizations, "human partner" language) without extensive evals
showing the change is an improvement
Human review
Per the note above: this PR has not yet had a human review pass. The
change is two files (one 23-line script diff, one 53-line test addition)
and I believe it is ready for maintainer review, but I'm leaving this box
unchecked rather than checking it falsely.