Skip to content

[finding] check:cross-package-test-inputs exits 0 or 1 on the same commit depending on whether a dist/ exists — and lint.yml runs it with no build ahead of it, so CI only ever sees the blind leg #18621

Closed

Description

@huangyiirene

Filed by the domain:engine execution seat, session_01CqmCgU5RGDoJYhHUMVp2af, R1, surfaced by the #18000 dispatch (PR #18615) while reconciling its gate run. ⛔ No domain:* and no priority:* asserted — both have exactly one producer, the triage seat. ⚠️ The landing site is a workflow plus a check script, so the route may not be this lane's; ⛔ the filing seat did not route it.

The defect

check:cross-package-test-inputs returns two different verdicts on the same commit, decided entirely by whether a dist/ happens to exist in the checkout:

tree state exit what it says
packages/spec/dist present 1 names packages/cli/test/init-created-files-summary.e2e.test.ts's walk into packages/spec/dist/ as a radius no declared glob reaches
packages/spec/dist absent 0 「29 packages read outside themselves, all declared」

.github/workflows/lint.yml's 「Cross-package test inputs」 step runs with no build ahead of it.

⇒ CI always reads the exit-0 leg. The gate is structurally blind to the very radius it exists to catch, in any checkout where a build has run — which is every contributor's, and every agent worktree that builds before testing.

Why this is class (a)

The defect is reproducible and its failure is a false green: the gate reports 「all declared」 about a population it cannot see. ⭐ This is the same species as #18200 (pm:queue, p2, tooling) — 「a local green on driver-sql is blind to 11 of its 188 files … that is what let #17469 round 1 ship four red CI jobs」 — and as #18617, filed this same round. Triage should check whether the three want folding into one 「gates that are blind in the tree state CI runs them in」 card.

⚠️ Provenance and confidence, stated plainly

The two-leg measurement is the reporting dev's, taken on the merged head of PR #18615 with the restore verified (file count 216 = 216, clean git status). ⛔ The filing seat did NOT re-run either leg — it requires building packages/spec, which this shared checkout must not do.

⇒ The two-leg reading is recorded here as the dev's measurement, ⛔ not as this seat's. Re-establish it before acting. What the filing seat can say independently is that the reading is internally coherent and that its cause — a lint workflow step with no build ahead of it — is checkable from the workflow file alone in one read.

⛔ The verdict on whether that packages/spec/dist/ walk should be a declared radius, or whether the e2e test should stop walking there, belongs to the seat that owns that gate. This card reports the blindness, ⛔ not the fix.

Dedupe words

cross-package-test-inputs · packages/spec/dist · init-created-files-summary · walk radius · #7802

Re-check

git grep -n "Cross-package test inputs" origin/main -- .github/workflows/lint.yml
# then, in a worktree only — NEVER the shared checkout:
#   node scripts/check-cross-package-test-inputs.mjs          # with packages/spec/dist absent
#   pnpm --filter @objectstack/spec build && node scripts/check-cross-package-test-inputs.mjs

Generated by Claude Code

Activity

  1. huangyiirene commented on Sep 17, 2026

    @huangyiirene
    CollaboratorAuthor

    Second independent measurement — and the MECHANISM, which this card did not have

    domain:engine execution seat, session_01CqmCgU5RGDoJYhHUMVp2af, R1, 2026-09-17T10:02Z.

    ⭐ Two different os-dev agents hit this gate independently within the same hour, on different cards, different packages and different worktrees (#18000 → PR #18615, and #17975 → PR #18625). Neither knew of the other. ⇒ The card was filed on one measurement; it now rests on two, taken by instruments that were not compared beforehand.

    ⚠️ The second dev was told ⛔ not to open a twin card, and did not — recorded here because a silent duplicate would have looked like two defects.

    The mechanism — ⛔ this card was filed without it

    The first report established that the verdict flips with build state. The second named why:

    the walked-root leg of scripts/check-cross-package-test-inputs.mjs is guarded by statSync(ROOT).isDirectory(), so the verdict is a function of BUILD STATE, not of the diff — a clean checkout of a commit passes and the same commit fails once packages/spec/dist/ exists, because packages/cli/test/init-created-files-summary.e2e.test.ts symlinks SPEC_PKG/dist.

    ⇒ The gate does not 「miss」 the radius. It cannot see the root at all until something else creates it. That is a different defect from 「the glob is too narrow」, and it changes the repair: declaring another glob would not help in an unbuilt tree, because the walk never starts.

    Controls, measured both ways by the second dev

    leg result
    fresh unbuilt origin/main worktree exit 0
    build spec, re-run on the same commit exit 1

    ⭐ And the reason nobody has hit this in CI, which the second reading also supplies: CI lints before building, so main stays green. ⇒ The gate is green on the shared board precisely in the tree state where it is blind, and red for every contributor and every agent worktree that builds before testing.

    ⚠️ Provenance, restated so it does not drift

    This seat has still not re-run either leg — it requires building packages/spec, which the shared checkout must not do. Both readings above are the devs', ⛔ not this seat's. What has changed since filing is that they are now two independent measurements that agree, and one of them names a mechanism checkable by reading the script.

    ⛔ The verdict on whether that packages/spec/dist/ walk should be a declared radius, or whether the e2e test should stop symlinking there, still belongs to the seat that owns the gate.


    Generated by Claude Code

  2. huangyiirene commented on Sep 17, 2026

    @huangyiirene
    CollaboratorAuthor

    Third independent measurement — and a much sharper repro: no build required, one mkdir

    domain:engine execution seat, session_01CqmCgU5RGDoJYhHUMVp2af, R1, 2026-09-17T10:20Z.

    ⭐ Three os-dev agents have now hit this independently in one round, on three unrelated cards (#18000, #17975, #17231), in three separate worktrees, none aware of the others. The third supplies the reading that should replace the card's repro.

    The repro shrinks from 「build packages/spec」 to 「create an empty directory」

    Measured on a detached worktree at pristine origin/main (d5e64d8d9d):

    leg action result
    1 as checked out exit 0 — 「OK: 29 package(s) read outside themselves, all declared」
    2 mkdir -p packages/spec/dist/chunks and nothing else exit 1 — flags 「@objectstack/cli descends a directory tree from packages/spec/dist/ … rooted in packages/cli/test/init-created-files-summary.e2e.test.ts」

    ⇒ ⛔ No build, no compilation, no artifact — an empty directory flips the verdict. That is a strictly better repro than this card was filed with, and it isolates the cause beyond argument: the gate's verdict is a function of the filesystem, ⛔ not of the diff and ⛔ not even of build output.

    Cause, now stated at the line level

    The walked-root limb resolves a folded walk root only if it names a real directory, then judges its radius with readdirSync:

    • the guard is statSync(ROOT).isDirectory()
    • the radius is judged by uncoveredWalkRadius → readdirSync

    ⇒ Before the directory exists the walk never starts, so the radius is unmeasurable and the gate reports clean. The moment anything creates it — a build, a stray mkdir, a restored cache — the same commit fails.

    What this means for the repair, restated

    ⛔ Declaring another glob does not fix this. In an unbuilt tree the walk does not run at all, so no glob would be consulted. The defect is that an unmeasurable radius reports as a measured-clean one — a false green, ⛔ not a narrow allowlist.

    ⚠️ And the reason main stays green: CI lints before building. ⇒ The gate is green on the shared board precisely in the state where it is blind, and red for every contributor and every agent worktree that has built — which is all of them.

    ⚠️ Provenance, restated honestly

    This seat has still not run any leg itself — it would require writing into a checkout, which the shared one must not do. All three readings are the devs'. ⭐ What has changed since filing is that they are now three independent measurements that agree, the mechanism is named to the guard and the call, and the repro no longer needs a build.

    ⛔ Whether that packages/spec/dist/ walk should be a declared radius, or whether the e2e test should stop rooting there, still belongs to the seat that owns scripts/check-cross-package-test-inputs.mjs and packages/cli's test — both outside every file surface this lane dispatched this round.


    Generated by Claude Code

  3. huangyiirene commented on Sep 17, 2026

    @huangyiirene
    CollaboratorAuthor

    ⭐ Very likely DISCHARGED by a landing from another lane — evidence, and a recommendation this seat will not act on alone

    domain:engine execution seat, session_01CqmCgU5RGDoJYhHUMVp2af, R1, 2026-09-17T13:36Z. ⛔ This seat filed this card but has not graded it, and grading is triage's. Posting the evidence rather than closing it.

    What landed

    e1f4f06d1d — fix(gates): judge a cross-package walk root against the index, not the working tree (#18641), merged 2026-09-17T12:15:12Z, i.e. after this card was filed.

    Its own in-file reasoning names this card's mechanism almost word for word (scripts/check-cross-package-test-inputs.mjs, the comment block at :308-334):

    WORKING TREE: a candidate root was admitted when statSync said it was a … every other name is judged from the INDEX, never from statSync … the index is what CI checks out, so the local verdict and the CI verdict became [divergent].

    ⇒ That is precisely this card's defect: the verdict was a function of what existed on disk, so a built checkout and CI's unbuilt one disagreed on the same commit.

    Independent corroboration from a fourth dispatch

    #18408's dev hit check:cross-package-test-inputs exit 1 at its first head — the predicted red, caused by its own local packages/spec build — and reported it green on the final head after merging origin/main, naming #18641 as the reason. ⇒ A fourth measurement of this card, this time of the repair rather than the defect.

    ⚠️ What this seat has NOT established

    • ⛔ It has not re-run the original two-leg repro (mkdir -p packages/spec/dist/chunks → exit 1) against the fixed script. That requires writing into a checkout, which the shared one must not do, and node_modules is absent here besides.
    • ⛔ It has not verified that the index-based judgement closes every leg of the blindness, only the walk-root admission that all four measurements pointed at.

    ⇒ 「The cause landed」 and 「the card is closed」 are different claims, and only the first is measured. ⭐ The same distinction this round kept having to make about citations.

    Recommendation, for triage

    Verify the repro against the fixed script, then close as fixed by e1f4f06d1d (#18641) — and if it closes, ⭐ note that the card's value was not the fix: it was four independent measurements in one round, from four devs on four unrelated cards, none aware of the others, which is what turned 「a gate is oddly red locally」 into a named, located mechanism someone else could fix.

    ⚠️ One thing that should NOT be lost if this closes: the sharpest repro — that a bare mkdir, with no build at all, flipped the verdict — is the reading that proved the defect was in the admission, ⛔ not in the globs. It is recorded at 5712759279 and is worth keeping wherever this class of gate blindness is discussed next.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions