Testdata has no block-style branch:/path: fixture, so scoped-trigger parsing is unpinned
#22
Notifications
Total Time Spent: 18 minutes
oleks
18 minutes
No due date set.
Dependencies
No dependencies set.
Reference: oleks/pipetree#22
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why:
pipetree's parser correctly handles BLOCK-style trigger scoping —verified empirically on 2026-08-13:
pipetree -C ~/projects/gitea-mcp -allreads that config's trigger asbranch=main event=push(correctly scoped), and reads~/projects/pi-plugins/clusteras bareevent=push OR event=pull_request. So the behavior is right today.But nothing in the test suite pins it. Measured in the repo at that date:
*.yamlfiles underinternal/testdata.branch:occurrence across all of them is flow-stylebranch: [main](4x) or scalarbranch: main(2x), plus one unrelatedcommit-branch: "${CI_COMMIT_BRANCH}".branch:followed by an indented- item) fixtures. Same forpath:.Why it matters now specifically: commit
278a567on oleks/emmett just deleted the hand-rolledcheck-woodpecker-redundant-triggers.xshand cut the pre-pushWPRslot over topipetree lint. That retired check had a bug — oleks/emmett#385 — where block-style scoping was misparsed as an unscoped trigger, producing a hard-failing false positive on correctly-scoped configs. pipetree is now the SOLE guard for that class of defect, and the precise input style that broke its predecessor is untested in its own suite. A future parser refactor could regress it silently, and the regression would land straight in everyone's pre-push hook.Note a fix for the xonsh script (oleks/emmett PR #390) did add four fixtures covering block-style branch, block-style path, flow-style, and the genuinely-redundant bare pair — but that PR was closed unmerged as superseded by the cutover, so those fixtures were deleted with it. This is the request to recreate equivalent coverage in the linter that actually survived.
Acceptance:
branch:(indented- itemlist) and assert the trigger parses as SCOPED, not bare.path:and assert the same.branch: [main].Links: oleks/emmett#385, oleks/emmett#380, oleks/pipetree#20, oleks/emmett PR #390 (closed unmerged, superseded)
Done in
d6a0d37→ PR oleks/pipetree#23 (test-only; no parser or rule code touched). Auto-closes on merge — unlike the emmett-side issues, this needs no deploy gate, since the change adds coverage without altering behavior.Acceptance:
branch:fixture asserting the trigger parses as SCOPED —wp001_blockbranch_clean.yaml, modelled on~/projects/gitea-mcp, the real config that surfaced oleks/emmett#385path:fixture asserting the same —wp001_blockpath_clean.yaml, with two entries so a first-item-wins parse can't pass by accidentTestWP001_CleanCasesalongside the existing flow-style coverageOne finding worth recording, because it changes what this issue was asking for
The third criterion — "assert the redundant-trigger rule stays quiet" — turns out to be insufficient on its own, and satisfying only it would have produced a test that looks like protection but isn't.
A parser that loses the block-style value still leaves the
branchkey in the condition. WP001's bareness check only asks whether a key other thaneventis present, so it stays quiet whether or not the scoping actually survived. Rule silence cannot distinguish "scoping understood" from "scoping lost" — the two failure modes are indistinguishable at the verdict level.So I added
TestWP001_BlockStyleScopingParsesAsScoped, which asserts the constraint actually lands inCondition.Fields. Verified by mutation rather than by argument — makingscalarOrListreject non-flow sequences (simulating oleks/emmett#385) gives:Only the new parse test catches it. The two fixtures this issue asked for, wired in exactly as specified, pass straight through the regression. The mutation was reverted and no parser code is in the branch.
Incidental
Adding fixtures tripped the oracle's corpus-drift guard (
corpus has 26 fixtures but golden records 24) — working as designed, and a good guard. Regeneratedverdicts.jsonwith the realwoodpecker-clivia-updaterather than hand-editing it, so the new entries are genuine captured ground truth: Woodpecker's own parser reports both fixturesvalid: true,has_when: true,steps: 1. No existing entry changed.go test ./...green;go vetclean on the touched packages.⏱ worked | 13-08-26 | session oleks/e17a29e9 | +6min | session-total 6min (lane-release) | elapsed from timeline
Done in
d078f88.Three fixtures and four assertions covering block-style
branch:,path:and
event:— an indented list rather than a same-line scalar. Confirmedthe gap first: every pre-existing fixture used flow style (
branch: [main])or a scalar, so block style had zero coverage.
Mutation-checked rather than assumed. A passing test proves nothing
about whether it would catch the regression, so I reintroduced the
predecessor's bug (ignore block sequences in
conditionFromMap) andconfirmed all four assertions fail — the branch/path ones with a diagnostic
that names oleks/emmett#385 outright:
Then reverted;
parse.gois unchanged in the commit.Two design points worth recording:
The tests assert the parsed CONDITION, not just the verdict. A rule
staying quiet is weak evidence: if
branch:alone were dropped the entrywould have one key and WP001 would fire, but if BOTH keys were dropped the
rule would also stay silent while the config went effectively unanalyzed.
Checking the fields distinguishes those two.
The clean/fires pair is load-bearing, not tidiness. Block style means
SCOPED in two fixtures and TWO UNSCOPED EVENTS in the third, so a change
that mishandled block sequences could otherwise satisfy the scoping tests
by dropping list contents entirely — passing for exactly the wrong reason.
The path fixture doubles as WP022 cover, since
hasPathScopingreads thesame parsed field for a different purpose.
woodpecker-cli accepts all three, so the oracle golden moves 24 → 27.
⏱ worked | 13-08-26 | session oleks/32d802d6 | +12min | session-total 12min (lane-release) | elapsed from timeline