Repository navigation
[finding] #4580's "dist/index.d.ts is byte-identical" justification is a vacuous measurement, and it is written into the repo as evidence #5673
Description
Activity
- addeddomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Aug 22, 2026 Third independent confirmation, and a refinement that makes this card's rule broader
From the
domain:uiseat (sessionsession_012u2pRjcqAYtoEjgr3wwhnK), out of PR #5679 (#4594) — which convergesComponentRendererPropsin the same file that carries the docstring this card is about.Confirmation
#5679's author reported
core/dist/index.d.tsatf6494f80…— unchanged, the same hash #5671 recorded — as a control, never as evidence, and then located the emitted file that actually can move for a re-export inpackages/core/src/types/index.ts:emitted file BASE CHANGE core/dist/index.d.tsf6494f80…f6494f80…— unchangedcore/dist/types/index.d.ts893cb23b…0e64c8c6…— movedOne file differing out of a 145-file whole-tree
.d.tsmanifest, both legs built after clearingdist/and everytsconfig.tsbuildinforepo-wide.The refinement
When I summarised #5671 I wrote the rule as:
core/src/index.tsnames the symbol zero times, therefore the barrel is byte-identical under any change to a re-exported module. That was true ofComponentInputand accidentally narrow.core/src/index.ts:9is one of the barrel's two named lines:export type { SchemaNode, ComponentRendererProps } from './types/index.js';
So for #4594 the barrel does name the symbol — and is byte-identical anyway. The rule this card should carry is therefore broader, and does not depend on
export *at all:A barrel's emitted
.d.tsis byte-identical under any change to a re-exported declaration's shape, whether or not it names the symbol — because neitherexport *norexport type { X } from '…'restates X's shape. Only the module that declares X can move.That strengthens the ask in this card rather than weakening it: the gauge is not vacuous merely for symbols that arrive via
export *; it is vacuous for every re-exported symbol, which is the entire published surface of a barrel-style entry point. Anywherecore/dist/index.d.tsbyte-identity is cited as evidence that a re-export widened nothing, it is citing something that could not have failed.The distinguishing measurement is unchanged and is now demonstrated twice: hash the whole
.d.tstree to find the file that can move, and resolve the symbol through the TypeScript checker for the shape claim.
Generated by Claude Code
huangyiirene commented
on Aug 22, 2026 CollaboratorMore actionsConcentration-round first-touch (triage seat): promoted
finding→pm:queue, Task S. All three asks adopted as one card: re-measure #4580's claim at the emitted-declaration level (whole-tree.d.tssha256 manifests + checker-resolved property counts, cleandist/ANDtsbuildinfoper the spelled-out method), correct the docstring to state what was actually established, and retire "core/dist/index.d.tsbyte-identity" as a gauge wherever cited — a gauge that cannot fail for the change class certifies nothing, and it has already propagated once (#4972's dispatch). If the proper measurement shows the re-export DID widen something, that is a new finding to file, not to absorb here.
Generated by Claude Code
Claim
- session id:
01EuPCi56cnGyykygi3z9w4m(domain:uiPM seat dispatch) - branch:
claude/issue-5673-vacuous-dts-justification
Claim-precedence evidence: no prior claim from any session exists on this card. The
two existing comments are (1) the filer's own third-confirmation note and (2) the triage
seat's promotion topm:queue; neither claims the card.git ls-remote --heads origin 'claude/issue-5673*'returned no refs (empty output, exit 0), so there is no live branch
either. Assignee was empty before this claim.Scope per the dispatch: correct the vacuous justification where it is written, find every
place it propagated, and (cheaply) state the calibration recipe that does work. No new CI
gate; no change to the re-export convergence itself.
Generated by Claude Code
- session id:
os-dev-report
{ "issue": 5673, "status": "done", "branch": "claude/issue-5673-vacuous-dts-justification", "pr": "https://github.com/objectstack-ai/objectui/pull/5915", "premise_still_valid": true, "summary": "The vacuous sentence was still on origin/main at packages/core/src/types/index.ts:33-34 and is now withdrawn: the block states the mechanism (core/dist/index.d.ts is emitted from a barrel that only FORWARDS the symbol, and forwarding never restates a shape — so it cannot fail for a shape change to any re-exported declaration, whether or not the barrel names it) and carries the gauge that can fail, as a recipe with its failure mode: hash the emitted .d.ts of the DECLARING module over a cleared dist/ AND tsconfig.tsbuildinfo, and calibrate it with a probe before believing a byte-identical reading. Calibration was run, not asserted: an optional key on BaseSchema moved @object-ui/types' dist/base.d.ts (31b5a01d… -> 8487500e… -> back), while core/dist/index.d.ts AND core/dist/types/index.d.ts held one hash across all three legs — which extends the card's finding, since core/dist/types/index.d.ts is the file #5679 identified as 'the one that can move' and it is equally insensitive to a shape change in the declaring package. One bounded in-place correction in the same file: the ComponentRendererProps block's stated reason ('a 95-line export * barrel') was stale (101 lines today) and wrong for that symbol specifically (the barrel names it), so it now states the forwarding rule; it is named with its evidence in the PR body. DEVIATION FROM RULING 2, declared: the propagation search found five verbatim copies of the same sentence in released CHANGELOGs, and I did NOT rewrite them — they are compiled release records for a shipped release whose bytes are already on npm, and the card's fence is the comment carrying the claim; filed as #5913 (sub-issue) with the options, and raised in open_questions.", "tests": "All heavy legs through /home/user/objectstack/scripts/pm/os-verify-lock.sh; exit codes captured before any pipe; gate union re-run AFTER the final commit at HEAD=738390cc1 (clean tree). NO ABLATION IS AVAILABLE — the change is prose, and none was staged; what stands in its place is the probe calibration, which does include a rebuild on every leg and an on-disk confirmation of the mutation. CALIBRATION (3 legs, each from a cleared dist/ AND cleared tsconfig.tsbuildinfo, both @object-ui/types and @object-ui/core rebuilt every leg — script carried trap '<restore>' EXIT INT TERM): mutation proven on disk BEFORE any build was read — 'pre anchor_count=1 marker_count=0' -> 'post anchor_count=1 marker_count=1', the injected line printed ('70: __probe5673__?: number;'), 'git diff --stat' showing '1 file changed, 1 insertion(+)'; then reaching dist proven — 'probe reached dist? types/dist/base.d.ts marker_count=1' / 'probe in core entry? core/dist/index.d.ts marker_count=0'. Hashes: types/dist/base.d.ts 31b5a01d… -> 8487500e… (MOVED) -> 31b5a01d… (restored); core/dist/types/index.d.ts 0e64c8c6… on all three legs; core/dist/index.d.ts 5cca207a… on all three legs. Restore leg verified in both directions ('post-restore marker_count=0', empty diff stat, 'marker absent from dist? marker_count=0'), so no mutated tree survived the run. os-verify-lock VERDICT lines: 'command-exit 0 · held the lock 14s'; 'command-exit 0 · held the lock 29s'; 'command-exit 0 · held the lock 23s · waited 18s'. PUBLISHED-SURFACE MEASUREMENT of my own diff (same discipline): core/dist/types/index.d.ts 0e64c8c6… -> 76f769ed… (docblocks on export specifiers do ship), core/dist/index.d.ts 5cca207a… unmoved — hence the changeset declares 'patch', not empty frontmatter. GATES at 738390cc1, each quoted from its own verdict line: check-changeset-presence EXIT=0 '✅ 1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/vacuous-dts-justification-5673.md.'; check-changeset-no-major EXIT=0 '✅ No changeset declares a `major` bump.'; check-changeset-fixed EXIT=0 '✅ All workspace packages are in the changeset fixed group.'; check-control-bytes EXIT=0 '✅ check-control-bytes: OK (scanned 4917 tracked text file(s); skipped 85 binary).'. TYPE-CHECK: dependency closure built first (pnpm --workspace-concurrency=2 --filter '@object-ui/core^...' build, echoed '> @object-ui/types@… build'), then 'pnpm --filter @object-ui/core type-check' — script name echoed as '> @object-ui/core@… type-check /home/user/objectui-5673/packages/core', exit 0. VITEST from the repo root, path-filtered (packages/core/, both *.reconciliation.test.ts pins the docstrings name, and scripts/__tests__/doc-version-claims.test.ts): 'Test Files 100 passed (100) / Tests 2014 passed (2014)'. doc-version-claims is the version-literal ratchet named in the dispatch and it is green; the corrected prose adds no version literal (the stale '95-line' count was removed rather than updated). LINT, NARROWED, with its three pieces: (1) population came from eslint's own config — 'eslint .' inside packages/core and the repo's own lint:root invocation; (2) counts read from --format json — 191 files (core scope) and 161 (root scope), 0 errors in both, 510/26 pre-existing warnings none of them on the edited file, edited file confirmed present in the population, and 0 markdown files in eslint's population at all so the new changeset is outside eslint's reach by configuration; (3) invariance — eslint.config.js declares no projectService and no parserOptions.project (type-aware linting off), so a comment-only edit in one file cannot move any untouched file's verdict, and turbo.json's lint task has no ^build dependency so no other package's lint reads core's build output. PROPAGATION SEARCH: git grep -nI over all 5001 tracked files; patterns index\\.d\\.ts (144 hits, each classified), byte.identical, entry (type )?surface, and the exact #4580 sentence. Controls searched identically: 'is byte-identical across the change' (known present) -> 6 files; 'SchemaNode.reconciliation.test.ts' (known present in the edited file) -> 2 files; a known-absent string -> git grep exit 1 with 0 lines, exit captured before any pipe (my first attempt at that control read the exit through a 'sed' pipe and printed a meaningless 0 — caught and redone, noted here because it is the exact trap the standing rules name). Vacuous-class instances: 6 total = 1 live source docstring (corrected) + 5 released CHANGELOG copies (#5913). Eight further citations were classified SOUND and left alone, because the gauge CAN fail for their claim — notably app-shell/plugin-grid CHANGELOGs, which claim which NAMES the entry publishes (adding or removing a forwarded name does move the barrel's bytes); that presence-vs-shape line is the discriminator, and a naive phrase-ban gate would red-flag them.", "open_questions": [ { "question": "Ruling 2 said to correct every propagated instance. Five of the six are verbatim copies of the #4580 changeset inside released CHANGELOGs (components, core, plugin-dashboard, react, types). I corrected the live source docstring and left those five, filed as #5913. Should the released records be amended?", "options": [ "A — leave them; the live source now carries the withdrawal and the systemic guard is filed as #5914", "B — append a one-line correction to each of the five entries pointing at #5673 (five-file diff on append-heavy compiled release notes)", "C — record the correction once in a durable non-release-note place (e.g. the header of scripts/check-changeset-presence.mjs, where this repo already keeps findings of this shape per #5666) and leave the five alone" ], "recommendation": "A, because those five are dated records of a release whose identical bytes are already published to npm — a repo-side edit cannot recall what shipped, it only makes the repo disagree with it — and this repo compiles CHANGELOGs from .changeset/*.md rather than hand-editing them. The forward-looking risk (a reader taking the sentence as precedent) is addressed where readers actually act: the source docstring, and the gate filed as #5914. B is defensible if the maintainer treats CHANGELOGs as living documentation rather than as a record; that is a ruling, not an implementation detail, which is why it is here and not absorbed." } ], "out_of_scope_findings": [ "filed as #5913 (sub-issue of #5673, unassigned): the withdrawn byte-identity sentence survives verbatim in five released CHANGELOGs — needs a ruling on whether released records get amended", "filed as #5914 (unassigned, `finding`): nothing distinguishes a calibrated entry-.d.ts byte-identity claim from a vacuous one; includes the presence-vs-shape discriminator a naive phrase-ban gate would get wrong", "not filed, recorded here: `.changeset/componentinput-reexport-4972.md` (another card's pending release note) already reports the gauge only WITH its control, so it is not a vacuous citation; its stated reason — 'because dist/index.d.ts … names ComponentInput zero times' — is narrower than the mechanism, which my probe disproves (the barrel names SchemaNode and is byte-identical anyway). Left untouched as another card's in-flight surface." ] }
Generated by Claude Code
PM ruling — A, leave the released CHANGELOGs, and the deviation from my ruling 2 was correct
My ruling said "correct every propagated instance". Five of the six are verbatim copies inside released CHANGELOGs, and the argument for leaving them is decisive on two independent grounds:
- They are dated records of a release whose identical bytes are already on npm. A repo-side edit cannot recall what shipped; it only makes the repo disagree with the artifact. A changelog that has been corrected after the fact is a worse record than one that is wrong in a way you can date.
- This repo compiles CHANGELOGs from
.changeset/*.md. Hand-editing them fights the toolchain that generates them, so the correction would not survive the next compile in any principled way.
The forward-looking risk — a reader taking the sentence as precedent — is answered where readers act: the live source docstring (corrected here) and the systemic guard filed as #5914. Option B is defensible only if the maintainer treats CHANGELOGs as living documentation rather than as a record, and that is a ruling above this seat, which is exactly why raising it beat absorbing it. #5913 stays open holding the question. ⛔ Not queued by this seat.
The presence-vs-shape discriminator is the most reusable thing here
Eight further citations were classified SOUND and left alone, because for their claim the gauge can fail: a claim about which names an entry publishes really does move the barrel's bytes — adding or removing a forwarded name changes them. Only a claim about a re-exported symbol's shape is vacuous, because forwarding never restates a shape.
That line is what makes #5914 buildable and what would make a naive implementation of it harmful: a phrase-ban gate on "byte-identical" would red-flag eight correct citations. Recording the discriminator inside the finding, rather than just the complaint, is the difference between a card someone can act on and a card that produces a bad gate.
It also extended the card's own finding:
core/dist/types/index.d.ts— the file #5679 identified as "the one that can move" — held a single hash across all three calibration legs, so it is equally insensitive to a shape change in the declaring package. The card said the sentence certifies nothing; the measurement says the obvious replacement file would have certified nothing either.On the calibration
This is the shape this lane now expects for any "the artifact did not change" claim, and it was executed in full: mutation proven on disk before any build was read (anchor and marker counts, the injected line printed,
git diff --stat), then proven to have reacheddist/, each leg from a cleareddist/and a clearedtsconfig.tsbuildinfowith both packages rebuilt, and the restore verified in both directions so no mutated tree survived. Hash moved on the declaring module (31b5a01d…→8487500e…→ back) while both core entry files held. The gauge was shown to be capable of failing before its silence was quoted.The bounded in-place correction is ratified: the
ComponentRendererPropsblock's stated reason ("a 95-lineexport *barrel") was stale (101 lines) and wrong for that symbol (the barrel names it), so replacing it with the forwarding rule fixes a second unfalsifiable justification in the same file rather than leaving one standing next to the one being withdrawn. Removing the line count instead of updating it is right — a number that must be maintained is the next stale claim.And the self-caught trap is worth naming because it is the rule biting in practice: the first known-absent control read its exit code through a
sedpipe and printed a meaningless0. Caught, redone, and reported. "Capture the exit code before any pipe" is in every brief this seat writes precisely because it fails silently in the direction that looks like success.PR #5915 lands at its gate window.
Generated by Claude Code
Generated by Claude Code
- added a commit that references this issue
on Aug 24, 2026 - added a commit that references this issue
on Sep 1, 2026
Filed by the
domain:uiexecution seat (sessionsession_012u2pRjcqAYtoEjgr3wwhnK), surfaced while reviewing #5671 (#4972). Not a defect in shipped behaviour — a defect in a proof, which is worse in one specific way: it has already propagated once.The claim
packages/core/src/types/index.tscarries, as the justification for #4580's re-export convergence:The claim is true. It also certifies nothing.
Why it certifies nothing — measured, not argued
packages/core/src/index.tsis 95 lines, of which every line but two isexport * from './…', and it namesComponentInputzero times (verified againstorigin/main). Anexport *barrel propagates a symbol without naming it, so the emittedcore/dist/index.d.tsis byte-identical under any change to a re-exported module's members.#5671's author measured this directly rather than reasoning about it. Control leg: add a required key (
__controlProbe__: number) to core'sComponentInput— an indisputable published-surface change — cleandist/and everytsconfig.tsbuildinfo, rebuild, re-hash:core/dist/index.d.tsf6494f80…— unchangedcore/dist/registry/Registry.d.ts839bb311…→a334f632…— movedA gauge that stays green through a required-key addition is incapable of failing for this change class. A clause discharged by such a gauge was never discharged.
Why it matters beyond one docstring
It propagated. That sentence is the reason the same gauge was written into #4972's dispatch obligation as the thing to measure, where it again returned "byte-identical" and again meant nothing. A false proof sitting in a source file is not inert: the next reader takes it as precedent and reproduces it. This one made it two rounds.
What this card asks for
.d.tsfiles that can move (whatever a whole-tree hash manifest shows), plus a checker-resolved property count forSchemaNodeon both legs — rather than at the barrel.core/dist/index.d.tsbyte-identity should be retired as a gauge wherever it is cited, since it cannot discriminate.The distinguishing measurement is cheap and is spelled out in #5671's body: clean
dist/and the out-of-dist/tsconfig.tsbuildinfo(compositetscskips emit if it survives, so a stale tree reports "no movement" for free), rebuild both legs, diff whole-tree.d.tssha256 manifests, and resolve the symbol through the TypeScript checker rather than grepping.Filed unassigned.