Uh oh!
There was an error while loading. Please reload this page.
ci(lint): record why the ledgered-closure build is NOT a duplicate of the gate's own refresh - #10568
Merged
Merged
Conversation
… the gate's own refresh `.github/workflows/lint.yml`'s "Build the ledgered packages' dependencies" step and `refreshBuiltClosure()` in scripts/check-type-check-coverage.mjs run the identical turbo command with identical filters, and #8331 was filed asking whether the workflow step is now dead weight. Re-derived on current main: it is not, and the reason is an ordering the gate documents but the workflow never mentioned. `--re-measure` checks `unbuiltClosure()` BEFORE it calls `refreshBuiltClosure()` and refuses rather than building from nothing (deliberate: a cold 7-task closure measured 3m2s on a gate people run before pushing). So the refresh owns the present-but-stale case only; it cannot rescue a closure member no earlier step built. Measured what the preceding step actually covers: of the 55 workspace packages in the ledgered dependency closure, `Build workspace packages` reaches 54 through `./packages/*`, and the 55th -- @objectstack/service-knowledge, pulled in by the ledgered knowledge-ragflow -- arrives only through `./examples/*^...`, because examples/app-todo happens to depend on it. Deleting the step would tie the lane's green to that one manifest. Comment only; no step, command, filter or ordering changed. Fixes: #8331 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
os-zhuang
marked this pull request as ready for review
August 21, 2026 04:00
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes: #8331
What the card asked, and what current
mainsays#8331 observed that
lint.yml's "Build the ledgered packages' dependencies" step runs the same turbo command, with the same filters, thatrefreshBuiltClosure()runs insidepnpm check:type-check-debt— and asked whoever picked it up to decide between removing the step and commenting it.Re-derived on
origin/main@ceb33a9f12. The duplication is real and byte-identical, and it is not redundant. This PR is comment-only: no step, command, filter or ordering changed (git diff --stat:1 file changed, 30 insertions(+)).The duplication, as it stands today
.github/workflows/lint.yml:2487(lanetypecheck-debt)pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*'scripts/check-type-check-coverage.mjs:1684(refreshBuiltClosure())['run','build','--filter=./packages/*','--filter=./packages/*/*'](The third instance,
lint.yml:2639in lanetypecheck-consumers, is a different lane with no gate that refreshes anything — out of this card's scope and already documented in place.)Why removing it would be wrong — the ordering the workflow never mentioned
--re-measurecallsunbuiltClosure()before it callsrefreshBuiltClosure(), and refuses rather than building from nothing. That order is deliberate and argued in the gate's own docblock (a cold 7-task closure measured 3m2s, on a gate people run before pushing). So the refresh owns the present-but-stale case only (#8271) — it cannot rescue a closure member that no earlier step built.What the preceding step actually covers — measured, not assumed
The gate enumerates the ledgered dependency closure itself when nothing is built: 55 workspace packages. Against turbo's own
--dry=jsonpackage sets:./packages/*alone@objectstack/service-knowledge./packages/*+./examples/*^...(the step above)./packages/*+./packages/*/*(this step)The middle row is why removal is tempting and the top row is why it is wrong.
@objectstack/service-knowledgelives atpackages/services/, is pulled into the closure by the ledgered@objectstack/knowledge-ragflow, and is reached in this lane only through./examples/*^...— becauseexamples/app-todohappens to depend on it. That is the same accidentlint.ymlalready records one step above for #3037.⇒ Delete this step and the lane's green starts depending on one example app's manifest. Drop that dependency and
check:type-check-debtrefuses in CI, with a remedy message naming the command the workflow no longer runs.And the drift argument is now loud, not silent
The card's case for removal was that "a precondition owned in two places drifts". Since #8330 the gate refreshes with its own filters before measuring, so divergence can only end in that refusal (narrower here) or extra cache hits (wider here) — never in CI measuring a different world than a local run, which is what #8271 was. The two owners are safe; what was missing is that nothing said so. This PR says so, at the step.
Verification
Gate union re-derived with
node scripts/pm/dispatch-gates.mjs(no path arguments) on the committed diff — 10 families, all run at7b2ce42081:check:type-check-debtran throughscripts/pm/os-verify-lock.shon a fully built closure:VERDICT command-exit 0 · held the lock 255s (4m15s).Ablations (both legs, on disk)
The claim under test is the ordering: the refusal precedes the refresh, so the refresh cannot rescue an unbuilt closure member.
Input genuinely absent. With the closure fully built and
turbopresent,rm -rf packages/services/service-knowledge/dist(mutation confirmed on disk: 6 files / 1.d.tsbefore, directory gone after) ⇒grep -c 'Tasks:|cached, 'over that run's output: 0 —refreshBuiltClosure()never got to run, though turbo was onPATHand would have rebuilt it in ~1s. Restored by rebuilding that one package;md5sumover the dist tree is byte-identical to the state the green run measured.Positive control, the other direction. With the closure fully built,
node_modules/.bin/turbomoved aside ⇒ the run gets past the refusal and dies inside the refresh instead:Restored immediately.
Together: dist missing + turbo present ⇒ refusal; dist present + turbo missing ⇒ refresh. The refusal is first, and it is not decorative.
Positive control for the set arithmetic.
C \ (./packages/* alone)is non-empty (1 member), so theC \ A = 0result above is a measurement, not a comparison that cannot see anything.What I could not verify
run:bodies were verified by parsing the editedlint.ymlwith theyamlpackage (typecheck-debtsteps 9 and 10 remainBuild the ledgered packages' dependencies→Re-measure the type-check DEBT / TEST_DEBT ledger, bothrun:strings unchanged); everything about how GitHub schedules the lane is CI's to confirm.node scripts/pm/check-half-states.mjswithout--self-test(the live GitHub scan run byhalf-state-patrol.yml) was not run: it scans issues over the org API and is unaffected by a YAML comment. The--self-testhalf — which is whatlint.ymlinvokes — is green above. Declared narrowing.lint.yml's sibling comment). The comment quotes the card's 9.5s, which is the CI-side one.Changeset
None: the diff is one YAML comment in
.github/workflows/lint.yml, so the publish surface at diff time is empty.skip-changesetapplied, which is this workflow's own prescription for a PR that "releases nothing".Generated by Claude Code