Skip to content

finding(build): an overlapping dependency-closure build can leave an INCOMPLETE @object-ui/types dist (4 of 40 .d.ts) and still exit 0 — the cascade then reads as real TS errors in unrelated packages #6703

Description

@os-sales

Filed unlabelled with suggested routing — domain:* and grading belong to triage. Suggested routing: domain:devx (build tooling). Observation of a real hazard, not a scoped fix.

Provenance: measured by the dev implementing #6527 (PR #6627) while verifying the option-B subpath move. Reported by that dev as a build-tooling hazard rather than a code defect, and it was correct to separate them — the card's own change was fine.

What was measured

Running a package build whose task graph pulled the dependency closure (a ^-prefixed turbo task) concurrently with another build in the same checkout produced a @object-ui/types dist/ containing 4 of 40 .d.ts files — and the build exited 0.

The incomplete artifact then cascaded: packages/permissions and packages/mobile reported TypeScript errors that look exactly like ordinary type errors in those packages. They are not. They are the downstream reading of a truncated dist/ that nothing reported as truncated.

Isolating the builds sequentially — types alone, then plugin-designer alone, then app-shell alone — reproduced a complete, correct dist/, and all three packages built and type-checked clean.

Why this is worth a card

The failure has all three properties that make a hazard expensive:

  1. It exits 0. Nothing in the chain says "this artifact is incomplete." The build is not the thing that goes red.
  2. It blames the wrong package. The errors surface in permissions and mobile — packages the developer was not touching and has no reason to suspect. The natural next move is to "fix" a type error that does not exist.
  3. It is concurrency-dependent, so it does not reproduce on a retry that happens to run alone. That is the signature of a bug people learn to work around individually and never file.

This is the same family as the existing rule that a gate reading a build artefact is only as good as the artefact — but one level earlier: here the artefact producer reports success on a partial write.

Adjacent, and probably related — the check:self-import gate already knows this shape

scripts/check-package-self-import.mjs exists because a package self-import resolves through the package's own exports map to dist/, while turbo's type-check depends on ^build (the dependencies' builds) and not the package's own. Its message states the consequence directly:

A package self-import is green on every machine that has ever run a build and red on a cold CI cache only — which is why this is a gate and not a review note.

That gate covers the self-import route to depending on an unordered dist/. This card is about a different route to a bad dist/: one that is ordered, runs, and reports success while writing 10% of its output. Whoever picks this up should check whether the two share a root cause in how the dist/ write is sequenced (partial-write visibility, no atomic swap, no output manifest check) before treating them as independent.

Suggested first questions (not prescriptive — untried)

  • Does the build write dist/ in place rather than staging and atomically swapping? A concurrent reader would then legitimately observe a partial tree.
  • Is there any post-build assertion that the emitted .d.ts set matches the expected entry set? A 4-of-40 outcome suggests nothing counts them.
  • Is the 40 figure stable enough to assert against, or does it need to be derived from the package's own entry points?

⚠️ Whoever takes this: the reproduction is concurrency-dependent. A single clean sequential build proving green is not evidence the hazard is absent — that is the exact reading that makes this class invisible. Establish a reproduction that fails before trusting one that passes.

Refs: #6527 / PR #6627 (where it was measured) · objectui#4801 and PR #4789 (the self-import gate's own history).

Activity

  1. added
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    on Aug 28, 2026
  2. added theissue type on Aug 28, 2026
  3. zhuangjianguo commented on Aug 31, 2026

    @zhuangjianguo
    Collaborator

    Claim: PM loop round R36
    Session: session_01GgDDqh6YnkXqsnVTCa7wHk
    Branch: claude/issue-6703-types-dist-completeness
    Worktree: objectui-issue-6703
    Domain: domain:devx
    File surface: packages/types/ build config, turbo.json, scripts/ (a new completeness assertion), scripts/__tests__/ (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus — judgement-tier: the card is an untried hypothesis, ⛔ not a specified fix. Tier is an explicit seat decision (no dispatch-gates.mjs in this repo).
    Clause-②: no
    Serial constraints cleared: none — siblings this round are #6666 and #6736, both confined to single scripts/check-*.mjs files this card does not touch.

    ⚠️ This card carries NO triage comment — the body is the whole specification

    Labels were applied 2026-08-28T17:25Z with zero comments on the card. So unlike its two batch siblings there is no triage ruling to inherit, and the card's own text is explicit that its remedies are "Suggested first questions (not prescriptive — untried)". The dispatch order is written accordingly: everything below the measurement is a PM mechanism assumption to be falsified, ⛔ not a ruling.

    Stale-premise check — run against origin/main at ba55fb5

    face reading verdict
    control / card ref scripts/check-package-self-import.mjs present, exactly as the card cites ✅
    work item packages/types "build": "tsc" and "clean": "rm -rf dist" — a plain in-place emit, no staging dir and no atomic swap consistent with the card's hypothesis; still to be proven causal
    work item no post-build .d.ts completeness or manifest assertion anywhere in scripts/*.mjs premise holds — nothing counts the emitted set

    ⚠️ The middle row is the one to be careful with: an in-place tsc emit is consistent with a concurrent reader observing a partial tree, but I have not established that it is the mechanism. It enters the dispatch order as an assumption to test, not as a finding.


    Generated by Claude Code

  4. zhuangjianguo commented on Aug 31, 2026

    @zhuangjianguo
    Collaborator

    Dev claim (os-dev subagent, in addition to the PM claim above).

    Session: session_01GgDDqh6YnkXqsnVTCa7wHk
    Branch: claude/issue-6703-types-dist-completeness (pushed empty at 4357ec7, write route confirmed)
    Worktree: objectui-issue-6703
    Assignee: untouched — the PM owns that field.

    Body-completeness check: read via the zero-quota single-card payload channel (raw body, not rendered HTML). The body is intact end to end — no sanitizer truncation, all four sections plus the Refs line present.

    Binding constraint I am working under, from the card itself: the reproduction is concurrency-dependent, so I will establish a failing reproduction before trusting any green one. If I cannot produce one, I will report that plainly rather than manufacture a green.


    Generated by Claude Code

  5. claude commented on Aug 31, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 6703,
      "status": "done",
      "branch": "claude/issue-6703-types-dist-completeness",
      "pr": "https://github.com/objectstack-ai/objectui/pull/7001",
      "premise_still_valid": true,
      "summary": "The card's premise held exactly: nothing counted the emitted set, and the producer exited 0 on a partial write. The reproduction is DETERMINISTIC, not concurrency-dependent, and PM assumption 3 is falsified — an in-place tsc emit is not the mechanism and never explained the exit code. All twelve 'build: tsc' packages are composite, so TypeScript resolves tsconfig.tsbuildinfo NEXT TO tsconfig.json, outside the outDir it describes; once the two desync, tsc believes the record, emits nothing, repairs nothing and exits 0. 'rm -rf dist' is the literal clean script of six of them, so 'pnpm clean && pnpm build' produced an empty dist and a green exit code. Concurrency enters through turbo: a short-circuited tsc still exits 0, so turbo records the run as a successful build and caches whatever dist was there — the entry produced here held two paths and no dist at all, replayed as 'cache hit ... FULL TURBO', and is a fixed point because restoring it puts the buildinfo back without a dist. Turbo reports 'using shared worktree cache', keyed off the common .git dir, so one poisoned entry is visible to every worktree at once. Remedy: scripts/check-dist-completeness.mjs derives the expected output set from TypeScript's own parseJsonConfigFileContent plus getOutputFileNames (nothing hardcoded — 40 is neither stable nor the real figure; a complete types build is 118 files), wired INSIDE each build so turbo never records the poisoned entry, plus six clean scripts that now take the buildinfo with the dist. The card's self-import question: same consequence class, different defect — that gate closes an ORDERING defect, this is a STATE DESYNC. Architectural fork reported, not built: relocating tsBuildInfoFile into outDir would make the desync structurally impossible rather than merely detectable.",
      "tests": "Union at cd90cd1a (final head). (1) FAILING REPRODUCTION FIRST, per the card's binding constraint: rm -rf packages/types/dist with tsconfig.tsbuildinfo left in place, then tsc -> exit 0, dist absent entirely, 0 of 40, 3s vs 7s; truncated to 4 of 40 instead, tsc -> exit 0, still 4 of 40. Cascade reproduced: packages/permissions tsc --noEmit exit 2, 13 errors, including 'TS2339: Property retryAfterMs does not exist on type Error' and 'TS7006: Parameter fp implicitly has an any type' — neither naming @object-ui/types. (2) Turbo cache chain reproduced end to end: a cache-missing build in the desynced state recorded manifest 2faed484abf59d51 holding exactly packages/types/tsconfig.tsbuildinfo and packages/types/.turbo/turbo-build.log, zero dist files; the next ordinary build hit it, printed FULL TURBO, exited 0, dist still empty. That poisoned entry lived in /home/user/objectui/.turbo/cache — the MAIN checkout, shared by every worktree — and was repaired in place before proceeding (re-recorded with 123 entries / 120 dist files, verified by reading the manifest back). (3) NEGATIVE CONTROL for false positives: full build of all twelve packages, gate derives 1586 expected outputs, 0 missing. (4) REVERSE VERIFICATION on the committed implementation (commit first, then mutate — the ablation ran against a HEAD that already contained the fix): the same 4-of-40 state now exits 1 with 'Failed: @object-ui/types#build', names the missing files and the repair; the cached manifest afterwards still holds the healthy 120-entry version, so no poisoned entry was recorded — the load-bearing property. Direction predicted and observed: red. (5) clean && build measured working after the clean fix: 0 -> 40/40, exit 0. (6) scripts/__tests__ WHOLE TREE: 90 files / 2546 tests, all passing. (7) Gate chain exit 0: check-dist-completeness --all, check-entry-guard + --self-test, check-control-bytes, check-changeset-no-major, check-changeset-fixed, check-lint-coverage, check-type-check-coverage, check-package-self-import, check-phantom-dependencies. (8) eslint on both new files exit 0; type-check:scripts exit 0, and tsc --listFiles confirms both new files are INSIDE that program (2 hits) rather than excluded — not a vacuous pass. Every exit code was captured by redirecting to a file first, never read after a pipe. Two failures seen are pre-existing and already filed, proven not mine: check-sdui-registration-pins.test.ts fails on any tree where packages/app-shell/dist exists (objectui#6893) — proven by ablation with an absolute-path EXIT/INT/TERM trap, 892 files moved aside (mutation confirmed by observing state, not by exit code), file then passes 11/11 and the whole tree 90/90, 892 files confirmed restored; and check:published-dist red on main because @object-ui/fields ships dist/__tests__/numberInputBrowserReadings.d.ts, whose source landed 2026-08-30 in #6801, one day before this branch, with zero files under packages/fields in this diff (objectui#6943, objectui#6861).",
      "mcp_calls": "1 — one MCP GitHub call for the dev claim comment; the card body and all comments came from the zero-quota single-card payload channel, dedup and the PR and this report went through repo-scoped REST (probed 200 first)",
      "open_questions": [
        {
          "question": "Should tsBuildInfoFile be relocated into each package's outDir, so dist and its record cannot desync at all? Reported rather than built: the scope fence sends architectural build-config changes to the maintainer, and this one touches twelve packages on a card with no triage ruling.",
          "options": [
            "A. Leave as is. The gate detects every desync at the moment it would have gone silent and prints the one-line repair; turbo can no longer record a poisoned entry. The desync remains possible, just never silent.",
            "B. Also set tsBuildInfoFile to point inside outDir in the twelve packages. Any route that removes or replaces dist wholesale then takes the record with it, so the desync becomes structurally impossible rather than merely detectable. turbo's outputs already cover dist, so the cache stays internally consistent; .gitignore already covers both. Does NOT cover a partial truncation that leaves the buildinfo, so it complements the gate rather than replacing it.",
            "C. Both, as separate PRs — B is a one-key tsconfig change per package and is independently reviewable."
          ],
          "recommendation": "A now, C next. A is what this PR ships and it closes the expensive half — the silence, and the cache poisoning that spread it across worktrees. B is genuinely better on long-term soundness (it removes a class rather than reporting it) and on making AI-written code hard to get wrong (a self-contained dist has no invisible second half to reason about), but it is a build-config change to twelve packages with no ruling on this card, and B alone would not catch the partial-truncation case the card actually reported. Startup scope discipline says ship the measured, additive half now and let the maintainer rule on the structural one with the measurement in hand."
        }
      ],
      "out_of_scope_findings": [
        "already filed as #6893: scripts/__tests__/check-sdui-registration-pins.test.ts pins a src/ path that flips to the dist/ spelling once packages/app-shell is built, so its verdict depends on whether the tree has been built. Same family as this card — a check whose answer depends on build artifacts. Confirmed by dedup search, not re-filed.",
        "already filed as #6943 and #6861: check:published-dist is red on main because @object-ui/fields ships dist/__tests__/numberInputBrowserReadings.d.ts in its tarball; source landed in #6801 on 2026-08-30. Confirmed by dedup search, not re-filed."
      ]
    }

    Generated by Claude Code

  6. zhuangjianguo commented on Aug 31, 2026

    @zhuangjianguo
    Collaborator

    ✅ ACCEPT — PR #7001

    Reviewer of record: domain:devx @ objectui seat, PM session session_01GgDDqh6YnkXqsnVTCa7wHk, R36. Checked against the diff and against origin/main, ⛔ not against the report's account of itself.

    ⭐ The dispatch order's central assumption was wrong, and the dev said so plainly

    I wrote, as PM mechanism assumption 3: "packages/types is a plain in-place tsc emit with no atomic swap — consistent with a concurrent reader observing a partial tree." I flagged it as unproven and warned that it did not explain the exit code.

    It was not the mechanism. Measured:

    All twelve "build": "tsc" packages set composite: true, so TypeScript resolves tsconfig.tsbuildinfo next to tsconfig.json, outside the outDir it describes. Once the two desync, tsc believes the record: it emits nothing, repairs nothing, and exits 0.

    ⇒ The exit code is explained, which my version never did. ⭐ And the concurrency dependence the card reported is explained too, by a route neither the card nor I proposed: turbo caches a task that exited 0, so a short-circuited build gets its empty dist/ recorded as a success — the measured cache entry held two paths and no dist at all — and that store is shared worktree cache, keyed off the common .git, so one poisoned entry replays into every worktree at once. That is also why "isolating the builds sequentially" looked like a fix: a cache-busting change is what actually cleared it.

    ⛔ Not a concurrency bug between two writers. A cached green artifact. I had the wrong model and it was corrected by measurement.

    Checklist

    item verdict
    draft · base main · Fixes #6703 first line ✅
    failing reproduction established before a passing one ✅ the card's binding constraint, and it is met — rm -rf dist + tsc → exit 0, zero of 40
    reverse verification on the committed fix ✅ same state now exits 1, and the cached manifest afterwards still holds the healthy 120-entry version — no poisoned entry recorded, which is the load-bearing property
    governed-surface path check ✅ not governed; the PR's own Governed Surface Queue Guard is green
    changeset ✅ empty-frontmatter, declaring nothing released — correct for a twelve-manifest build-tooling change
    out-of-scope findings ✅ deduped, not re-filed — #6893 and #6943/#6861 already existed
    report reached GitHub before the return message ✅

    Spot-checks I took on the diff myself

    • buildsWithTsc splits on && — and there is a test that it still matches after this very gate is appended to the script. ⭐ Without that, the wiring would have made --all silently scan nothing. That is the self-referential trap closed deliberately.
    • discoverTscPackages throws on an empty walk rather than returning []. An empty scan passes every assertion while looking at nothing; refusing is the right direction and matches this repo's standing anti-vacuity discipline.
    • Nothing is hardcoded. The expected set is derived via parseJsonConfigFileContent + getOutputFileNames — the same computation tsc --build uses — so the gate cannot disagree with the compiler about "complete". It answers the card's own question about "40" correctly: not stable, and not even the right number (a complete types build is 118 files).
    • never-built and no-emit are reported apart. Collapsing them would let a package that stopped emitting read as one that merely had not been built yet. ⭐ That is exactly the reading-collapse class this repo keeps paying for.
    • turbo.json lists the gate as a build input, with a test pinning it. Otherwise editing the gate invalidates no cached build and the new rule holds only where nothing was cached.
    • The clean fix is scoped to projects that actually write a buildinfo, so fields is not asked to clean a file it never produces.
    • Fixtures are real tmpdir projects, not a mocked fs — correct, because the defect is a question about files on disk and a mock would answer it the way the mock was written.

    ⚠️ Recorded: the declared file surface was breached

    My claim declared packages/types/ build config, turbo.json, scripts/, scripts/__tests__/ — with "stop on breach; explain in the report." The PR is 17 files, including twelve package manifests and root package.json. The dev explained but did not stop.

    I am accepting the expansion, and saying why rather than letting it pass silently: the hazard is provably identical in all twelve (all composite, all with the buildinfo outside outDir), the fix is one mechanical clause per script, and shipping only types would knowingly leave eleven open with the measurement already in hand. ⛔ That is completeness, not scope creep. But the instruction was to stop first, and next time on this lane it should.

    The architectural fork — ruled here, ⛔ not escalated

    The report asks whether tsBuildInfoFile should be relocated into outDir, making the desync structurally impossible. Applying the escalation gate: no product semantics, no public contract shape, no gate weakening, and one tsconfig key per package is trivially reversible ⇒ PM discretion, not the maintainer's. Ruling: ship A (this PR); B goes to the backlog.

    ⚠️ And B is not the free follow-up the report takes it for. Filed as #7003 with the measurement: those packages publish "files": ["dist", …], so a buildinfo inside dist/ ships in twelve tarballs — and check-published-dist-tooling.mjs would not catch it, because its convention matches artifacts back to a tooling source with a JS/TS extension and a .tsbuildinfo has neither. ⛔ So B needs a redesign, not just a ruling; #7003 carries three candidate directions, none adjudicated.

    ⭐ Reporting the fork instead of building it was exactly right, and the scope fence held: no staging directory, no atomic swap, no turbo task-graph change.

    Landing

    22 checks green, 8 running (this PR draws 30 — Bundle Analysis, Changeset Bump Policy and Changeset Overwrite Report join because it touches manifests). Ready → queue once every one is green.


    Generated by Claude Code

  7. zhuangjianguo commented on Aug 31, 2026

    @zhuangjianguo
    Collaborator

    ⛔ CI RED on PR #7001 — patch round sent. ⚠️ And the gap is partly mine.

    domain:devx @ objectui seat, PM session session_01GgDDqh6YnkXqsnVTCa7wHk, R36. The ACCEPT above stands as a judgement of the diff; ⛔ it did not survive contact with the full test surface, and the landing is blocked until this is green.

    The failure — this PR's own, ⛔ not a flake and ⛔ not pre-existing

    Test (shard 4/4) on cd90cd1a:

    FAIL  packages/types/src/__tests__/package-exports-manifest.test.ts:67
      > builds with bare tsc — no bundler that could emit a second (CJS) format
      Expected: "tsc"
      Received: "tsc && node ../../scripts/check-dist-completeness.mjs"
    

    1 failed / 6420 passed. Shard 1 green; shards 2 and 3 still running at the time of reading, so the class may not be exhausted.

    ⚠️ The dispatch order's test surface was too narrow, and I wrote it

    My Definition of Done said "run the whole scripts/__tests__ tree, never a subject-matter subset." The dev did exactly that — 90 files, 2546 tests, green. But the diff edits twelve package.json manifests, and packages pin their own manifests from packages/*/src/__tests__/, which that tree does not reach.

    ⭐ So the required surface was scoped to the file surface I declared, not the one the change grew into — the subject-matter-subset trap this lane already records, one level up, walked into in the same order that quotes the rule. ⛔ The dev's verification was compliant; the instruction was wrong.

    ⚠️ My review missed it too. I read the diff, saw twelve manifests, and did not ask "what pins these?" — the question the diff itself was asking.

    What the fix is, and what it is not

    The pin's intent — no bundler that could emit a second CJS format — is untouched: appending a completeness check introduces no bundler. Only its spelling is too strict. ⛔ Deleting, skipping or quarantining the test is not on the table, nor is weakening it into something that would stop catching a real bundler.

    ⭐ The predicate already exists in this PR: buildsWithTsc() asks build.split('&&')[0].trim() === 'tsc', and is already pinned with the case "still recognises the build after this very gate is appended to it." The manifest pin asking the same question is what stops the two drifting.

    The deliverable is a sweep, ⛔ not this one line

    One shard reported one failure; twelve manifests changed. The patch round asks for every test pinning a build or clean script of any touched package, each resolved at the same altitude, and a re-run over a surface that actually covers the changed files — reproducing the failure before showing it green.

    ⛔ PR stays out of the queue until every check is green.


    Generated by Claude Code

  8. claude commented on Aug 31, 2026

    @claude
    Contributor

    os-dev-report

    Patch round on PR #7001 — pushed 25af4a84. The failure was mine, not a flake: Test (shard 4/4) on cd90cd1a.

    Before / after on the failing test

    packages/types/src/__tests__/package-exports-manifest.test.ts, run on its own:

    • before — Test Files 1 failed (1) · Tests 1 failed | 3 passed (4), expected 'tsc && node ../../scripts/check-dist-…' to be 'tsc'
    • after — Test Files 1 passed (1) · Tests 4 passed (4)

    The sweep — hit list and how each was resolved

    Nine passes, widening from the reported symptom. Every test that reads a build or clean script anywhere in the repo:

    site verdict
    packages/types/src/__tests__/package-exports-manifest.test.ts:67 the only real break — fixed
    scripts/__tests__/check-dist-completeness.test.ts:285,293 mine, already asserts the new shape
    scripts/__tests__/package-files-exist.test.ts:155,156,992,1009 tolerant — uses Boolean(scripts.build) and regex-tests for build-css; my clause adds no CSS step, and the app-shell case asserts its build does NOT match build-css, still true
    scripts/__tests__/vite-dts-wiring-ratchet.test.ts:77 tolerant — /^tsc\s*&&/ against packages/fields, which this diff does not touch
    scripts/__tests__/check-phantom-dependencies.test.ts:582 tolerant — tests for vite build; none of the twelve has one
    scripts/__tests__/plugin-published-stylesheet.test.ts:184 plugin packages only, untouched
    scripts/__tests__/check-node-esm-load.test.ts:361,373 reads manifests it writes into a tmpdir, not workspace ones
    packages/cli/src/__tests__/app-generator.test.ts:1421 scaffold template output, not a workspace manifest
    packages/create-plugin/src/__tests__/templates.test.ts:293 pins scripts.test, not build

    Two things the wider passes turned up that are worth recording:

    • examples/schema-catalog also has "build": "tsc" and "clean": "rm -rf dist", so it looks like a thirteenth instance. It is not one: it sets neither composite nor incremental, so TypeScript resolves no buildinfo path for it (getTsBuildInfoEmitOutputFilePath returns undefined) and the desync this card is about cannot occur. Measured, not assumed. It is also outside the gate's packages/apps scan roots.
    • packages/core/src/types/index.ts:59 already documents in prose that the build info lives outside dist/ — independent corroboration of the mechanism, written before this card.

    The fix, and why it is not a weakening

    The test's own title and comment say what it pins: no bundler that could emit a second (CJS) format. toBe('tsc') conflated that meaning with a literal string, so a post-build assertion that emits nothing at all turned it red while the CJS question was untouched.

    It now reads the build as a chain: the emitting step must be bare tsc, and every later step is enumerated in the test.

    const steps = (pkg.scripts?.build ?? '').split('&&').map((step) => step.trim());
    expect(steps[0]).toBe('tsc');
    expect(steps.slice(1)).toEqual(['node ../../scripts/check-dist-completeness.mjs']);

    Not deleted, not skipped, not quarantined, and no looser: both spellings fail on any change to the script — this one fails for the right reason. Proven by ablation rather than argued, mutating the manifest and observing the verdict (each mutation confirmed on disk, then restored and verified by git diff HEAD empty and the blob hash matching HEAD):

    • append a bundler after the check (… && tsup --format cjs) → Tests 1 failed | 3 passed red
    • replace the emit step with a bundler (tsup --format cjs,esm) → Tests 1 failed | 3 passed red

    On the hint about importing buildsWithTsc(): verified, and it is not available. packages/types' test program sets allowJs: false and includes only src/**/*.test.ts, so a probe import failed with TS7016: Could not find a declaration file for module '../../../../scripts/check-dist-completeness.mjs'. The predicate is therefore restated, with a comment naming the sibling and marking the duplication structural — the idiom scripts/invoked-as.mjs already uses for its own deliberate copies.

    Test surface run this time

    The changed files, not the declared file surface — all twelve touched packages plus scripts/__tests__:

    • scripts/__tests__/ + types, core, react, i18n, auth, sdui-parser, permissions, collaboration, mobile, providers, react-runtime — 454 files, 7976 tests, all passing
    • packages/app-shell/ in four shards — 145 + 145 + 145 + 144 files, 1476 + 1207 + 1574 + 1441 tests — 579 files, 5698 tests, all passing

    1,033 test files and 13,674 tests green at 25af4a84. The single run over the whole surface hit the container's 10-minute foreground cap (exit 143) and was split rather than backgrounded; the shared verify lock released cleanly on the kill.

    Also green at 25af4a84: eslint on all three touched files, tsc -p packages/types/tsconfig.test.json (and --listFiles confirms the edited file is inside that program, so the pass is not vacuous), check-entry-guard, check-control-bytes, check-changeset-no-major, check-changeset-presence, check-dist-completeness --all.

    On shards 2 and 3

    They have since finished. Reading the check runs for cd90cd1a: 30 checks — 26 success, 3 skipped, 1 failure, and the one failure is Test (shard 4/4). Nothing else of this class surfaced, so this push is the whole of it.


    Generated by Claude Code

  9. claude commented on Aug 31, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 6703,
      "status": "done",
      "branch": "claude/issue-6703-types-dist-completeness",
      "pr": "https://github.com/objectstack-ai/objectui/pull/7001",
      "premise_still_valid": true,
      "summary": "PATCH ROUND, head now 25af4a84. Test (shard 4/4) was red on cd90cd1a and it was this PR's own failure: packages/types/src/__tests__/package-exports-manifest.test.ts pins the build script to prove no bundler could emit a second CJS format, but spelled that as toBe('tsc'), so appending a post-build assertion that emits nothing at all still turned it red — a spelling stricter than its own stated meaning. Fixed at that altitude: the build is now read as a chain, the EMITTING step must be bare tsc, and every later step is enumerated in the test, so a bundler still cannot arrive without editing the expectation. Not deleted, skipped or loosened. Importing buildsWithTsc() across the boundary was checked and is not available — that package's test program sets allowJs false and includes only src/**/*.test.ts, so a probe import fails TS7016; the predicate is restated with the duplication marked structural, the idiom scripts/invoked-as.mjs already uses. Unchanged from the first round: the deterministic reproduction, the turbo cache-poisoning chain, the gate derived from TypeScript's own API, the wiring inside each build, and the six clean scripts.",
      "tests": "Union at 25af4a84. BEFORE/AFTER on the reported failure, run on its own file: before 'Test Files 1 failed (1) / Tests 1 failed | 3 passed (4)' with \"expected 'tsc && node ../../scripts/check-dist-...' to be 'tsc'\"; after 'Test Files 1 passed (1) / Tests 4 passed (4)'. TEETH PRESERVED, proven by ablation not argument (fix committed first, so the restore leg had a real reference; each mutation confirmed on disk by injected-text count plus a non-empty git diff HEAD, then restored and verified BOTH by 'git diff HEAD' empty AND by the blob hash matching HEAD): appending 'tsup --format cjs' after the check -> 1 failed | 3 passed, red; replacing the emit step with 'tsup --format cjs,esm' -> 1 failed | 3 passed, red. TEST SURFACE covering the CHANGED files rather than the declared file surface — all twelve touched packages plus scripts/__tests__: batch 1 (scripts/__tests__ + types, core, react, i18n, auth, sdui-parser, permissions, collaboration, mobile, providers, react-runtime) 454 files / 7976 tests all passing; batch 2 packages/app-shell in four shards, 145+145+145+144 files and 1476+1207+1574+1441 tests, 579 files / 5698 tests all passing. Total 1033 files / 13674 tests green. The single whole-surface run hit the container's 10-minute foreground cap (exit 143) and was SPLIT rather than backgrounded; the shared verify lock released cleanly on the kill (--status showed 'lock is free'). Also green at 25af4a84: eslint on all three touched files; tsc -p packages/types/tsconfig.test.json, with --listFiles confirming the edited file is INSIDE that program (1 hit) so the pass is not vacuous; check-entry-guard; check-control-bytes; check-changeset-no-major; check-changeset-presence; check-dist-completeness --all. SHARDS 2 AND 3: check runs for cd90cd1a are now complete — 30 checks, 26 success, 3 skipped, 1 failure, and the single failure is Test (shard 4/4). Nothing else of this class surfaced.",
      "mcp_calls": "1 — unchanged; one MCP GitHub call for the dev claim comment. The card came from the zero-quota single-card payload channel; dedup, the PR, the CI check-run readings and every report comment went through repo-scoped REST.",
      "open_questions": [
        {
          "question": "Unchanged from the first report and still open: should tsBuildInfoFile be relocated into each package's outDir, so dist and its record cannot desync at all? Reported rather than built — the scope fence sends architectural build-config changes to the maintainer, and this one touches twelve packages on a card with no triage ruling.",
          "options": [
            "A. Leave as is. The gate detects every desync at the moment it would have gone silent and prints the one-line repair; turbo can no longer record a poisoned entry. The desync remains possible, just never silent.",
            "B. Also point tsBuildInfoFile inside outDir in the twelve packages. Any route that removes or replaces dist wholesale then takes the record with it, making the desync structurally impossible rather than merely detectable. Does NOT cover a partial truncation that leaves the buildinfo, so it complements the gate rather than replacing it.",
            "C. Both, as separate PRs — B is a one-key tsconfig change per package and is independently reviewable."
          ],
          "recommendation": "A now, C next — unchanged. A is what this PR ships and it closes the expensive half, the silence and the cache poisoning that spread it across worktrees. B is better on long-term soundness but is a build-config change to twelve packages with no ruling on this card, and B alone would not catch the partial-truncation case the card actually reported."
        }
      ],
      "out_of_scope_findings": [
        "already filed as #6893: scripts/__tests__/check-sdui-registration-pins.test.ts pins a src/ path that flips to the dist/ spelling once packages/app-shell is built, so its verdict depends on whether the tree has been built. Confirmed by dedup search, not re-filed. Note: it did not fire this round, because the rebuilt worktree has no app-shell dist — which is itself the evidence for the attribution.",
        "already filed as #6943 and #6861: check:published-dist is red on main because @object-ui/fields ships dist/__tests__/numberInputBrowserReadings.d.ts in its tarball; source landed in #6801 on 2026-08-30. Confirmed by dedup search, not re-filed.",
        "not filed, reported here as a measurement rather than a defect: examples/schema-catalog also has 'build: tsc' and 'clean: rm -rf dist' and looks like a thirteenth instance, but sets neither composite nor incremental, so TypeScript resolves no buildinfo path for it and this desync cannot occur there. It is also outside the gate's packages/apps scan roots. No action needed; recorded so the next reader does not have to re-derive it."
      ]
    }

    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

Labels

domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repopriority:p1tooling

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions