test(lint): pin block-style branch/path scoping for WP001 (oleks/pipetree#22) #23

Closed
oleks wants to merge 1 commits from test/pipetree-22-blockstyle-fixtures into main
Owner

Closes oleks/pipetree#22. Test-only — no parser or rule code is touched.

Why now

Every branch: in the corpus was flow-style [main] (4x) or scalar main (2x). Zero block-style:

branch:
  - main

The parser handles it correctly, but nothing pinned that. It matters because 278a567 on oleks/emmett deleted the hand-rolled check-woodpecker-redundant-triggers.xsh and cut the pre-push WPR slot over to pipetree lint — and block-style scoping is the exact input that defeated that predecessor (oleks/emmett#385), where it parsed as an unscoped trigger and hard-failed correctly-scoped configs. pipetree is now the sole guard for this class, so a silent regression lands straight in everyone's pre-push hook.

What's here

Two _clean fixtures, both wired into TestWP001_CleanCases:

  • wp001_blockbranch_clean.yaml — block branch:, modelled on the real config that surfaced #385 (~/projects/gitea-mcp)
  • wp001_blockpath_clean.yaml — block path: with two entries, so a first-item-wins parse can't pass by accident

The part worth reviewing: rule silence is not enough

I added a separate TestWP001_BlockStyleScopingParsesAsScoped asserting the constraint actually lands in Condition.Fields, because the fixtures alone would not have caught the regression they exist for.

A parser that drops the list value still leaves the branch key in the condition, so WP001 sees a non-event key and stays quiet either way. Silence can't distinguish "scoping understood" from "scoping lost".

This isn't theoretical — I verified by mutation. Making scalarOrList reject non-flow sequences (simulating #385) gives:

--- PASS: TestWP001_Fires
--- PASS: TestWP001_EventListFires
--- PASS: TestWP001_CleanCases          <-- including both new fixtures
--- FAIL: TestWP001_BlockStyleScopingParsesAsScoped
--- PASS: TestWP001_RemediationCarriesClass2Caveat

Only the new parse test fails. The mutation was reverted; git diff confirms no parser code is in this branch.

Golden file

verdicts.json regenerated via go test ./internal/oracle -run TestGolden -update against the real woodpecker-cli, not hand-edited — the oracle corpus is globbed, so new fixtures would otherwise trip its drift guard (corpus has 26 fixtures but golden records 24). Woodpecker's own parser independently reports both fixtures valid: true, has_when: true, steps: 1. No existing entry changed (+12 lines, 0 deletions).

Verification

go test ./... green. go vet clean on the touched packages.

Closes oleks/pipetree#22. Test-only — no parser or rule code is touched. ## Why now Every `branch:` in the corpus was flow-style `[main]` (4x) or scalar `main` (2x). Zero block-style: ```yaml branch: - main ``` The parser handles it correctly, but nothing pinned that. It matters because `278a567` on oleks/emmett deleted the hand-rolled `check-woodpecker-redundant-triggers.xsh` and cut the pre-push `WPR` slot over to `pipetree lint` — and block-style scoping is the **exact** input that defeated that predecessor (oleks/emmett#385), where it parsed as an unscoped trigger and hard-failed correctly-scoped configs. pipetree is now the sole guard for this class, so a silent regression lands straight in everyone's pre-push hook. ## What's here Two `_clean` fixtures, both wired into `TestWP001_CleanCases`: - `wp001_blockbranch_clean.yaml` — block `branch:`, modelled on the real config that surfaced #385 (`~/projects/gitea-mcp`) - `wp001_blockpath_clean.yaml` — block `path:` with **two** entries, so a first-item-wins parse can't pass by accident ## The part worth reviewing: rule silence is not enough I added a separate `TestWP001_BlockStyleScopingParsesAsScoped` asserting the constraint actually lands in `Condition.Fields`, because the fixtures alone would **not** have caught the regression they exist for. A parser that drops the list value still leaves the `branch` **key** in the condition, so WP001 sees a non-`event` key and stays quiet either way. Silence can't distinguish "scoping understood" from "scoping lost". This isn't theoretical — I verified by mutation. Making `scalarOrList` reject non-flow sequences (simulating #385) gives: ``` --- PASS: TestWP001_Fires --- PASS: TestWP001_EventListFires --- PASS: TestWP001_CleanCases <-- including both new fixtures --- FAIL: TestWP001_BlockStyleScopingParsesAsScoped --- PASS: TestWP001_RemediationCarriesClass2Caveat ``` Only the new parse test fails. The mutation was reverted; `git diff` confirms no parser code is in this branch. ## Golden file `verdicts.json` regenerated via `go test ./internal/oracle -run TestGolden -update` against the **real** `woodpecker-cli`, not hand-edited — the oracle corpus is globbed, so new fixtures would otherwise trip its drift guard (`corpus has 26 fixtures but golden records 24`). Woodpecker's own parser independently reports both fixtures `valid: true`, `has_when: true`, `steps: 1`. No existing entry changed (+12 lines, 0 deletions). ## Verification `go test ./...` green. `go vet` clean on the touched packages.
oleks added 1 commit 2026-08-13 22:27:54 +03:00
Every `branch:` in the corpus was flow-style `[main]` or scalar `main`; no
fixture used the block form

    branch:
      - main

so the parser's handling of it was correct but unpinned. That matters now
because oleks/emmett 278a567 deleted the hand-rolled xonsh check and cut the
pre-push WPR slot over to `pipetree lint` -- and block-style scoping is the
exact input that defeated that predecessor (oleks/emmett#385), where it read
as an unscoped trigger and hard-failed correctly-scoped configs. pipetree is
now the sole guard for the class, so a silent regression here would land
straight in everyone's pre-push hook.

Adds two `_clean` fixtures (block `branch:`, and block `path:` with two
entries so a first-item-wins parse cannot pass by accident), both wired into
TestWP001_CleanCases.

The verdict assertion alone is NOT sufficient, which is the reason for the
separate TestWP001_BlockStyleScopingParsesAsScoped: a parser that drops the
list value still leaves the `branch` KEY in the condition, so WP001 sees a
non-`event` key and stays quiet whether or not the scoping survived. Silence
cannot tell "understood" from "lost".

Verified by mutation rather than by assertion alone -- making scalarOrList
reject non-flow sequences (simulating oleks/emmett#385) leaves
TestWP001_Fires, _EventListFires, _CleanCases and _RemediationCarriesClass2Caveat
all PASSING and fails only the new parse test. The mutation was reverted; the
diff here touches no parser code.

verdicts.json regenerated with the real woodpecker-cli via `-update`, not
hand-written: the oracle corpus is globbed, so new fixtures would otherwise
drift it out of sync. Woodpecker's own parser independently reports both as
valid with a when-block and one step, and no existing entry changed.
Author
Owner

Closing as superseded — not merging. d078f88 ("test: pin block-style trigger scoping, the shape that broke the predecessor") landed on main from a parallel session and closed oleks/pipetree#22 at 23:01, while this PR sat open.

It's a strict superset of this branch, and it would conflict outright — we independently created the same two fixture filenames (wp001_blockbranch_clean.yaml, wp001_blockpath_clean.yaml) and both regenerated verdicts.json.

What landed has everything here plus more:

this PR d078f88
block branch: / block path: fixtures
parse-level assertion (push.Fields[key], not just rule silence) — same conclusion, independently
block-style event-list fires case wp001_blockevent_fires.yaml
WP022 interaction (TestBlockStylePathSatisfiesWP022)
oracle golden 24 → 26 24 → 27

Worth recording that the other session reached the same core conclusion I did — that asserting WP001's silence is not sufficient, because a parser that drops the value leaves the branch key in place and the rule stays quiet either way, so the test must assert the parsed Fields. Two independent derivations of that is decent evidence it's the right shape for this test.

Nothing here is worth rebasing on top. Branch deleted.

Closing as **superseded** — not merging. `d078f88` ("test: pin block-style trigger scoping, the shape that broke the predecessor") landed on `main` from a parallel session and closed oleks/pipetree#22 at 23:01, while this PR sat open. It's a strict superset of this branch, and it would conflict outright — we independently created the **same two fixture filenames** (`wp001_blockbranch_clean.yaml`, `wp001_blockpath_clean.yaml`) and both regenerated `verdicts.json`. What landed has everything here plus more: | | this PR | `d078f88` | |---|---|---| | block `branch:` / block `path:` fixtures | ✅ | ✅ | | parse-level assertion (`push.Fields[key]`, not just rule silence) | ✅ | ✅ — same conclusion, independently | | block-style **event-list** fires case | ❌ | ✅ `wp001_blockevent_fires.yaml` | | WP022 interaction (`TestBlockStylePathSatisfiesWP022`) | ❌ | ✅ | | oracle golden | 24 → 26 | 24 → 27 | Worth recording that the other session reached the same core conclusion I did — that asserting WP001's silence is **not** sufficient, because a parser that drops the value leaves the `branch` key in place and the rule stays quiet either way, so the test must assert the parsed `Fields`. Two independent derivations of that is decent evidence it's the right shape for this test. Nothing here is worth rebasing on top. Branch deleted.
oleks closed this pull request 2026-08-13 23:26:45 +03:00

Pull request closed

This pull request cannot be reopened because the branch was deleted.
Sign in to join this conversation.