Skip to content

fix(traces): stop the segment guard zeroing multi-line commands - #2969

Open
saksharthakkar wants to merge 1 commit into
mainfrom
fix/traces-grader-multiline-continuations
Open

fix(traces): stop the segment guard zeroing multi-line commands#2969
saksharthakkar wants to merge 1 commit into
mainfrom
fix/traces-grader-multiline-continuations

Conversation

@saksharthakkar

@saksharthakkar saksharthakkar commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Motivation

PR #2836's segment guard stops at any newline, so every backslash-continued command scores 0 — skill-platform-traces-feedback-list-filters fell from 1.00 to 0.00 on Claude with identical agent behavior. Dropping the \n term restores multi-line matching without giving back the batched-call fix #2836 was written for.

Summary

The command_executed segment guard scopes a lookahead to "the rest of THIS command":

OLD:  (?:(?!\n|&&|\|\||;|\||\s(?:uip|$UIP)\s).)*
NEW:  (?:(?!&&|\|\||;|\||\s(?:uip|$UIP)\s)[\s\S])*

The safety net assumed in the #2836 review does not exist. coder_eval matches a raw haystack and a shlex-normalized one, but normalization does not collapse a backslash line continuation:

>>> shlex.split("a \\\n b", posix=True)
['a', '\n', 'b']

The escaped newline survives as a literal '\n' token and " ".join(tokens) puts it straight back — now as a bare newline. Both haystacks keep the line break, so every lookahead dies at the first continuation.

Run evidence

Run Task Command shape Score
2026-08-27_04-13-44 (last pre-#2836) skill-platform-traces-feedback-list-filters 2/2 multi-line 1.00
2026-08-31_04-15-47 (post-#2836) same task same 2/2 multi-line 0.00 / 3

Identical agent behavior; #2836 alone flipped it. All three commands in the 08-31 run were correct — just backslash-continued. The sibling task skill-platform-traces-feedback-list-detailed scored 1.00 in the same run only because its commands happened to be single-line; it carries the same latent bug, so both files are fixed here.

Codex is unaffected and keeps the fix it needed: it writes single-line commands and went 6-fail/9 pre-#2836 to 3/3 pass post-#2836.

Why (?<!\\)\n is not enough

A lookbehind that skips only escaped newlines repairs the raw haystack but not the normalized one, where \ + newline has already become a bare newline. That mis-splits one merged command into two fake segments and opens a false positive: control B2 below is a single merged command carrying both --span-id and --agent-id, and the criterion that must reject it starts passing. The \n term buys no scoping that \s(?:uip|$UIP)\s does not already provide, so the fix is to remove it rather than narrow it.

File-by-file

  • tests/tasks/uipath-platform/traces/traces_feedback_list_filters_smoke.yaml — all 3 command_pattern regexes moved to the new guard (8 guard occurrences). The task description no longer claims the lookahead stops at a newline.
  • tests/tasks/uipath-platform/traces/traces_feedback_list_detailed_smoke.yaml — same, all 3 command_pattern regexes (7 guard occurrences). No behavior change today (its commands were single-line), but it removes the latent zero.
  • tests/scripts/test_command_pattern_segment_guard.py (new) — replays a 7-control matrix in single-line and multi-line shape through a faithful copy of coder_eval 0.11.5 _match_haystacks / _normalize_shell (raw + shlex-normalized haystacks, re.DOTALL). It reads the shipped regexes out of the task YAML, so it fails red if the \n term ever comes back, and it pins both rejected alternatives.
  • tests/README.md — corrects the segment-scoping rule: it claimed the grader "also matches a normalized haystack with newlines collapsed to spaces", which is what made the fix(traces): segment-scope list_* graders; stop gating on default sort order #2836 review look sound. Now states the corrected guard and that continuations are not collapsed.
  • .github/workflows/test-helpers.yml — new command-pattern-guard job running the matrix, plus tests/tasks/uipath-platform/** added to the trigger paths so editing the guarded YAMLs re-runs it.

Control matrix

Every control runs in both a single-line and a multi-line (backslash-continued) shape — 14 cells, 42 criterion outcomes.

# Control Expected
A correct 2-call trajectory PASS
B both filters merged into ONE command FAIL c1 only
B2 merged, line break between --span-id and --agent-id FAIL c1 only
C --offset 10 (page number, not zero-based offset) FAIL c3 only
D span read done via list detailed FAIL c1 only
E two reads chained with && in one call PASS
F two reads stacked on separate newlines in one call PASS
Guard Mismatching cells
OLD (currently on main) 7 — every multi-line shape wrongly zeroed (18 of 42 criterion outcomes wrong)
(?<!\\)\n variant 1 — B2 multi-line, where c1 wrongly passes a merged command
proposed (no \n term) 0

E and F stay green, so #2836's batched-call fix is preserved.

The plan for this change predicted 2 mismatches for the (?<!\\)\n variant; the measured matrix produces 1. The hole is the same one (the merged-command false positive) — only the cell count differs, because the single-line rendering of B2 is identical to B and grades correctly.

Tests performed

$ python3 -m pytest tests/scripts/test_command_pattern_segment_guard.py -q
19 passed in 0.03s

$ python3 -m pytest tests/scripts/ -q
122 passed in 29.29s

$ python3 scripts/check-task-driver.py tests/tasks
OK — no task pins `sandbox.driver: tempdir`.

$ python3 scripts/check-cli-verbs.py \
    tests/tasks/uipath-platform/traces/traces_feedback_list_filters_smoke.yaml \
    tests/tasks/uipath-platform/traces/traces_feedback_list_detailed_smoke.yaml
OK — no CLI-verb issues (catalog: uip 1.202.0-dev.8414, 1555 verbs).

Plus a YAML parse and re.compile(..., re.DOTALL) over every command_pattern in tests/tasks/uipath-platform/traces/ (11 files, 30 patterns — all compile), and a YAML parse of the edited workflow.

scripts/check-skill-verbs.py was not run: it takes a skill directory and this PR changes no skill under skills/ or preview/.

The two graded tasks were not re-run end to end against a tenant — a live run needs credentials this change does not affect. The change is to the grader regex only, and the control matrix replays the exact scoring path (_match_haystacks + re.DOTALL search) that a live run would take, including the two real command shapes recorded in runs 2026-08-27_04-13-44 and 2026-08-31_04-15-47.

Test plan

  • All 6 command_pattern regexes in both traces task YAMLs use the continuation-safe guard
  • git grep -F '(?!\n|&&' -- tests/tasks/ returns nothing
  • 7-control matrix passes in single-line and multi-line shape under the shipped guard
  • Old guard and the (?<!\\)\n variant are pinned as failing, so neither can quietly return
  • Batched-call controls (E &&, F stacked lines) still grade per command
  • Existing tests/scripts/ suite unchanged and green
  • Next nightly Claude run scores skill-platform-traces-feedback-list-filters above 0

LLMOPS-3152

🤖 Generated with Claude Code

The `command_executed` segment guard stopped at any newline, including a
backslash line continuation, so every multi-line command scored 0. Drop
the `\n` term and match `[\s\S]`; `\s(?:uip|$UIP)\s` is what actually
separates stacked commands.

Adds a 7-control replay matrix (single-line and multi-line shapes) that
grades through a copy of coder_eval's haystack matching, and corrects the
false claim in tests/README.md that normalization collapses newlines.

LLMOPS-3152

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Claude finished @saksharthakkar's task in 1m 51s —— View job


Coder-eval task lint (advisory)

2 task YAMLs changed; verdict counts: 0 Critical, 0 High, 0 Medium, 0 Low, 2 OK.

Rubric: .claude/commands/lint-task.md. This check is advisory and never blocks merge.

Evidence of passing run

High — PR body explicitly states "The two graded tasks were not re-run end to end against a tenant" and the test-plan item "Next nightly Claude run scores skill-platform-traces-feedback-list-filters above 0" is unchecked.

The author does provide strong indirect evidence: a 7-control × 2-shape matrix test (test_command_pattern_segment_guard.py, 19 passed) that replays the exact _match_haystacks + re.DOTALL scoring path coder-eval takes, plus run data from 2026-08-27_04-13-44 (pre-regression) and 2026-08-31_04-15-47 (post-regression) demonstrating the failure. Given the change is regex-only and the control matrix is comprehensive, this is a reasonable substitute — but per convention, please consider adding a line to the PR description confirming the tasks pass once the next nightly lands, e.g.: Ran skill-platform-traces-feedback-list-filters locally and it passed.

Per-task lint

tests/tasks/uipath-platform/traces/traces_feedback_list_filters_smoke.yaml — verdict: OK

All three command_executed criteria use segment-scoped lookaheads with substantive flag-combination checks (span scoping with --span-id but not --agent-id; sentiment + agent attribution; pagination with exact --limit 5 --offset 5). No self-report, no over-specification, no gameability. Well-differentiated from all five nearest siblings (traces_feedback_smoke, _mutate_smoke, _negative_smoke, _comment_file_smoke, _list_detailed_smoke) — each tests a distinct operation surface.

tests/tasks/uipath-platform/traces/traces_feedback_list_detailed_smoke.yaml — verdict: OK

Three command_executed criteria cover time-window filters (--since 24h, --after/--before), category scoping, and sort-order negative assertion. The file_contains check on detailed.json for '"Result"' (weight 1.0) validates the CLI response envelope — low-weight supplementary check alongside the primary command-shape criteria (weight 2.0–2.5). Not a near-duplicate of the filters task: different subcommand (list detailed vs list), different filter surface, different assertions.

Within-PR duplicates

No duplicate clusters detected. Both tasks test distinct subcommands with different filter surfaces.

Conclusion

✅ All changed tasks pass the rubric. Evidence of passing run is pending the next nightly — the control matrix test provides strong interim coverage for a regex-only change.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Claude finished @saksharthakkar's task in 2m 41s —— View job


Summary

Fixes a regression from PR #2836 where the command_executed segment guard stopped at any \n, zeroing every backslash-continued command. The fix removes \n from the guard's stop list and changes . to [\s\S] for explicit newline matching, while preserving the batched-call scoping that #2836 introduced. A thorough 7-control × 2-shape test matrix pins the fix and prevents reintroduction.

Change-by-Change Review

1. tests/tasks/uipath-platform/traces/traces_feedback_list_filters_smoke.yaml

Severity: OK

All 3 command_pattern regexes (8 guard occurrences) correctly updated: \n removed from the negative lookahead, . changed to [\s\S]. The description field at line 10 is also corrected to no longer claim the guard stops at newlines. The patterns are consistent and compile under re.DOTALL.

2. tests/tasks/uipath-platform/traces/traces_feedback_list_detailed_smoke.yaml

Severity: OK

Same mechanical guard update across all 3 command_pattern regexes (7 guard occurrences). No behavioral change today (its commands were single-line in recorded runs), but removes the latent scoring bug. Consistent with the filters task.

3. tests/scripts/test_command_pattern_segment_guard.py (new, 211 lines)

Severity: OK

Well-structured regression test. Key strengths:

  • Faithfully copies coder_eval 0.11.5's _normalize_shell / _match_haystacks surface — correct approach given coder_eval isn't a test dependency.
  • test_filters_criteria_match_the_replayed_templates (line 191) ties the test's CRITERIA templates to the actual shipped YAML, catching template drift.
  • test_shipped_guard_never_stops_at_a_newline (line 177) checks both YAML files for substring presence of both rejected guard variants — clean regression gate.
  • Controls E (chained &&) and F (stacked newlines) verify fix(traces): segment-scope list_* graders; stop gating on default sort order #2836's batched-call fix is preserved.
  • Regression-pinning tests (lines 204, 209) lock in the failure modes of the old guard and the (?<!\\)\n variant, preventing either from quietly returning.

The control matrix covers all the important edge cases (correct two-call, merged single-command, break-between-flags, wrong offset, wrong subcommand, chained, stacked).

4. tests/README.md

Severity: OK

Two corrections at lines 441 and 448:

  • "also matches a normalized haystack with newlines collapsed to spaces" → "also matches a shlex-normalized haystack" — fixes the false claim that made fix(traces): segment-scope list_* graders; stop gating on default sort order #2836's review look sound.
  • Updated segment idiom example and added a clear "Do NOT put \n in that stop list" paragraph with the technical rationale (shlex behavior, both haystacks preserving the newline). Good guardrail for future task authors.

5. .github/workflows/test-helpers.yml

Severity: OK

  • New command-pattern-guard job follows the same structure as sibling jobs (pinned action SHAs, Python 3.13).
  • Correctly installs pyyaml alongside pytest (the test imports yaml).
  • tests/tasks/uipath-platform/** added to trigger paths — correct, since the test reads YAML files from that directory and edits to those files should re-run the guard.

What's Missing

Nothing identified. The PR is well-scoped: it fixes exactly the regression, updates the documentation that led to the incorrect review, and adds a comprehensive regression test. The sibling trace tasks that don't use the segment guard idiom (e.g., traces_feedback_comment_file_smoke.yaml using unscoped [\s\S]* lookaheads) are a pre-existing pattern outside this PR's scope.

Area Ratings

Area Status Notes
Frontmatter OK Task YAML frontmatter unchanged and valid
E2E Tests OK Existing tasks updated; new regression test added with CI job
Skill Body N/A No skill files under skills/ modified
References & Assets OK tests/README.md corrected accurately
Repo Hygiene OK No secrets, no cross-skill deps, scoped changes, CODEOWNERS not needed (no new skill paths)

Issues for Manual Review

  • The coder_eval _normalize_shell copy is pinned to v0.11.5. If coder_eval changes its haystack matching in a future version, this test could diverge. The comment at line 30 documents this — acceptable tradeoff since coder_eval isn't a test dependency.
  • The final test-plan checkbox ("Next nightly Claude run scores above 0") is pending — worth confirming after merge.

Conclusion

Clean PR. The root-cause analysis is rigorous (shlex behavior with backslash continuations), the fix is minimal (remove \n from the stop list, use [\s\S] for explicitness), and the 7-control × 2-shape matrix is thorough enough to prevent reintroduction. All CI checks pass. No issues found — approve.

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