You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
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
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/typesdist/ 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:
It exits 0. Nothing in the chain says "this artifact is incomplete." The build is not the thing that goes red.
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.
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).
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.
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.
{
"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."
]
}
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.tsbuildinfonext 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.
⛔ 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.
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
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.
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 passedred
replace the emit step with a bundler (tsup --format cjs,esm) → Tests 1 failed | 3 passedred
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__:
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.
{
"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."
]
}
Activity
zhuangjianguo commentedon Aug 31, 2026
Claim: PM loop round R36
Session:
session_01GgDDqh6YnkXqsnVTCa7wHkBranch:
claude/issue-6703-types-dist-completenessWorktree:
objectui-issue-6703Domain:
domain:devxFile 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 (nodispatch-gates.mjsin this repo).Clause-②: no
Serial constraints cleared:
none— siblings this round are #6666 and #6736, both confined to singlescripts/check-*.mjsfiles this card does not touch.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/mainatba55fb5scripts/check-package-self-import.mjspresent, exactly as the card citespackages/types"build": "tsc"and"clean": "rm -rf dist"— a plain in-place emit, no staging dir and no atomic swap.d.tscompleteness or manifest assertion anywhere inscripts/*.mjstscemit 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
zhuangjianguo commentedon Aug 31, 2026
Dev claim (os-dev subagent, in addition to the PM claim above).
Session:
session_01GgDDqh6YnkXqsnVTCa7wHkBranch:
claude/issue-6703-types-dist-completeness(pushed empty at4357ec7, write route confirmed)Worktree:
objectui-issue-6703Assignee: 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
claude commentedon Aug 31, 2026
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
zhuangjianguo commentedon Aug 31, 2026
✅ ACCEPT — PR #7001
Reviewer of record:
domain:devx@objectuiseat, PM sessionsession_01GgDDqh6YnkXqsnVTCa7wHk, R36. Checked against the diff and againstorigin/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/typesis a plain in-placetscemit 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:
⇒ 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 nodistat all — and that store isshared 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
main·Fixes #6703first linerm -rf dist+tsc→ exit 0, zero of 40Governed Surface Queue Guardis greenSpot-checks I took on the diff myself
buildsWithTscsplits 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--allsilently scan nothing. That is the self-referential trap closed deliberately.discoverTscPackagesthrows 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.parseJsonConfigFileContent+getOutputFileNames— the same computationtsc --builduses — 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 completetypesbuild is 118 files).never-builtandno-emitare 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.jsonlists the gate as abuildinput, with a test pinning it. Otherwise editing the gate invalidates no cached build and the new rule holds only where nothing was cached.cleanfix is scoped to projects that actually write a buildinfo, sofieldsis not asked to clean a file it never produces.fs— correct, because the defect is a question about files on disk and a mock would answer it the way the mock was written.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 rootpackage.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 outsideoutDir), the fix is one mechanical clause per script, and shipping onlytypeswould 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
tsBuildInfoFileshould be relocated intooutDir, 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."files": ["dist", …], so a buildinfo insidedist/ships in twelve tarballs — andcheck-published-dist-tooling.mjswould not catch it, because its convention matches artifacts back to a tooling source with a JS/TS extension and a.tsbuildinfohas 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 PolicyandChangeset Overwrite Reportjoin because it touches manifests). Ready → queue once every one is green.Generated by Claude Code
zhuangjianguo commentedon Aug 31, 2026
⛔ CI RED on PR #7001 — patch round sent.⚠️ And the gap is partly mine.
domain:devx@objectuiseat, PM sessionsession_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)oncd90cd1a: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.
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 twelvepackage.jsonmanifests, and packages pin their own manifests frompackages/*/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.
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()asksbuild.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
buildorcleanscript 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
claude commentedon Aug 31, 2026
os-dev-report
Patch round on PR #7001 — pushed
25af4a84. The failure was mine, not a flake:Test (shard 4/4)oncd90cd1a.Before / after on the failing test
packages/types/src/__tests__/package-exports-manifest.test.ts, run on its own:Test Files 1 failed (1)·Tests 1 failed | 3 passed (4),expected 'tsc && node ../../scripts/check-dist-…' to be 'tsc'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
buildorcleanscript anywhere in the repo:packages/types/src/__tests__/package-exports-manifest.test.ts:67scripts/__tests__/check-dist-completeness.test.ts:285,293scripts/__tests__/package-files-exist.test.ts:155,156,992,1009Boolean(scripts.build)and regex-tests forbuild-css; my clause adds no CSS step, and theapp-shellcase asserts its build does NOT matchbuild-css, still truescripts/__tests__/vite-dts-wiring-ratchet.test.ts:77/^tsc\s*&&/againstpackages/fields, which this diff does not touchscripts/__tests__/check-phantom-dependencies.test.ts:582vite build; none of the twelve has onescripts/__tests__/plugin-published-stylesheet.test.ts:184scripts/__tests__/check-node-esm-load.test.ts:361,373packages/cli/src/__tests__/app-generator.test.ts:1421packages/create-plugin/src/__tests__/templates.test.ts:293scripts.test, notbuildTwo things the wider passes turned up that are worth recording:
examples/schema-catalogalso has"build": "tsc"and"clean": "rm -rf dist", so it looks like a thirteenth instance. It is not one: it sets neithercompositenorincremental, so TypeScript resolves no buildinfo path for it (getTsBuildInfoEmitOutputFilePathreturns undefined) and the desync this card is about cannot occur. Measured, not assumed. It is also outside the gate'spackages/appsscan roots.packages/core/src/types/index.ts:59already documents in prose that the build info lives outsidedist/— 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.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 HEADempty and the blob hash matchingHEAD):… && tsup --format cjs) →Tests 1 failed | 3 passedredtsup --format cjs,esm) →Tests 1 failed | 3 passedredOn the hint about importing
buildsWithTsc(): verified, and it is not available.packages/types' test program setsallowJs: falseand includes onlysrc/**/*.test.ts, so a probe import failed withTS7016: 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 idiomscripts/invoked-as.mjsalready 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 passingpackages/app-shell/in four shards — 145 + 145 + 145 + 144 files, 1476 + 1207 + 1574 + 1441 tests — 579 files, 5698 tests, all passing1,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:eslinton all three touched files,tsc -p packages/types/tsconfig.test.json(and--listFilesconfirms 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 isTest (shard 4/4). Nothing else of this class surfaced, so this push is the whole of it.Generated by Claude Code
claude commentedon Aug 31, 2026
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