Skip to content

fix(pm-tooling): derive a job's path population from the dorny/paths-filter block its if: names - #13049

Merged
os-elon merged 2 commits into
mainfrom
claude/issue-12956-paths-filter-derivation
Aug 29, 2026
Merged

fix(pm-tooling): derive a job's path population from the dorny/paths-filter block its if: names#13049
os-elon merged 2 commits into
mainfrom
claude/issue-12956-paths-filter-derivation

Conversation

@os-elon

Copy link
Copy Markdown
Collaborator

Part of #12956

Direction 1 only, as dispatched. dispatch-gates read exactly one path declaration CI obeys — a workflow's on.pull_request.paths. ci.yml declares none: its filtering lives in a filter job whose dorny/paths-filter step computes per-area outputs, which every other job reads in its own if:. That one hop is why a single-file .objectui-sha diff derived zero families.

The derivation now follows needs.JOB.outputs.NAME back to the dorny/paths-filter block that defines it, through the job's own outputs: indirection, and uses those globs as that job's path population.

The pinned acceptance criterion — before / after

Single-file .objectui-sha diff, both runs on this tree.

Before (8cb96ec41b):

No check family names the given paths in its own source, and no workflow's path filter schedules one for them.
0 matched above · 36 undetermined · 145 silent

After (7528206d36):

Local gates for this card (paste into the dispatch prompt):
- pnpm check:console-injection [ci.yml] matched via .objectui-sha ⇢ CI job filter for 'Console Pin Gate' in ci.yml '.objectui-sha'
- pnpm check:console-sha [ci.yml, cut-rc.yml, release.yml, showcase-smoke.yml] matched via .objectui-sha ⇢ CI job filter for 'Console Pin Gate' in ci.yml '.objectui-sha'

Console Pin Gate and check:console-sha are both named, by the JOB NAME a dev sees go red rather than the console-pin job id. Family growth is not offered as evidence: the card already proved an 8 to 33 family jump was unrelated to hitting the pin gates.

What was measured, not inherited

The "4 outputs / 9 sites" figure, re-derived.grep -c returns 9 lines in ci.yml, and 2 of the 9 are prose inside the FILTER CONTRACT docblock (lines 51 and 63). Live conditional sites: 7, carrying 8 references (line 269 carries two). Distinct outputs: 4 (console, core, crosspkg, docs) — that half holds exactly.

How many of the 7 were actually missed. Two of the seven jobs (temporal-conformance, build-docs) invoke no check:* family at all, so there is nothing there to name. The other 5 were all missed — every family they run gained a match it did not have. Measured old-tool vs new-tool, one probe per filter:

probe pathfiltermatched beforematched afternewly named
.objectui-shaconsole02check:console-sha, check:console-injection
packages/spec/src/index.tscore2629check:dual-build-cjs-loads, check-shard-attestation, check-test-completeness
content/docs/x.mdxcrosspkg2325check-shard-attestation, check-test-completeness
apps/docs/x.tsxdocs66none — build-docs runs no check family
AGENTS.mdnone88none (negative control)

Losses: zero on every probe. Re-attributions: exactly one family, scripts/check-cross-package-test-inputs.mjs, whose provenance label moves from gate source via … to the job filter. Verdict unchanged; only the column that justifies the lead changes, and the stronger provenance is the one printed.

Six families gained a job-level declaration in total, which the residue counts independently: 166 → 160 "reached by neither path declaration".

Two honesty consequences, both in this PR

Neither is tidying — each is a line the tool prints that this change would otherwise make false.

  1. The residue's unfiltered count said "no path derivation can narrow them" about six families the same run had just narrowed. It now counts families reached by neither declaration.
  2. check-test-completeness is unreachable (its own literals are package names) and now scheduled from core/crosspkg. The listing's blanket "CI runs these on every pull request" is false of it, so that claim is stated per entry and counted.

What this deliberately does NOT do

  • check:objectui-pin-citations gets no path filter. lint.yml states the reason in its own step comment: a filter on packages/spec/** "would go dormant on exactly the PR that moves .objectui-sha, which is the PR this exists to catch." Its underivability is a consequence of its correctness. It remains undetermined after this change, and that is the honest verdict.
  • ⛔ No declaration idiom for deliberately-unfiltered gates (direction 2). That is a gate-semantics design change and needs its own card.
  • ⛔ The residue buckets' output format is untouched.

Refusals, each costing a missing lead rather than a fabricated one

  • An if: contributes a population only when it reduces exactly to filter-output comparisons joined by ||. && between two outputs is an intersection this returns null for; an inverted or unreadable comparison refuses the whole expression.
  • dorny/paths-filter matches with picomatch; triggerPatternRegex implements GitHub's paths: language, which has no extglob. A glob carrying one is dropped and counted, never translated with the wrong language. Live cost: apps/!(docs)/**, one entry of six in core. A dropped negation would widen the claim instead, so that refuses the whole population.
  • A block-scalar if:, a job with no resolvable filter, and a workflow with no paths-filter step all contribute nothing.

Verification

Gate family re-derived from the actual diff (node scripts/pm/dispatch-gates.mjs, no paths — merge-base semantics), then cross-checked against the pre-change tool on the same path: only additions, zero subtractions. All run on 7528206d36.

gateexit
pnpm check:pm-dispatch-gates0 — 872 cases pass
pnpm check:watch-hint-literal0
node scripts/check-self-test-wired.mjs0
node scripts/check-ci-filter-parity.mjs0
node scripts/check-cross-package-test-inputs.mjs0
node scripts/check-shard-attestation.mjs0
pnpm check:entry-guard · check:parse-guard · check:agent-test-spelling · check:bash32-floor · check:cli-command-ids · check:cross-package-test-inputs · check:pnpm-filter-targets · check:nul-bytes0
pnpm lint (whole repo, eslint . --no-inline-config)0 — 70s, no narrowing claimed

node scripts/check-test-completeness.mjs is not measured locally: it needs a CI-produced turbo test log and exits 1 with a usage line without one. It runs in CI's test job.

Self-test: 834 to 872 cases (38 added — fixture extraction, every refusal, precedence in both directions, and the live acceptance criterion pinned against the real ci.yml).

Ablation (predicted before running, mutation and restore both proven on disk). Removing only the coveringJobFilter branch from coveringKey, with the extraction left intact:

  • predicted the live derivation returns to 0 matched / "No check family names the given paths" — observed exactly that;
  • predicted exactly 2 self-test cases red (the two positive coveringKey assertions), with the null-assertion, the trigger-precedence case, the negative control and all 33 extraction cases staying green — observed 2 of 867 case(s) failed, and they are the two predicted.

Mutation landed on disk: removed-text occurrences 1 → 0, injected marker 1, on-disk hash 0ca99e39… to dc1dad51…. Restore proven three ways: empty git diff HEAD, git hash-object back to the HEAD blob 0ca99e39…, marker count 0; self-test green again at 867 before the later cases landed. No build step is involved — this is a dependency-free node script with no dist/.

Self-referential watch-hint control. This file's own hint set is unchanged at 9 → 9 across the change (added [], lost []), and the instrument is proved live in the same run: a planted code-position literal moves it 9 → 10. So the zero is a reading, not a dead instrument.

Changeset

None, derived not inherited: the diff is one file, scripts/pm/dispatch-gates.mjs, which sits in no packages/* and ships in no tarball. Nothing is released, so a changeset naming a package and a bump would be a false claim. skip-changeset applied.

Generated by Claude Code


Generated by Claude Code

…s dorny/paths-filter block
dispatch-gates read only the workflow-level on.pull_request.paths filter. ci.yml
declares none: its filtering lives in a filter job's dorny/paths-filter step,
read by every other job's own if:. So a single-file .objectui-sha diff derived
ZERO families and the two gates a pin bump exists to run reached the dev only
from CI.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw
…lter can schedule one
An unreachable family's own literals still name nothing, but the job that runs
it may now carry a resolvable paths-filter if: — so the blanket 'CI runs these
on every pull request' is no longer true of every entry. Stated per entry, and
counted, rather than asserted of the set.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/lskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-elon@claude