Skip to content

fix(tests): make the Claude Code skill suite runnable and realign SDD assertions with v6.2.0 - #2071

Open
ericyen97903-lab wants to merge 2 commits into
obra:mainfrom
ericyen97903-lab:fix/sdd-test-stale-setup-assertion
Open

fix(tests): make the Claude Code skill suite runnable and realign SDD assertions with v6.2.0#2071
ericyen97903-lab wants to merge 2 commits into
obra:mainfrom
ericyen97903-lab:fix/sdd-test-stale-setup-assertion

Conversation

@ericyen97903-lab

Copy link
Copy Markdown

tests/claude-code/run-skill-tests.sh does not currently produce a usable signal for test-subagent-driven-development.sh. Two harness defects and two stale assertions compound: the script blocks on stdin, is reported as a 900s timeout, and when it does run it exits at the first of three failing assertions, hiding the rest.

All four are fixed here. The changes are test-only; no skill content is touched.

1. run_claude blocks on inherited stdin

The prompt is passed as an argument, so these calls never want piped input. But when stdin is a non-TTY that never closes (CI, or the suite invoked from a script), claude -p waits on it. The script then hits the runner's 900s per-test cap and is reported as a timeout rather than reporting its assertions.

Redirecting stdin from /dev/null took test-subagent-driven-development.sh from a 900s timeout to a 110s complete run locally.

2. assert_contains matched line by line

Its patterns deliberately link two prose keywords with .* (implementer.*fix, read.*plan), but grep is line-based, so a correct answer that puts those keywords in different paragraphs fails.

Sampling "what happens if a reviewer finds issues? Is it a one-time review or a loop?" three times, the answer contained implementer 3x and fix 7-9x every time, yet implementer.*fix|fix.*issues passed only 1 of 3 runs, purely on how the model happened to wrap lines. In a failing sample, implementer sat on lines 7-8 and fix on lines 3, 5, 9, 19, 21.

Newlines are now flattened before matching. This is confined to assert_contains, and flattening can only turn a FAIL into a PASS, so no currently-passing assertion changes meaning. assert_not_contains and assert_order keep their line-based semantics, and assert_count is line-counting by definition and has no callers.

3. "Read at beginning" still looked for the pre-rename vocabulary

The assertion searched Step 1|beginning|start|Load Plan. The plan-scoped workspace work renamed that phase to Setup, so the model answers "during Setup" and the assertion only passed when the wording happened to also contain "start".

Sampling that prompt 5 times: Setup appeared 5/5, the assertion passed 2/5. Setup is added to the alternation.

4. "Provides text directly" asserts behaviour v6.2.0 removed

The assertion searched provide.*directly|full.*text|paste|include.*prompt. SKILL.md now routes the task through scripts/task-brief: the dispatch carries the brief path, exact values "appear only in the brief", and "Never make a subagent read the whole plan file."

The prompt asks for <directly or by file> and the answer is now consistently "by file" (3/3 samples), so this assertion was testing for the behaviour the rewrite deliberately replaced. It now asserts the brief-file handoff and is renamed to match.

Verification

Two consecutive clean runs of the full runner, no external stdin redirect, exit code 0:

  Passed:  3
  Failed:  0
  Skipped: 0
STATUS: PASSED

15/15 assertions in test-subagent-driven-development.sh. Environment: macOS 15 (arm64), GNU coreutils 9.11 for timeout, Claude Code 2.1.156.

Because assertion 3 and 4 are prose-matching against a model's free-form answer, each was sampled repeatedly rather than trusted on a single green run. Sample counts are given above.

🤖 Generated with Claude Code

ericyen97903-lab and others added 2 commits August 2, 2026 01:30
…stic

Two defects in tests/claude-code/test-helpers.sh made this suite unusable
as a signal.

1. run_claude did not redirect stdin. The prompt is passed as an argument,
   so these calls never want piped input, but an inherited non-TTY stdin
   that never closes (CI, or the suite invoked from a script) makes
   `claude -p` block waiting on it. The script then hits run-skill-tests.sh's
   900s per-test cap and is reported as a timeout instead of reporting its
   assertions. Locally this took test-subagent-driven-development.sh from a
   900s timeout to a 110s complete run.

2. assert_contains matched line by line. Its patterns deliberately link two
   prose keywords with `.*` ("implementer.*fix", "read.*plan"), but grep is
   line-based, so a correct answer that puts those keywords in different
   paragraphs failed. Sampling "what happens if a reviewer finds issues?"
   three times: the answer contained "implementer" 3x and "fix" 7-9x every
   time, yet the assertion passed only 1 of 3 runs purely on how the model
   happened to wrap lines. Newlines are now flattened before matching.

Flattening is confined to assert_contains, and it can only turn a FAIL into
a PASS, so no currently-passing assertion changes meaning.
assert_not_contains and assert_order keep their line-based semantics;
assert_count is line-counting by definition and has no callers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two assertions in test-subagent-driven-development.sh still described
pre-v6.2.0 behaviour. Because the script exits at the first failure, only
the earlier one was ever visible.

1. "Read at beginning" looked for `Step 1|beginning|start|Load Plan`. The
   plan-scoped workspace work renamed that phase to Setup, so the model now
   answers "during Setup" and the assertion only passed when the wording
   happened to also contain "start". Sampling the prompt 5 times: "Setup"
   appeared 5/5, the assertion passed 2/5. Added Setup to the alternation.

2. "Provides text directly" looked for `provide.*directly|full.*text|paste|
   include.*prompt`, but SKILL.md now routes the task through
   `scripts/task-brief`: the dispatch carries the brief path, exact values
   "appear only in the brief", and "Never make a subagent read the whole
   plan file". The prompt asks for "<directly or by file>" and the answer is
   now "by file" (3/3 samples), so the assertion was testing for the
   behaviour the rewrite removed. It now asserts the brief-file handoff and
   is renamed accordingly.

The file header's example keyword list is updated from "Step 1" to "Setup"
for the same reason.

With these and the harness fixes, the suite runs green end to end
(15/15 assertions, exit 0) on macOS with GNU coreutils installed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant