Repository navigation
lint.yml's "Template version-time rewriter self-test" comment carries the same retired scheduling claim, plus a wrong picomatch version #10045
Description
Found while working #10016 (which fixes the other carrier of this claim, the GAP 1 header in scripts/sync-template-versions.mjs). Filed unassigned. Not folded into that PR: #10016's declared file surface is scripts/sync-template-versions.mjs, and its ruling is explicit that the workflow/gate-wiring surface belongs to the change that just landed there — so this is reported rather than edited.
.github/workflows/lint.yml, the comment above the Template version-time rewriter self-test step (currently lines ~536-556 on main), states:
Measured with picomatch 4.0.5 (the matcher dorny/paths-filter uses): a diff confined to
scripts/sync-template-versions.mjs yields core=false, so Test Core is skipped ENTIRELY on the PR
that changes the rewriter, and with it the vitest. […] the--union-intostep that DOES pull
create-objectstack back in is a step inside the skipped job. The queue build catches it, one merge
late, as batch collateral
Two separate inaccuracies, measured on main (c1731d023) rather than inferred:
1. Test Core is skipped ENTIRELY is no longer true. core=false is still correct — that filter was not widened — but the test job now ORs in a second filter output. Evaluating the merged filter job's globs against a diff confined to scripts/sync-template-versions.mjs, using picomatch 2.3.1 with {dot: true} and the SOME quantifier exactly as dorny/paths-filter@v4's createRuleItem does:
docs => false
core => false
console => false
scripts => true (MATCH scripts/**)
so if: ${{ !cancelled() && (needs.filter.outputs.core != 'false' || needs.filter.outputs.scripts != 'false') }} resolves to RUN. The --union-into step is therefore no longer "inside the skipped job", and the queue build is no longer the first signal. Traced further, the vitest itself runs too: --union-into adds @objectstack/spec and create-objectstack, partition-test-shards.mjs places create-objectstack on a shard, and create-objectstack#test declares $TURBO_ROOT$/scripts/sync-template-versions.mjs in turbo.json so the task hash moves and no cached green is replayed (measured: pnpm turbo run test --filter=create-objectstack on such a diff reported Cached: 0 cached, 1 total and ran src/template-version-stamps.test.ts, 8 tests).
2. picomatch 4.0.5 (the matcher dorny/paths-filter uses) names the wrong version. dorny/paths-filter@v4's own package-lock.json resolves picomatch (a runtime, non-dev dependency, ncc-bundled into dist/index.js) to 2.3.1; 4.0.5 is what this repo has in its tree. The two happen to agree on these globs — I evaluated under both and got the same four verdicts — so nothing downstream is wrong today, but the parenthetical asserts the action's behaviour while quoting a version the action does not carry, which is exactly the sort of citation the next author would trust without re-measuring.
The rest of that comment is unaffected and worth keeping: lint.yml carries no paths filter and no filter job, so the step runs on every pull request, push and merge-queue build, and that is still the reason the flag lives there rather than as more cases in the vitest file. What changed is only which gap justifies it — the scheduling half is closed, the red-paths half (GAP 2) is not.
Same class as #9651 and #10016: a rationale comment that stopped being true and would have the next reader re-derive the wrong conclusion.
Refs: #10016 (the sibling carrier, fixed), #9829 / #10014 (what closed the scheduling half), #9348 (the card that added the flag), #10015 (the filter's other missing roots — a different question from this claim).
Generated by Claude Code
Activity
claude commentedon Aug 19, 2026
Claim: PM loop round 23
Session:
session_01XqDQYVU5smx29ts9pAErjaBranch:
claude/issue-10045-lintyml-retired-claimWorktree:
objectstack-issue-10045Domain:
domain:devxFile surface:
.github/workflows/lint.yml(the comment above the "Template version-time rewriter self-test" step, ~536-556) (stop on breach; explain in the report)Container & model: S, mode:subagent, model: opus (
--tieron this path returns "no path-derived mandate … This line is a FLOOR, never a clearance"; content limbno— comment prose only)Clause-②: no
Serial constraints cleared: swept all open PRs for any touching
.github/workflows/lint.yml— none. Both predecessors on this file are MERGED: PR #10005 (#9840, the governed-surface list comment) and PR #10014 (#9829, thecorefilter fix whose landing is what made this claim false).Generated by Claude Code
os-steve commentedon Aug 19, 2026
{ "issue": 10045, "status": "done", "branch": "claude/issue-10045-lintyml-retired-claim", "pr": "https://github.com/objectstack-ai/objectstack/pull/10052", "premise_still_valid": true, "summary": "Rewrote the comment above lint.yml's `Template version-time rewriter self-test` step (lines 536-568 on main) to RECORD that its scheduling claim retired rather than delete it, with the mechanism attached, and corrected the picomatch version. Comment prose only: 46 insertions / 23 deletions, every changed line a `#` comment (`git diff -U0 | grep -cvE '^[+-] #'` = 0), and both versions parsed with the `yaml` package come out byte-identical on the executable surface. One deviation from a literal transcription worth flagging: the old sentence said `create-objectstack#test` is reachable only from ci.yml's `test` job; `rerun-safety-nightly.yml` also runs `turbo run test` over everything, but on `schedule`/`workflow_dispatch` only, so the rewrite says 'at PR time'. NOTE on the dispatch context: PR #10047, the sibling that fixes the twin in `scripts/sync-template-versions.mjs`, is OPEN and NOT MERGED (same base sha 2ee7c453b5 as this branch), so that carrier is still stale on `main` today. That did not change my work — different file, no conflict — but the dispatch note read as if it had landed.", "tests": "All at final commit d169eda16d (worktree clean, `git status --porcelain` empty). Gate union re-derived with `node scripts/pm/dispatch-gates.mjs` (no path arguments; it took the change set from merge base 2ee7c453b) -> 1 path, 8 families, all run, all exit 0, each verdict quoted from the gate's own judgment line (exit status captured by redirect-first, never after a pipe): check:nul-bytes `OK (scanned 6358 text file(s) ... no raw ASCII control bytes)`; check:node-version `OK (30 setup-node step(s) across 27 workflow(s), all on Node 22)`; check:workflow-status-functions `OK (scanned 27 workflow file(s), 46 job(s), 24 job-level if: expression(s); 9 read needs.*.outputs.*, all naming a status function)`; check:required-contexts `6 required context name(s) pinned across 2 workflow(s); 5 instruction surface(s) scanned against 2 retired name(s)`; check:shard-attestation `2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s)`; check:type-check-coverage `OK - 64/77 workspace packages type-checked (plus the root), 13 in the DEBT ledger, 1 exempt`; check:type-check-debt `--re-measure: OK - 33 ledger entr(ies) re-measured in 238.6s, 1924 raw tsc error(s) total, none above its recorded number` (it refuses to run without a built closure, so `turbo run build --filter='./packages/*' --filter='./packages/*/*'` ran first under the shared /tmp/os-heavy-verify.lock: 70/70 successful in 6m04s -- NO narrowing was declared, the gate really ran). Plus two directly relevant: check:template-version-sync `40 assertions over temp fixtures, running the real CLI` and check:cross-package-test-inputs `All 52 self-test cases passed` / `OK: 12 package(s) read outside themselves`. STRUCTURAL PROOF (ruling 3): both versions parsed with `yaml` 2.9.0 and compared on job ids, every job name/runs-on/needs/if/timeout/permissions/env/strategy, step counts and every step's name/uses/run/if/with/env/id/shell/working-directory/continue-on-error -- `job ids before: lint, typecheck` = `after: lint, typecheck`; `lint` name 'Lint & Repo Gates' both sides, steps 70 -> 70, run: 67 -> 67, uses: 3 -> 3; `typecheck` name 'TypeScript Type Check', steps 44 -> 44, run: 39 -> 39, uses: 5 -> 5; parsed-shape sha256/16 `2d5048dd42a4bc45` on BOTH sides, `PARSED SHAPES IDENTICAL: true`. NO ABLATION was run; there is nothing to ablate in a comment. The one probe with a mutation leg was the turbo hash measurement, and its restore leg is proven byte-identical by the hash returning to its original value: HEAD fdb9dd70563b8fb5 -> edited 2509c3c0a22a8f9d -> restored fdb9dd70563b8fb5.", "open_questions": [], "out_of_scope_findings": [] }The four things you asked for explicitly
H1 — per-sentence grading
The card quoted three; the comment carries more than three, so each was graded on its own rather than as a block.
create-objectstack#testis reachable only from ci.yml'stestjobrerun-safety-nightly.ymlalso runsturbo run testover everything but isschedule+workflow_dispatchonlycorepaths-filter"if:ORsscriptscoreispackages/**… and matches no path underscripts/corewas never wideneddorny/paths-filteruses)"scripts/sync-template-versions.mjsyieldscore=falseturbo ls --affectedreturns ZERO packages (a package-local edit returns 1)$TURBO_ROOT$moves the task HASH, "not what schedules the job"--union-intostep … is a step inside the skipped job"create-objectstackback incheck-cross-package-test-inputs.mjs's own ledger records twice"On the third one you flagged as interesting: it is one sentence in the original, joined by a semicolon, and the two halves grade differently. The consolation half ("the queue build catches it, one merge late") is newly false — not merely redundant, because it asserts which signal comes first, and that changed. The ledger citation after the semicolon is still exactly right: those two entries are historical records of real incidents (PR #8742, queue build 31825946401; and PR #8983, which "dequeued the PR and took two unrelated PRs down as batch collateral"), and neither is about the
scripts/scheduling gap — they are about the shape. Rewriting that paragraph as a block would have thrown the surviving half away, which is why the rewrite keeps the failure shape and the ledger pointer and only retires the "first signal" claim.H2 — the step's remaining justification
Not gone. Narrowed to GAP 2, and GAP 2 is intact — same conclusion #10047 reached for the twin, but re-derived for this step rather than assumed.
GAP 1 is closed for this step too: every input that can break this rewriter now schedules
Test Core— the script itself matchesscripts/**, and the template corpus lives underpackages/create-objectstack/, which matchescore'spackages/**.What survives is coverage, and it is real and load-bearing. The self-test owns eight controls (A clean-corpus-unwritten, B missing stamp, C one run names every unstamped surface, D no
@objectstack/*dependency, E missing stamp file, F unparseable package.json, G fixture-table coverage, Hmain()'s vacuous-green guard).packages/create-objectstack/src/template-version-stamps.test.tsasserts import-safety, the export surface, the throwing version read, the entry-point guard, and fourstampedPaths()properties — none of those eight. The nearest miss is deliberate and documented: the vitest coversstampedPaths()'s empty-set throw, while Control H coversmain()'s guard, a separate code path. So deleting the step would delete coverage; it stays, and the comment now says why.The residual scheduling difference is stated in the rewrite as robustness, not as the reason: lint.yml has no paths filter at all, where the vitest's scheduling now rides a chain (filter output → the union's declaration → the task hash).
H3 — sweep totals
Swept for the claim, not the filter:
picomatch,core=false,Test Coreoutside ci.yml's own job names,skipped ENTIRELY/skipped in full,reachable only from/reached only from,matches NONE,union-into,queue build catches,one merge late,batch collateral,first signal,outputs.core,outputs.scripts.Total 2 · stale 2 · fixed here 1.
.github/workflows/lint.yml— this PR.scripts/sync-template-versions.mjs'sGAP 1 — SCHEDULINGheader — still stale onmainright now; PR docs(scripts): record that GAP 1's scheduling half closed, and that its coverage half closed with it #10047 is open and unmerged. Not mine to touch (different declared file surface).Checked and not stale: ci.yml's own
scripts:filter comment (past tense, and itspicomatchmention cites no version);check-cross-package-test-inputs.mjs's header and ledger; lint.yml ~1085-1095 andcheck-examples-live-imports.mjs'sinvisible-tier blurb (a different claim —examples/**is insidecore, soTest Corewas never skipped for those diffs); lint.yml ~1655 (thedocsfilter, verified still true);packages/spec/scripts/protocol-map.test.ts.No carrier lives on a governed surface —
.claude/skills/pm-dispatch/references/lanes/cli.mdmentionsTest Coreonly in a required-contexts list, which is not this claim. Nothing underdocs/adr/,.claude/,skills/,AGENTS.mdorCLAUDE.mdwas read as a carrier or edited.The wrong picomatch figure has exactly the same distribution: three files mention
picomatch, and only the two carriers cite a version. ci.yml's does not.The picomatch version, and how
2.3.1, read from the action's own committed lockfile rather than from the card or #10047:
A runtime, non-
devdependency. 4.0.5 is this repo's tree copy. I then evaluated the filters under both, parsing the glob table out of.github/workflows/ci.ymlrather than transcribing it, and replicatingcreateRuleItemfrom the action'ssrc/filter.tsatv4(which I also fetched:const MatchOptions = { dot: true },picomatch(pattern, MatchOptions, true),isMatch: str => matchers.some(...)):Identical, so the wrong figure never produced a wrong verdict — but the parenthetical asserted the action's behaviour while quoting a version the action does not carry.
Chain measurement (so "the vitest runs too" is measured here, not inherited)
turbo 2.10.10, in my own worktree:
H4 — is the claim's shape the recurring defect?
Partly, and the useful split is narrower than "rationale prose rots". What rotted in both carriers is a restated evaluated verdict — an output of ci.yml's
filterjob, plus a version number for a matcher living in someone else's lockfile — and a durable form for that half plainly exists: name the authority (ci.yml'sfilterjob and thetestjob'sif:) and stop reprinting what it evaluates to. The other half of the same comment, the argument that these eight red paths are executed nowhere else, is not derivable from any machine-readable file and genuinely needs the prose; it has not rotted in either carrier.My opinion on a card: marginal, and I would not file one yet. A gate that re-evaluates prose claims against ci.yml's filters would be parsing English, which is a worse failure mode than the one it catches; and the cheap durable rule ("cite the file and job, never the evaluated output") is a convention, not something a gate can hold. Both carriers now also record their own retirement, which is the guard that actually pays. I would file only if a third carrier of a different CI-scheduling fact turns up — that would make it a pattern rather than one fact copied twice.
Not filed as a finding (already open)
While measuring the shard set I independently reproduced
--union-intoleavingpackages.countat0whilepackages.itemsholds 2 — already filed as #10046 by the sibling card, so no duplicate from me.Generated by Claude Code
Generated by Claude Code