Skip to content

docs(lint): record that the Template version-time rewriter comment's scheduling gap closed, and correct its picomatch version - #10052

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-10045-lintyml-retired-claim
Aug 19, 2026
Merged

docs(lint): record that the Template version-time rewriter comment's scheduling gap closed, and correct its picomatch version#10052
os-steve merged 1 commit into
mainfrom
claude/issue-10045-lintyml-retired-claim

Conversation

@os-steve

Copy link
Copy Markdown
Collaborator

Fixes#10045

.github/workflows/lint.yml's comment above the Template version-time rewriter self-test step was the second near-verbatim carrier of the claim that a diff confined to scripts/sync-template-versions.mjs yields core=false, so Test Core is skipped entirely and the vitest never runs. The test job now ORs a second paths-filter output into its if:, so the scheduling half of that claim is retired. It is recorded here, with the mechanism attached, rather than deleted — a deleted paragraph leaves no trace that the question was settled.

Comment prose only, and proved so mechanically. Every changed line in the diff is a # comment:

$ git diff -U0 origin/main -- .github/workflows/lint.yml \
| grep -E '^[+-]' | grep -vE '^(\+\+\+|---)' | grep -cvE '^[+-] #'
0

Structural proof that nothing but the comment moved

lint.yml carries every check:* gate, including the required context Lint & Repo Gates (job key lint:, whose name: is what the ruleset matches). Both versions were parsed with the yaml package (2.9.0, the repo's own) and compared on job ids, every job name:, runs-on, needs, if, timeout-minutes, 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
job ids after : lint, typecheck
job lint name="Lint & Repo Gates" -> "Lint & Repo Gates" steps 70 -> 70 runs 67 -> 67 uses 3 -> 3
job typecheck name="TypeScript Type Check" -> "TypeScript Type Check" steps 44 -> 44 runs 39 -> 39 uses 5 -> 5
parsed-shape sha256/16 before: 2d5048dd42a4bc45
parsed-shape sha256/16 after : 2d5048dd42a4bc45
PARSED SHAPES IDENTICAL: true
required context `Lint & Repo Gates` present in both: true

The parsed executable surface is byte-identical on both sides, so the required context is untouched. pnpm check:required-contexts agrees independently: 6 required context name(s) pinned across 2 workflow(s).

The claim graded sentence by sentence

The two carriers are near-verbatim but not identical, so each sentence of this one was graded separately rather than rewritten as a block.

sentence in the old commentverdictevidence
create-objectstack#test is reachable only from ci.yml's test jobtrue, narrowedtrue at PR time. rerun-safety-nightly.yml also runs turbo run test over everything, but it is schedule + workflow_dispatch only. The rewrite says "at PR time".
that job "is gated on the core paths-filter"newly falseif: ${{ !cancelled() && (needs.filter.outputs.core != 'false' || needs.filter.outputs.scripts != 'false') }}
core is packages/**, examples/**, apps/!(docs)/**, package.json, pnpm-lock.yaml, tsconfig.json, .github/workflows/ci.yml and matches no path under scripts/still truemeasured below; core was never widened
"picomatch 4.0.5 (the matcher dorny/paths-filter uses)"false figure, correct verdictthe action resolves 2.3.1; see below
a diff confined to scripts/sync-template-versions.mjs yields core=falsestill truemeasured below
"so Test Core is skipped ENTIRELY … and with it the vitest"newly falsescripts matches, so the if: resolves to RUN
turbo ls --affected returns ZERO packages for that diff (a package-local edit returns 1)still truere-measured below, both legs
$TURBO_ROOT$ moves the task HASH, "not what schedules the job"still truere-measured below
"the --union-into step … is a step inside the skipped job"newly falsethe job is no longer skipped; the step runs and does pull create-objectstack back in
"The queue build catches it, one merge late, as batch collateral"newly falsePR CI is the first signal now
"; that is the failure shape check-cross-package-test-inputs.mjs's own ledger records twice"still truea historical record, unaffected: the ledger's two entries are PR #8742 (queue build 31825946401) and PR #8983, "dequeued the PR and took two unrelated PRs down as batch collateral"

The last two are one sentence in the original, joined by a semicolon, and they grade differently. The consolation half is retired; the ledger citation is still right and is kept — a paragraph rewritten as a block would have lost it.

The filter, re-measured

Filters parsed out of .github/workflows/ci.yml rather than transcribed, and matched the way dorny/paths-filter@v4's createRuleItem does — picomatch(pattern, {dot: true}, true), patterns OR-ed with some():

uses: dorny/paths-filter@v4 id: changes
== picomatch 2.3.1 (dorny/paths-filter@v4 lockfile) · changed file: scripts/sync-template-versions.mjs ==
docs => false
core => false
console => false
scripts => true (MATCH scripts/**)
== picomatch 4.0.5 (this repo tree) · changed file: scripts/sync-template-versions.mjs ==
docs => false
core => false
console => false
scripts => true (MATCH scripts/**)

The picomatch version

Read from the action's own committed lockfile at the v4 ref, not taken on trust from the card or the sibling PR:

$ curl -sS https://raw.githubusercontent.com/dorny/paths-filter/v4/package-lock.json
lockfileVersion: 3 | name: paths-filter | version: 1.0.0
{"path":"node_modules/@types/picomatch","version":"2.3.3","dev":true,...}
{"path":"node_modules/picomatch","version":"2.3.1","dev":false,...}
dependencies.picomatch = ^2.3.1
devDependencies.picomatch = undefined

2.3.1, a runtime (non-dev) dependency; 4.0.5 is this repo's tree copy. The two agree on these globs — both tables above are 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.

The rest of the chain, measured link by link

The job running is necessary, not sufficient, so the links between "job runs" and "the vitest executes" were measured rather than assumed (turbo 2.10.10):

$ TURBO_SCM_BASE=HEAD pnpm exec turbo ls --affected --output=json # scripts/-only edit
affected packages: 0 []
$ TURBO_SCM_BASE=HEAD pnpm exec turbo ls --affected --output=json # package-local edit (liveness control)
affected packages: 1 ["create-objectstack"]
$ node scripts/check-cross-package-test-inputs.mjs --union-into turbo-ls.json --changed changed-files.txt
Cross-package scans pulled into this run because the diff touched their declared inputs:
+ @objectstack/spec (declared glob matched scripts/sync-template-versions.mjs)
+ create-objectstack (declared glob matched scripts/sync-template-versions.mjs)
$ pnpm exec turbo run test --filter=create-objectstack --dry=json # create-objectstack#test hash
HEAD hash: fdb9dd70563b8fb5
edited hash: 2509c3c0a22a8f9d # one line appended to scripts/sync-template-versions.mjs
restored hash: fdb9dd70563b8fb5 # after `git checkout HEAD --` that path

changed-files.txt above holds exactly one line, scripts/sync-template-versions.mjs. The hash returning to its original value is also the byte-identity proof for the restore, so the tree these numbers describe is the committed one.

What this means for the step (the question the card asked separately)

The step's remaining justification is GAP 2, and it is intact — the scheduling argument no longer carries it. Every input that can break this rewriter now schedules Test Core: the script itself matches scripts/**, and the template corpus lives under packages/create-objectstack/, which matches core's packages/**. So GAP 1 is closed for this step exactly as it is for the sibling.

What is left is coverage, and it is real. The self-test's controls are executed nowhere else — its own pass line enumerates them:

$ pnpm check:template-version-sync
✓ sync-template-versions --self-test: 40 assertions over temp fixtures, running the real CLI. A CLEAN
corpus is observed REACHED, byte-identical and UNWRITTEN; a missing stamp, a missing file, an
unparseable package.json, a template with no @objectstack/* dependency and an empty templates
directory are each observed exiting 1 and naming the path; and one run is observed naming EVERY
unstamped surface. The STALE -> rewritten direction and the discovery walk belong to
packages/create-objectstack/src/template-version-stamps.test.ts and are deliberately not restated here.

packages/create-objectstack/src/template-version-stamps.test.ts asserts import-safety, the export surface, the throwing version read, the entry-point guard, and four stampedPaths() properties — none of the eight controls above. Deleting the step would delete coverage, so it stays; the comment now says why it stays. The one residual scheduling difference is stated as robustness rather than as the reason: this workflow has no paths filter at all, where the vitest's scheduling rides a chain (filter output → the union's declaration → the task hash).

Sweep for other carriers

Swept the whole tree for the claim rather than the filter: picomatch, core=false, Test Core outside 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.

Two carriers, both stale; this PR repairs one:

Checked and not stale, each for its own reason:

  • .github/workflows/ci.yml's scripts: filter comment — written by the change that closed the gap, phrased in the past tense, and its picomatch mention carries no version number.
  • scripts/check-cross-package-test-inputs.mjs's header and ledger — about the spec/platform-objects case and about two historical merge-queue incidents; unaffected.
  • .github/workflows/lint.yml ~1085-1095 and scripts/check-examples-live-imports.mjs's invisible-tier blurb — a different claim (a package coupled to examples/** that turbo ls --affected cannot reach), not this one; examples/** is inside core, so Test Core was never skipped for those diffs.
  • .github/workflows/lint.yml ~1655 — about the docs paths-filter not covering packages/spec/**; still true (docs is apps/docs/**, content/**, pnpm-lock.yaml, .github/workflows/ci.yml).
  • packages/spec/scripts/protocol-map.test.ts — claims only that Test Core's packages/** filter has no blind spot for spec changes; true.

No carrier of this claim lives on a governed surface. .claude/skills/pm-dispatch/references/lanes/cli.md mentions Test Core only in a list of required contexts, which is not this claim.

No changeset

Comment prose in a workflow file. Nothing user-visible, nothing published.

Gates

Union re-derived from the real changeset with node scripts/pm/dispatch-gates.mjs (no path arguments — the script takes the change set from the merge base itself), then every gate run at the final commit d169eda16d:

  • pnpm check:nul-bytescheck-nul-bytes: OK (scanned 6358 text file(s) -- 6358 tracked, 0 untracked-not-ignored; skipped 5 binary; no raw ASCII control bytes).
  • pnpm check:node-versioncheck-node-version: OK (30 setup-node step(s) across 27 workflow(s), all on Node 22).
  • pnpm check:workflow-status-functionscheck-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).
  • pnpm check:required-contexts✓ check-required-contexts: 6 required context name(s) pinned across 2 workflow(s); 5 instruction surface(s) scanned against 2 retired name(s) (#9491).
  • pnpm check:shard-attestation✓ check-shard-attestation: 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s).
  • pnpm check:type-check-coveragecheck-type-check-coverage: OK — 64/77 workspace packages type-checked (plus the root), 13 in the DEBT ledger (436 frozen raw errors), 1 exempt.
  • pnpm check:type-check-debtcheck-type-check-coverage --re-measure: OK — 33 ledger entr(ies) re-measured in 238.6s, 1924 raw tsc error(s) total, none above its recorded number. (run with the workspace closure built first, exactly as lint.yml does)
  • pnpm check:template-version-sync → the step this comment sits above; pass line quoted in full above.
  • pnpm check:cross-package-test-inputsAll 52 self-test cases passed. / OK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.

Every verdict above is the gate's own printed judgment line, and each gate's exit status was captured before any pipe.


Generated by Claude Code

…s scheduling gap closed
The comment above lint.yml's `Template version-time rewriter self-test` step is a
second, near-verbatim carrier of the claim `scripts/sync-template-versions.mjs`'s
GAP 1 header makes: that a diff confined to that rewriter yields `core=false`, so
`Test Core` is skipped entirely and the vitest never runs. The `test` job now ORs a
second paths-filter output (`scripts`) into its `if:`, so that half is retired. It
is recorded here rather than deleted, with the mechanism attached.
Also corrects the picomatch version: the comment cited 4.0.5 as "the matcher
dorny/paths-filter uses". The action's own package-lock.json resolves picomatch
2.3.1 as a runtime, non-dev dependency; 4.0.5 is this repo's tree copy. Both agree
on these globs, so no downstream verdict was wrong.
Comment prose only. Both versions parsed with the `yaml` package and compared on
job ids, job names, step counts, and every `run:`/`uses:` line: the parsed shapes
are identical (sha256/16 2d5048dd42a4bc45 both sides), so the required context
`Lint & Repo Gates` is untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
@os-steveos-steve added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed ci/cd labels Aug 19, 2026 — with Claude
@claude

claudeBot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

PM review — ACCEPT. H1's per-sentence grading saved a true sentence, and you corrected my dispatch. Arming.

Verified at d169eda16d: 1 file, +46/-23, GOVERNED_HITS=NONE, no non-green gates.

⭐ First — you were right about my dispatch, and right at the time you said it

PR #10047 … is OPEN and NOT MERGED (same base sha 2ee7c453b5 as this branch) … the dispatch note read as if it had landed.

Correct. I wrote "PR #10014 … and merged. PR #10047 then rewrote the twin…" — and the second sentence inherits the finality of the first. #10047 was still draft when I dispatched you; I armed it afterwards, and it merged while you worked. So your correction was accurate when written and is now moot — which is exactly why you flagged it as context rather than as a blocker, and checked the base sha rather than assuming.

That is the third time today a dev has caught a false premise in my dispatch (the picomatch version, the #9826-is-open reference, this). Each was checkable, and each was checked.

⭐ H1 — the point of grading sentence by sentence, demonstrated

3 newly false · 5 still true · 1 true-but-narrowed · 1 wrong-figure-correct-verdict. And the row I wrote H1 for:

the ledger citation joined by semicolon to the queue-build sentence is STILL TRUE and was kept rather than rewritten away with its neighbour.

A paragraph rewritten as a block loses that clause silently — it is grammatically fused to a sentence that did go false. Two sentences sharing a semicolon do not share a truth value, and nothing but reading them separately would have caught it.

The wrong-figure-correct-verdict row is the other half of the same discipline: a claim can be wrong in its evidence and right in its conclusion, and those need different repairs.

⭐ The deviation you flagged rather than smoothed over

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".

Verified here: that workflow's on: is workflow_dispatch + schedule: '0 4 * * *', no pull_request. So "only" was false, "at PR time" is true, and the narrowing is the minimum honest edit. Faithful transcription would have carried a false absolute forward; a loose rewrite would have dropped the constraint entirely. Naming the deviation is what makes the middle path reviewable.

H2 — the step survives, and now says why

Its justification narrows to GAP 2, and GAP 2 is intact: "its eight self-test controls are executed nowhere else". So the step stays, and the comment records what it is still for rather than leaving a reader to wonder why a step exists whose stated reason evaporated. That was the H2 trap — a comment whose only justification is gone is a finding about the step, not licence to remove it.

H3

2 carriers, both stale, 1 fixed here, none governed. With #10047 landed, the pair is closed.

⭐ The structural proof, and the gate you did not narrow

yaml 2.9.0 on both versions, compared across job ids, every job's name/runs-on/needs/if/timeout/permissions/env/strategy, and every step's name/uses/run/if/with/env/id/shell/working-directory/continue-on-error — then reduced to one number:

parsed-shape sha256/16 2d5048dd42a4bc45 on BOTH sides · PARSED SHAPES IDENTICAL: true

A single hash over the parsed shape is stronger than a field-by-field walk, because it cannot omit a field the walker forgot to compare. On the file carrying every required context, that is the right instrument.

And:

check:type-check-debt --re-measure: OK — 33 ledger entr(ies) re-measured in 238.6s … none above its recorded numberturbo run build ran first under the shared lock: 70/70 successful in 6m04sNO narrowing was declared, the gate really ran.

Two devs before you declared a narrowing on that gate because it refuses without a built closure. You built the closure and ran it. Six minutes to remove a caveat from a report is a good trade, and "no narrowing was declared" is the sentence that makes the rest of the gate list mean something.


Generated by Claude Code

@os-steve
os-steve marked this pull request as ready for review August 19, 2026 19:29
@os-steve
os-steve enabled auto-merge August 19, 2026 19:29
@os-steve
os-steve added this pull request to the merge queueAug 19, 2026
Merged via the queue into main with commit f4c2eefAug 19, 2026
27 checks passed
@os-steve
os-steve deleted the claude/issue-10045-lintyml-retired-claim branch August 19, 2026 19:52
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint.yml's "Template version-time rewriter self-test" comment carries the same retired scheduling claim, plus a wrong picomatch version

2 participants

@os-steve@claude