fix(types): read the SPEC_DERIVED_PAIRS re-check off the AST, not raw text - #7277

Merged
yinlianghui merged 4 commits into
mainfrom
claude/issue-6705-parity-scan-comments
Sep 2, 2026
Merged

fix(types): read the SPEC_DERIVED_PAIRS re-check off the AST, not raw text#7277
yinlianghui merged 4 commits into
mainfrom
claude/issue-6705-parity-scan-comments

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes#6705

What was wrong

The SPEC_DERIVED_PAIRS re-check decided which zod mirrors are spec-derived by slicing the source between export const boundaries and testing the slice against a \bSpec[A-Z] word pattern. Two independent defects, both of which one docstring fired:

  1. A comment counted as a reference. Raw text cannot tell SpecGanttConfigSchema.extend(...) from the same token in prose.
  2. The window was not the declaration. The slice ran from export const X to the nextexport, so everything in between — private consts and the next export's own docstring — was charged to X.

The fix

specReferencingExports(fileName, source) parses instead of scanning:

  • Spec… identifiers come off the AST, so comments and string literals are not references. Nothing strips anything, so there is no comment-stripping regex to get wrong on a string containing */ sequences — a fixture pins exactly that string.
  • Attribution is by declaration. File-local references through private consts are resolved to a fixed point, so a mirror built from a spec-derived field map keeps its dependency; exported mirrors are not hopped through, since each is a registered pair carrying its own entry.

Two entries leave the derived list — please read this part

This is the one thing in the PR that is not purely mechanical, and it is reported rather than absorbed. Neither mirror changed; the re-check stopped mis-reading them. Neither references a spec schema:

EntryWhy it was in the list
base.zod.ts#BaseSchemaIts file's onlySpec… token is in a comment: rather than calling `SpecSchema.omit({…}).partial()`. Prose.
objectql.zod.ts#ObjectViewSchemaBaseSchema.extend({…}) where every member is a local literal. Its declaration ends at line 212; the next export const is ~50 lines later, and the old window swallowed the private KanbanConfig = SpecKanbanConfigSchema… block that belongs to no export.

⚠️The ObjectViewSchema removal has a consequence this PR deliberately did not settle. This file's header records objectui#6058's remedy split, which routes ObjectViewSchema's unmirrored declared keys as SPEC-DERIVED — "the LOCAL declaration carries members the spec schema does not model, which is objectui#2231's unification question and NOT a local mirror edit". That routing rests on the false positive. Re-routing those keys is a remedy decision on the UnmirroredDeclared ledger, which this card was fenced out of, so the split is left standing as written, counts and all, with a ⚠️ note at the site telling whoever works it off to re-derive that side first. No ledger (KnownDrift / UnmirroredDeclared / RuntimeOnlyDeclared) and no corpus schema is touched.

Evidence — both directions, and the gate is not blinded

The card's fence is that a fix which stops seeing comments and stops seeing real references goes green on the old suite while destroying the gate.

Live reproduction (ablation 1). The pre-fix scanner, run against this branch's restored docstring, reproduces the reported failure exactly — and names the innocent neighbour:

AssertionError: a mirror gained or lost a spec dependency: expected [ …(11) ] to deeply equal [ …(10) ]
+ "objectql.zod.ts#ObjectTreeSchema"

Mutation proven on disk (old window code present = 1, new AST helper present = 0); restored by blob hash, 1a45bced… before and after.

Positive direction (ablation 2). Neutering the fixed helper to return an empty set reddens 5 tests — the live re-check plus every positive fixture:

AssertionError: ... expected [] to deeply equal [ …(8) ]
AssertionError: expected [] to deeply equal [ 'ObjectGanttSchema' ] (x3)
AssertionError: expected [] to include 'ObjectGanttSchema'
Tests 5 failed | 7 passed (12)

Mutation proven on disk (injected marker = 1, removed expression = 0); restored by blob hash with git diff HEAD empty. All eight surviving pairs are found by code reference alone.

Seven fixtures pin the shape in memory: comment-only mention yields nothing; code reference fires; a Spec… token inside a string literal carrying */ and // is not a reference; a private const between two exports charges its prose to neither neighbour; a private const with a real reference is charged to its user, not its neighbour; the next export's docstring is not charged to the previous export; plus a non-vacuity pin against the real objectql.zod.ts.

The docstring restoration

PR #6704 reworded a docstring to dodge the literal token and left a trap comment asking the next editor to remember. That convention is what a gate is supposed to replace, so with the scanner fixed the workaround is retired and the docstring says what it wanted to say. This is the only change under packages/types/src/zod/, it is comment-only, and it doubles as the live positive proof above.

Gates run — all verdict lines quoted, against final commit 85e014af2

GateVerdict
parity fileTest Files 1 passed (1) / Tests 12 passed (12)
packages/types suiteTest Files 84 passed (84) / Tests 1070 passed (1070)
@object-ui/typestype-checkexit 0 — tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json
check:control-bytes✅ check-control-bytes: OK (scanned 6005 tracked text file(s); skipped 85 binary).
check:vi-mock-specifiers✅ check-vi-mock-specifiers: OK (4129 tracked source file(s) …)
check:vi-mock-inherit✅ check-vi-mock-inherit: OK (…)
check:spec-symbols✅ spec symbol derivation: 1333 files scanned against 4959 spec export names; … 0 untriaged collisions
check:phantom-deps✅ Every in-scope import is declared by the package that publishes it.
changeset presence✅ 2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s) — empty frontmatter, "the explicit exemption and a complete answer to this gate"
check-changeset-fixed / -no-major / -overwriteall ✅

type-check is a real measurement, not a vacuous pass: tsc -p tsconfig.test.json --listFiles shows the edited test file (2 hits), objectql.zod.ts (1) and typescript/lib/typescript.d.ts (1) in the compiled set.

Declared narrowing — lint. Repo-wide turbo run lint is CI's run. Locally lint was narrowed to the affected package and the narrowing is measured, not assumed: ① the population came from eslint's own traversal under the root flat config, not a guess; ② --format json reports 148 files linted, 0 errors, 267 warnings, exit 0 — the package's own lint script (eslint .) verbatim; ③ eslint.config.js declares no projectService and no parserOptions.project, so linting is not type-aware and this diff cannot move the verdict of any file it does not contain.

Merge state.origin/main was merged at 1a1235c94 (picking up #7241, #7216, #7236, #7262), and every verdict above was taken after that merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

… text
`zod-mirror-parity.test.ts` decided which mirrors are spec-derived by slicing
the source between `export const` boundaries and testing the slice against a
`\bSpec[A-Z]` word pattern. That had two independent defects, and one docstring
fired both: a `Spec…` token in PROSE counted as a reference, and the slice ran
past its own declaration to the next `export`, so the red named the neighbouring
export instead of the edited one.
`specReferencingExports()` parses instead. `Spec…` identifiers come off the AST,
so comments and string literals are not references and nothing has to strip
them. Attribution is by declaration; file-local references through PRIVATE
consts are resolved to a fixed point so a mirror built from a spec-derived field
map keeps its dependency, while exported mirrors are not hopped through.
Two entries leave the derived list as false positives of exactly those two
defects — `base.zod.ts#BaseSchema` (its file's only `Spec…` token is a comment)
and `objectql.zod.ts#ObjectViewSchema` (a `BaseSchema.extend` of local literals
whose window swallowed a private `SpecKanbanConfigSchema` block ~50 lines past
its end). No corpus and no ledger changes.
Seven fixtures pin both directions: a comment-only mention yields nothing, a
code reference still fires.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
PR #6704 reworded this docstring to dodge a literal `Spec` + capital-letter
token and left a comment asking the next editor to remember, because the
`SPEC_DERIVED_PAIRS` re-check scanned raw text and misattributed the mention to
`ObjectTreeSchema`. The previous commit made that scanner parse, so the
convention held by discipline is no longer needed and the workaround is
retired: the docstring names `SpecGanttConfigSchema` in prose again, which is
what it wanted to say.
This doubles as the live positive proof for objectui#6705 — the pre-fix scanner
goes red on this file state, the fixed one stays green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
Empty frontmatter — the presence gate's own explicit exemption. The scanner
change is test-only and the one change under `packages/types/src/zod/` is a
docstring; no runtime, type or schema surface moves.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3160.2 KB3191.4 KB
Main entry chunk (gzip)142.6 KB350 KB
Entry fileindex-BOEfHVe7.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.33KB5.59KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.46KB117.29KB
core (index.js)5.55KB2.23KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.25KB61.73KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)70.02KB19.44KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)250.65KB63.91KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.78KB32.58KB
plugin-gantt (index.js)166.77KB40.76KB
plugin-grid (index.js)208.88KB56.59KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)29.34KB8.47KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

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

Projects

None yet

2 participants

@yinlianghui@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(types): read the SPEC_DERIVED_PAIRS re-check off the AST, not raw text - #7277

Merged
yinlianghui merged 4 commits into
mainfrom
claude/issue-6705-parity-scan-comments
Sep 2, 2026
Merged

fix(types): read the SPEC_DERIVED_PAIRS re-check off the AST, not raw text#7277
yinlianghui merged 4 commits into
mainfrom
claude/issue-6705-parity-scan-comments

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes#6705

What was wrong

The SPEC_DERIVED_PAIRS re-check decided which zod mirrors are spec-derived by slicing the source between export const boundaries and testing the slice against a \bSpec[A-Z] word pattern. Two independent defects, both of which one docstring fired:

  1. A comment counted as a reference. Raw text cannot tell SpecGanttConfigSchema.extend(...) from the same token in prose.
  2. The window was not the declaration. The slice ran from export const X to the nextexport, so everything in between — private consts and the next export's own docstring — was charged to X.

The fix

specReferencingExports(fileName, source) parses instead of scanning:

  • Spec… identifiers come off the AST, so comments and string literals are not references. Nothing strips anything, so there is no comment-stripping regex to get wrong on a string containing */ sequences — a fixture pins exactly that string.
  • Attribution is by declaration. File-local references through private consts are resolved to a fixed point, so a mirror built from a spec-derived field map keeps its dependency; exported mirrors are not hopped through, since each is a registered pair carrying its own entry.

Two entries leave the derived list — please read this part

This is the one thing in the PR that is not purely mechanical, and it is reported rather than absorbed. Neither mirror changed; the re-check stopped mis-reading them. Neither references a spec schema:

EntryWhy it was in the list
base.zod.ts#BaseSchemaIts file's onlySpec… token is in a comment: rather than calling `SpecSchema.omit({…}).partial()`. Prose.
objectql.zod.ts#ObjectViewSchemaBaseSchema.extend({…}) where every member is a local literal. Its declaration ends at line 212; the next export const is ~50 lines later, and the old window swallowed the private KanbanConfig = SpecKanbanConfigSchema… block that belongs to no export.

⚠️The ObjectViewSchema removal has a consequence this PR deliberately did not settle. This file's header records objectui#6058's remedy split, which routes ObjectViewSchema's unmirrored declared keys as SPEC-DERIVED — "the LOCAL declaration carries members the spec schema does not model, which is objectui#2231's unification question and NOT a local mirror edit". That routing rests on the false positive. Re-routing those keys is a remedy decision on the UnmirroredDeclared ledger, which this card was fenced out of, so the split is left standing as written, counts and all, with a ⚠️ note at the site telling whoever works it off to re-derive that side first. No ledger (KnownDrift / UnmirroredDeclared / RuntimeOnlyDeclared) and no corpus schema is touched.

Evidence — both directions, and the gate is not blinded

The card's fence is that a fix which stops seeing comments and stops seeing real references goes green on the old suite while destroying the gate.

Live reproduction (ablation 1). The pre-fix scanner, run against this branch's restored docstring, reproduces the reported failure exactly — and names the innocent neighbour:

AssertionError: a mirror gained or lost a spec dependency: expected [ …(11) ] to deeply equal [ …(10) ]
+ "objectql.zod.ts#ObjectTreeSchema"

Mutation proven on disk (old window code present = 1, new AST helper present = 0); restored by blob hash, 1a45bced… before and after.

Positive direction (ablation 2). Neutering the fixed helper to return an empty set reddens 5 tests — the live re-check plus every positive fixture:

AssertionError: ... expected [] to deeply equal [ …(8) ]
AssertionError: expected [] to deeply equal [ 'ObjectGanttSchema' ] (x3)
AssertionError: expected [] to include 'ObjectGanttSchema'
Tests 5 failed | 7 passed (12)

Mutation proven on disk (injected marker = 1, removed expression = 0); restored by blob hash with git diff HEAD empty. All eight surviving pairs are found by code reference alone.

Seven fixtures pin the shape in memory: comment-only mention yields nothing; code reference fires; a Spec… token inside a string literal carrying */ and // is not a reference; a private const between two exports charges its prose to neither neighbour; a private const with a real reference is charged to its user, not its neighbour; the next export's docstring is not charged to the previous export; plus a non-vacuity pin against the real objectql.zod.ts.

The docstring restoration

PR #6704 reworded a docstring to dodge the literal token and left a trap comment asking the next editor to remember. That convention is what a gate is supposed to replace, so with the scanner fixed the workaround is retired and the docstring says what it wanted to say. This is the only change under packages/types/src/zod/, it is comment-only, and it doubles as the live positive proof above.

Gates run — all verdict lines quoted, against final commit 85e014af2

GateVerdict
parity fileTest Files 1 passed (1) / Tests 12 passed (12)
packages/types suiteTest Files 84 passed (84) / Tests 1070 passed (1070)
@object-ui/typestype-checkexit 0 — tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json
check:control-bytes✅ check-control-bytes: OK (scanned 6005 tracked text file(s); skipped 85 binary).
check:vi-mock-specifiers✅ check-vi-mock-specifiers: OK (4129 tracked source file(s) …)
check:vi-mock-inherit✅ check-vi-mock-inherit: OK (…)
check:spec-symbols✅ spec symbol derivation: 1333 files scanned against 4959 spec export names; … 0 untriaged collisions
check:phantom-deps✅ Every in-scope import is declared by the package that publishes it.
changeset presence✅ 2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s) — empty frontmatter, "the explicit exemption and a complete answer to this gate"
check-changeset-fixed / -no-major / -overwriteall ✅

type-check is a real measurement, not a vacuous pass: tsc -p tsconfig.test.json --listFiles shows the edited test file (2 hits), objectql.zod.ts (1) and typescript/lib/typescript.d.ts (1) in the compiled set.

Declared narrowing — lint. Repo-wide turbo run lint is CI's run. Locally lint was narrowed to the affected package and the narrowing is measured, not assumed: ① the population came from eslint's own traversal under the root flat config, not a guess; ② --format json reports 148 files linted, 0 errors, 267 warnings, exit 0 — the package's own lint script (eslint .) verbatim; ③ eslint.config.js declares no projectService and no parserOptions.project, so linting is not type-aware and this diff cannot move the verdict of any file it does not contain.

Merge state.origin/main was merged at 1a1235c94 (picking up #7241, #7216, #7236, #7262), and every verdict above was taken after that merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

… text
`zod-mirror-parity.test.ts` decided which mirrors are spec-derived by slicing
the source between `export const` boundaries and testing the slice against a
`\bSpec[A-Z]` word pattern. That had two independent defects, and one docstring
fired both: a `Spec…` token in PROSE counted as a reference, and the slice ran
past its own declaration to the next `export`, so the red named the neighbouring
export instead of the edited one.
`specReferencingExports()` parses instead. `Spec…` identifiers come off the AST,
so comments and string literals are not references and nothing has to strip
them. Attribution is by declaration; file-local references through PRIVATE
consts are resolved to a fixed point so a mirror built from a spec-derived field
map keeps its dependency, while exported mirrors are not hopped through.
Two entries leave the derived list as false positives of exactly those two
defects — `base.zod.ts#BaseSchema` (its file's only `Spec…` token is a comment)
and `objectql.zod.ts#ObjectViewSchema` (a `BaseSchema.extend` of local literals
whose window swallowed a private `SpecKanbanConfigSchema` block ~50 lines past
its end). No corpus and no ledger changes.
Seven fixtures pin both directions: a comment-only mention yields nothing, a
code reference still fires.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
PR #6704 reworded this docstring to dodge a literal `Spec` + capital-letter
token and left a comment asking the next editor to remember, because the
`SPEC_DERIVED_PAIRS` re-check scanned raw text and misattributed the mention to
`ObjectTreeSchema`. The previous commit made that scanner parse, so the
convention held by discipline is no longer needed and the workaround is
retired: the docstring names `SpecGanttConfigSchema` in prose again, which is
what it wanted to say.
This doubles as the live positive proof for objectui#6705 — the pre-fix scanner
goes red on this file state, the fixed one stays green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
Empty frontmatter — the presence gate's own explicit exemption. The scanner
change is test-only and the one change under `packages/types/src/zod/` is a
docstring; no runtime, type or schema surface moves.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3160.2 KB3191.4 KB
Main entry chunk (gzip)142.6 KB350 KB
Entry fileindex-BOEfHVe7.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.33KB5.59KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.46KB117.29KB
core (index.js)5.55KB2.23KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.25KB61.73KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)70.02KB19.44KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)250.65KB63.91KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.78KB32.58KB
plugin-gantt (index.js)166.77KB40.76KB
plugin-grid (index.js)208.88KB56.59KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)29.34KB8.47KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

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

Projects

None yet

2 participants

@yinlianghui@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(types): read the SPEC_DERIVED_PAIRS re-check off the AST, not raw text - #7277

Merged
yinlianghui merged 4 commits into
mainfrom
claude/issue-6705-parity-scan-comments
Sep 2, 2026
Merged

fix(types): read the SPEC_DERIVED_PAIRS re-check off the AST, not raw text#7277
yinlianghui merged 4 commits into
mainfrom
claude/issue-6705-parity-scan-comments

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes#6705

What was wrong

The SPEC_DERIVED_PAIRS re-check decided which zod mirrors are spec-derived by slicing the source between export const boundaries and testing the slice against a \bSpec[A-Z] word pattern. Two independent defects, both of which one docstring fired:

  1. A comment counted as a reference. Raw text cannot tell SpecGanttConfigSchema.extend(...) from the same token in prose.
  2. The window was not the declaration. The slice ran from export const X to the nextexport, so everything in between — private consts and the next export's own docstring — was charged to X.

The fix

specReferencingExports(fileName, source) parses instead of scanning:

  • Spec… identifiers come off the AST, so comments and string literals are not references. Nothing strips anything, so there is no comment-stripping regex to get wrong on a string containing */ sequences — a fixture pins exactly that string.
  • Attribution is by declaration. File-local references through private consts are resolved to a fixed point, so a mirror built from a spec-derived field map keeps its dependency; exported mirrors are not hopped through, since each is a registered pair carrying its own entry.

Two entries leave the derived list — please read this part

This is the one thing in the PR that is not purely mechanical, and it is reported rather than absorbed. Neither mirror changed; the re-check stopped mis-reading them. Neither references a spec schema:

EntryWhy it was in the list
base.zod.ts#BaseSchemaIts file's onlySpec… token is in a comment: rather than calling `SpecSchema.omit({…}).partial()`. Prose.
objectql.zod.ts#ObjectViewSchemaBaseSchema.extend({…}) where every member is a local literal. Its declaration ends at line 212; the next export const is ~50 lines later, and the old window swallowed the private KanbanConfig = SpecKanbanConfigSchema… block that belongs to no export.

⚠️The ObjectViewSchema removal has a consequence this PR deliberately did not settle. This file's header records objectui#6058's remedy split, which routes ObjectViewSchema's unmirrored declared keys as SPEC-DERIVED — "the LOCAL declaration carries members the spec schema does not model, which is objectui#2231's unification question and NOT a local mirror edit". That routing rests on the false positive. Re-routing those keys is a remedy decision on the UnmirroredDeclared ledger, which this card was fenced out of, so the split is left standing as written, counts and all, with a ⚠️ note at the site telling whoever works it off to re-derive that side first. No ledger (KnownDrift / UnmirroredDeclared / RuntimeOnlyDeclared) and no corpus schema is touched.

Evidence — both directions, and the gate is not blinded

The card's fence is that a fix which stops seeing comments and stops seeing real references goes green on the old suite while destroying the gate.

Live reproduction (ablation 1). The pre-fix scanner, run against this branch's restored docstring, reproduces the reported failure exactly — and names the innocent neighbour:

AssertionError: a mirror gained or lost a spec dependency: expected [ …(11) ] to deeply equal [ …(10) ]
+ "objectql.zod.ts#ObjectTreeSchema"

Mutation proven on disk (old window code present = 1, new AST helper present = 0); restored by blob hash, 1a45bced… before and after.

Positive direction (ablation 2). Neutering the fixed helper to return an empty set reddens 5 tests — the live re-check plus every positive fixture:

AssertionError: ... expected [] to deeply equal [ …(8) ]
AssertionError: expected [] to deeply equal [ 'ObjectGanttSchema' ] (x3)
AssertionError: expected [] to include 'ObjectGanttSchema'
Tests 5 failed | 7 passed (12)

Mutation proven on disk (injected marker = 1, removed expression = 0); restored by blob hash with git diff HEAD empty. All eight surviving pairs are found by code reference alone.

Seven fixtures pin the shape in memory: comment-only mention yields nothing; code reference fires; a Spec… token inside a string literal carrying */ and // is not a reference; a private const between two exports charges its prose to neither neighbour; a private const with a real reference is charged to its user, not its neighbour; the next export's docstring is not charged to the previous export; plus a non-vacuity pin against the real objectql.zod.ts.

The docstring restoration

PR #6704 reworded a docstring to dodge the literal token and left a trap comment asking the next editor to remember. That convention is what a gate is supposed to replace, so with the scanner fixed the workaround is retired and the docstring says what it wanted to say. This is the only change under packages/types/src/zod/, it is comment-only, and it doubles as the live positive proof above.

Gates run — all verdict lines quoted, against final commit 85e014af2

GateVerdict
parity fileTest Files 1 passed (1) / Tests 12 passed (12)
packages/types suiteTest Files 84 passed (84) / Tests 1070 passed (1070)
@object-ui/typestype-checkexit 0 — tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json
check:control-bytes✅ check-control-bytes: OK (scanned 6005 tracked text file(s); skipped 85 binary).
check:vi-mock-specifiers✅ check-vi-mock-specifiers: OK (4129 tracked source file(s) …)
check:vi-mock-inherit✅ check-vi-mock-inherit: OK (…)
check:spec-symbols✅ spec symbol derivation: 1333 files scanned against 4959 spec export names; … 0 untriaged collisions
check:phantom-deps✅ Every in-scope import is declared by the package that publishes it.
changeset presence✅ 2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s) — empty frontmatter, "the explicit exemption and a complete answer to this gate"
check-changeset-fixed / -no-major / -overwriteall ✅

type-check is a real measurement, not a vacuous pass: tsc -p tsconfig.test.json --listFiles shows the edited test file (2 hits), objectql.zod.ts (1) and typescript/lib/typescript.d.ts (1) in the compiled set.

Declared narrowing — lint. Repo-wide turbo run lint is CI's run. Locally lint was narrowed to the affected package and the narrowing is measured, not assumed: ① the population came from eslint's own traversal under the root flat config, not a guess; ② --format json reports 148 files linted, 0 errors, 267 warnings, exit 0 — the package's own lint script (eslint .) verbatim; ③ eslint.config.js declares no projectService and no parserOptions.project, so linting is not type-aware and this diff cannot move the verdict of any file it does not contain.

Merge state.origin/main was merged at 1a1235c94 (picking up #7241, #7216, #7236, #7262), and every verdict above was taken after that merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

… text
`zod-mirror-parity.test.ts` decided which mirrors are spec-derived by slicing
the source between `export const` boundaries and testing the slice against a
`\bSpec[A-Z]` word pattern. That had two independent defects, and one docstring
fired both: a `Spec…` token in PROSE counted as a reference, and the slice ran
past its own declaration to the next `export`, so the red named the neighbouring
export instead of the edited one.
`specReferencingExports()` parses instead. `Spec…` identifiers come off the AST,
so comments and string literals are not references and nothing has to strip
them. Attribution is by declaration; file-local references through PRIVATE
consts are resolved to a fixed point so a mirror built from a spec-derived field
map keeps its dependency, while exported mirrors are not hopped through.
Two entries leave the derived list as false positives of exactly those two
defects — `base.zod.ts#BaseSchema` (its file's only `Spec…` token is a comment)
and `objectql.zod.ts#ObjectViewSchema` (a `BaseSchema.extend` of local literals
whose window swallowed a private `SpecKanbanConfigSchema` block ~50 lines past
its end). No corpus and no ledger changes.
Seven fixtures pin both directions: a comment-only mention yields nothing, a
code reference still fires.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
PR #6704 reworded this docstring to dodge a literal `Spec` + capital-letter
token and left a comment asking the next editor to remember, because the
`SPEC_DERIVED_PAIRS` re-check scanned raw text and misattributed the mention to
`ObjectTreeSchema`. The previous commit made that scanner parse, so the
convention held by discipline is no longer needed and the workaround is
retired: the docstring names `SpecGanttConfigSchema` in prose again, which is
what it wanted to say.
This doubles as the live positive proof for objectui#6705 — the pre-fix scanner
goes red on this file state, the fixed one stays green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
Empty frontmatter — the presence gate's own explicit exemption. The scanner
change is test-only and the one change under `packages/types/src/zod/` is a
docstring; no runtime, type or schema surface moves.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3160.2 KB3191.4 KB
Main entry chunk (gzip)142.6 KB350 KB
Entry fileindex-BOEfHVe7.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.33KB5.59KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.46KB117.29KB
core (index.js)5.55KB2.23KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.25KB61.73KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)70.02KB19.44KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)250.65KB63.91KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.78KB32.58KB
plugin-gantt (index.js)166.77KB40.76KB
plugin-grid (index.js)208.88KB56.59KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)29.34KB8.47KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

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

Projects

None yet

2 participants

@yinlianghui@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(types): read the SPEC_DERIVED_PAIRS re-check off the AST, not raw text - #7277

Merged
yinlianghui merged 4 commits into
mainfrom
claude/issue-6705-parity-scan-comments
Sep 2, 2026
Merged

fix(types): read the SPEC_DERIVED_PAIRS re-check off the AST, not raw text#7277
yinlianghui merged 4 commits into
mainfrom
claude/issue-6705-parity-scan-comments

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes#6705

What was wrong

The SPEC_DERIVED_PAIRS re-check decided which zod mirrors are spec-derived by slicing the source between export const boundaries and testing the slice against a \bSpec[A-Z] word pattern. Two independent defects, both of which one docstring fired:

  1. A comment counted as a reference. Raw text cannot tell SpecGanttConfigSchema.extend(...) from the same token in prose.
  2. The window was not the declaration. The slice ran from export const X to the nextexport, so everything in between — private consts and the next export's own docstring — was charged to X.

The fix

specReferencingExports(fileName, source) parses instead of scanning:

  • Spec… identifiers come off the AST, so comments and string literals are not references. Nothing strips anything, so there is no comment-stripping regex to get wrong on a string containing */ sequences — a fixture pins exactly that string.
  • Attribution is by declaration. File-local references through private consts are resolved to a fixed point, so a mirror built from a spec-derived field map keeps its dependency; exported mirrors are not hopped through, since each is a registered pair carrying its own entry.

Two entries leave the derived list — please read this part

This is the one thing in the PR that is not purely mechanical, and it is reported rather than absorbed. Neither mirror changed; the re-check stopped mis-reading them. Neither references a spec schema:

EntryWhy it was in the list
base.zod.ts#BaseSchemaIts file's onlySpec… token is in a comment: rather than calling `SpecSchema.omit({…}).partial()`. Prose.
objectql.zod.ts#ObjectViewSchemaBaseSchema.extend({…}) where every member is a local literal. Its declaration ends at line 212; the next export const is ~50 lines later, and the old window swallowed the private KanbanConfig = SpecKanbanConfigSchema… block that belongs to no export.

⚠️The ObjectViewSchema removal has a consequence this PR deliberately did not settle. This file's header records objectui#6058's remedy split, which routes ObjectViewSchema's unmirrored declared keys as SPEC-DERIVED — "the LOCAL declaration carries members the spec schema does not model, which is objectui#2231's unification question and NOT a local mirror edit". That routing rests on the false positive. Re-routing those keys is a remedy decision on the UnmirroredDeclared ledger, which this card was fenced out of, so the split is left standing as written, counts and all, with a ⚠️ note at the site telling whoever works it off to re-derive that side first. No ledger (KnownDrift / UnmirroredDeclared / RuntimeOnlyDeclared) and no corpus schema is touched.

Evidence — both directions, and the gate is not blinded

The card's fence is that a fix which stops seeing comments and stops seeing real references goes green on the old suite while destroying the gate.

Live reproduction (ablation 1). The pre-fix scanner, run against this branch's restored docstring, reproduces the reported failure exactly — and names the innocent neighbour:

AssertionError: a mirror gained or lost a spec dependency: expected [ …(11) ] to deeply equal [ …(10) ]
+ "objectql.zod.ts#ObjectTreeSchema"

Mutation proven on disk (old window code present = 1, new AST helper present = 0); restored by blob hash, 1a45bced… before and after.

Positive direction (ablation 2). Neutering the fixed helper to return an empty set reddens 5 tests — the live re-check plus every positive fixture:

AssertionError: ... expected [] to deeply equal [ …(8) ]
AssertionError: expected [] to deeply equal [ 'ObjectGanttSchema' ] (x3)
AssertionError: expected [] to include 'ObjectGanttSchema'
Tests 5 failed | 7 passed (12)

Mutation proven on disk (injected marker = 1, removed expression = 0); restored by blob hash with git diff HEAD empty. All eight surviving pairs are found by code reference alone.

Seven fixtures pin the shape in memory: comment-only mention yields nothing; code reference fires; a Spec… token inside a string literal carrying */ and // is not a reference; a private const between two exports charges its prose to neither neighbour; a private const with a real reference is charged to its user, not its neighbour; the next export's docstring is not charged to the previous export; plus a non-vacuity pin against the real objectql.zod.ts.

The docstring restoration

PR #6704 reworded a docstring to dodge the literal token and left a trap comment asking the next editor to remember. That convention is what a gate is supposed to replace, so with the scanner fixed the workaround is retired and the docstring says what it wanted to say. This is the only change under packages/types/src/zod/, it is comment-only, and it doubles as the live positive proof above.

Gates run — all verdict lines quoted, against final commit 85e014af2

GateVerdict
parity fileTest Files 1 passed (1) / Tests 12 passed (12)
packages/types suiteTest Files 84 passed (84) / Tests 1070 passed (1070)
@object-ui/typestype-checkexit 0 — tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json
check:control-bytes✅ check-control-bytes: OK (scanned 6005 tracked text file(s); skipped 85 binary).
check:vi-mock-specifiers✅ check-vi-mock-specifiers: OK (4129 tracked source file(s) …)
check:vi-mock-inherit✅ check-vi-mock-inherit: OK (…)
check:spec-symbols✅ spec symbol derivation: 1333 files scanned against 4959 spec export names; … 0 untriaged collisions
check:phantom-deps✅ Every in-scope import is declared by the package that publishes it.
changeset presence✅ 2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s) — empty frontmatter, "the explicit exemption and a complete answer to this gate"
check-changeset-fixed / -no-major / -overwriteall ✅

type-check is a real measurement, not a vacuous pass: tsc -p tsconfig.test.json --listFiles shows the edited test file (2 hits), objectql.zod.ts (1) and typescript/lib/typescript.d.ts (1) in the compiled set.

Declared narrowing — lint. Repo-wide turbo run lint is CI's run. Locally lint was narrowed to the affected package and the narrowing is measured, not assumed: ① the population came from eslint's own traversal under the root flat config, not a guess; ② --format json reports 148 files linted, 0 errors, 267 warnings, exit 0 — the package's own lint script (eslint .) verbatim; ③ eslint.config.js declares no projectService and no parserOptions.project, so linting is not type-aware and this diff cannot move the verdict of any file it does not contain.

Merge state.origin/main was merged at 1a1235c94 (picking up #7241, #7216, #7236, #7262), and every verdict above was taken after that merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

… text
`zod-mirror-parity.test.ts` decided which mirrors are spec-derived by slicing
the source between `export const` boundaries and testing the slice against a
`\bSpec[A-Z]` word pattern. That had two independent defects, and one docstring
fired both: a `Spec…` token in PROSE counted as a reference, and the slice ran
past its own declaration to the next `export`, so the red named the neighbouring
export instead of the edited one.
`specReferencingExports()` parses instead. `Spec…` identifiers come off the AST,
so comments and string literals are not references and nothing has to strip
them. Attribution is by declaration; file-local references through PRIVATE
consts are resolved to a fixed point so a mirror built from a spec-derived field
map keeps its dependency, while exported mirrors are not hopped through.
Two entries leave the derived list as false positives of exactly those two
defects — `base.zod.ts#BaseSchema` (its file's only `Spec…` token is a comment)
and `objectql.zod.ts#ObjectViewSchema` (a `BaseSchema.extend` of local literals
whose window swallowed a private `SpecKanbanConfigSchema` block ~50 lines past
its end). No corpus and no ledger changes.
Seven fixtures pin both directions: a comment-only mention yields nothing, a
code reference still fires.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
PR #6704 reworded this docstring to dodge a literal `Spec` + capital-letter
token and left a comment asking the next editor to remember, because the
`SPEC_DERIVED_PAIRS` re-check scanned raw text and misattributed the mention to
`ObjectTreeSchema`. The previous commit made that scanner parse, so the
convention held by discipline is no longer needed and the workaround is
retired: the docstring names `SpecGanttConfigSchema` in prose again, which is
what it wanted to say.
This doubles as the live positive proof for objectui#6705 — the pre-fix scanner
goes red on this file state, the fixed one stays green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
Empty frontmatter — the presence gate's own explicit exemption. The scanner
change is test-only and the one change under `packages/types/src/zod/` is a
docstring; no runtime, type or schema surface moves.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3160.2 KB3191.4 KB
Main entry chunk (gzip)142.6 KB350 KB
Entry fileindex-BOEfHVe7.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.33KB5.59KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.46KB117.29KB
core (index.js)5.55KB2.23KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.25KB61.73KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)70.02KB19.44KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)250.65KB63.91KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.78KB32.58KB
plugin-gantt (index.js)166.77KB40.76KB
plugin-grid (index.js)208.88KB56.59KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)29.34KB8.47KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

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

Projects

None yet

2 participants

@yinlianghui@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(types): read the SPEC_DERIVED_PAIRS re-check off the AST, not raw text - #7277

Merged
yinlianghui merged 4 commits into
mainfrom
claude/issue-6705-parity-scan-comments
Sep 2, 2026
Merged

fix(types): read the SPEC_DERIVED_PAIRS re-check off the AST, not raw text#7277
yinlianghui merged 4 commits into
mainfrom
claude/issue-6705-parity-scan-comments

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes#6705

What was wrong

The SPEC_DERIVED_PAIRS re-check decided which zod mirrors are spec-derived by slicing the source between export const boundaries and testing the slice against a \bSpec[A-Z] word pattern. Two independent defects, both of which one docstring fired:

  1. A comment counted as a reference. Raw text cannot tell SpecGanttConfigSchema.extend(...) from the same token in prose.
  2. The window was not the declaration. The slice ran from export const X to the nextexport, so everything in between — private consts and the next export's own docstring — was charged to X.

The fix

specReferencingExports(fileName, source) parses instead of scanning:

  • Spec… identifiers come off the AST, so comments and string literals are not references. Nothing strips anything, so there is no comment-stripping regex to get wrong on a string containing */ sequences — a fixture pins exactly that string.
  • Attribution is by declaration. File-local references through private consts are resolved to a fixed point, so a mirror built from a spec-derived field map keeps its dependency; exported mirrors are not hopped through, since each is a registered pair carrying its own entry.

Two entries leave the derived list — please read this part

This is the one thing in the PR that is not purely mechanical, and it is reported rather than absorbed. Neither mirror changed; the re-check stopped mis-reading them. Neither references a spec schema:

EntryWhy it was in the list
base.zod.ts#BaseSchemaIts file's onlySpec… token is in a comment: rather than calling `SpecSchema.omit({…}).partial()`. Prose.
objectql.zod.ts#ObjectViewSchemaBaseSchema.extend({…}) where every member is a local literal. Its declaration ends at line 212; the next export const is ~50 lines later, and the old window swallowed the private KanbanConfig = SpecKanbanConfigSchema… block that belongs to no export.

⚠️The ObjectViewSchema removal has a consequence this PR deliberately did not settle. This file's header records objectui#6058's remedy split, which routes ObjectViewSchema's unmirrored declared keys as SPEC-DERIVED — "the LOCAL declaration carries members the spec schema does not model, which is objectui#2231's unification question and NOT a local mirror edit". That routing rests on the false positive. Re-routing those keys is a remedy decision on the UnmirroredDeclared ledger, which this card was fenced out of, so the split is left standing as written, counts and all, with a ⚠️ note at the site telling whoever works it off to re-derive that side first. No ledger (KnownDrift / UnmirroredDeclared / RuntimeOnlyDeclared) and no corpus schema is touched.

Evidence — both directions, and the gate is not blinded

The card's fence is that a fix which stops seeing comments and stops seeing real references goes green on the old suite while destroying the gate.

Live reproduction (ablation 1). The pre-fix scanner, run against this branch's restored docstring, reproduces the reported failure exactly — and names the innocent neighbour:

AssertionError: a mirror gained or lost a spec dependency: expected [ …(11) ] to deeply equal [ …(10) ]
+ "objectql.zod.ts#ObjectTreeSchema"

Mutation proven on disk (old window code present = 1, new AST helper present = 0); restored by blob hash, 1a45bced… before and after.

Positive direction (ablation 2). Neutering the fixed helper to return an empty set reddens 5 tests — the live re-check plus every positive fixture:

AssertionError: ... expected [] to deeply equal [ …(8) ]
AssertionError: expected [] to deeply equal [ 'ObjectGanttSchema' ] (x3)
AssertionError: expected [] to include 'ObjectGanttSchema'
Tests 5 failed | 7 passed (12)

Mutation proven on disk (injected marker = 1, removed expression = 0); restored by blob hash with git diff HEAD empty. All eight surviving pairs are found by code reference alone.

Seven fixtures pin the shape in memory: comment-only mention yields nothing; code reference fires; a Spec… token inside a string literal carrying */ and // is not a reference; a private const between two exports charges its prose to neither neighbour; a private const with a real reference is charged to its user, not its neighbour; the next export's docstring is not charged to the previous export; plus a non-vacuity pin against the real objectql.zod.ts.

The docstring restoration

PR #6704 reworded a docstring to dodge the literal token and left a trap comment asking the next editor to remember. That convention is what a gate is supposed to replace, so with the scanner fixed the workaround is retired and the docstring says what it wanted to say. This is the only change under packages/types/src/zod/, it is comment-only, and it doubles as the live positive proof above.

Gates run — all verdict lines quoted, against final commit 85e014af2

GateVerdict
parity fileTest Files 1 passed (1) / Tests 12 passed (12)
packages/types suiteTest Files 84 passed (84) / Tests 1070 passed (1070)
@object-ui/typestype-checkexit 0 — tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json
check:control-bytes✅ check-control-bytes: OK (scanned 6005 tracked text file(s); skipped 85 binary).
check:vi-mock-specifiers✅ check-vi-mock-specifiers: OK (4129 tracked source file(s) …)
check:vi-mock-inherit✅ check-vi-mock-inherit: OK (…)
check:spec-symbols✅ spec symbol derivation: 1333 files scanned against 4959 spec export names; … 0 untriaged collisions
check:phantom-deps✅ Every in-scope import is declared by the package that publishes it.
changeset presence✅ 2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s) — empty frontmatter, "the explicit exemption and a complete answer to this gate"
check-changeset-fixed / -no-major / -overwriteall ✅

type-check is a real measurement, not a vacuous pass: tsc -p tsconfig.test.json --listFiles shows the edited test file (2 hits), objectql.zod.ts (1) and typescript/lib/typescript.d.ts (1) in the compiled set.

Declared narrowing — lint. Repo-wide turbo run lint is CI's run. Locally lint was narrowed to the affected package and the narrowing is measured, not assumed: ① the population came from eslint's own traversal under the root flat config, not a guess; ② --format json reports 148 files linted, 0 errors, 267 warnings, exit 0 — the package's own lint script (eslint .) verbatim; ③ eslint.config.js declares no projectService and no parserOptions.project, so linting is not type-aware and this diff cannot move the verdict of any file it does not contain.

Merge state.origin/main was merged at 1a1235c94 (picking up #7241, #7216, #7236, #7262), and every verdict above was taken after that merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

… text
`zod-mirror-parity.test.ts` decided which mirrors are spec-derived by slicing
the source between `export const` boundaries and testing the slice against a
`\bSpec[A-Z]` word pattern. That had two independent defects, and one docstring
fired both: a `Spec…` token in PROSE counted as a reference, and the slice ran
past its own declaration to the next `export`, so the red named the neighbouring
export instead of the edited one.
`specReferencingExports()` parses instead. `Spec…` identifiers come off the AST,
so comments and string literals are not references and nothing has to strip
them. Attribution is by declaration; file-local references through PRIVATE
consts are resolved to a fixed point so a mirror built from a spec-derived field
map keeps its dependency, while exported mirrors are not hopped through.
Two entries leave the derived list as false positives of exactly those two
defects — `base.zod.ts#BaseSchema` (its file's only `Spec…` token is a comment)
and `objectql.zod.ts#ObjectViewSchema` (a `BaseSchema.extend` of local literals
whose window swallowed a private `SpecKanbanConfigSchema` block ~50 lines past
its end). No corpus and no ledger changes.
Seven fixtures pin both directions: a comment-only mention yields nothing, a
code reference still fires.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
PR #6704 reworded this docstring to dodge a literal `Spec` + capital-letter
token and left a comment asking the next editor to remember, because the
`SPEC_DERIVED_PAIRS` re-check scanned raw text and misattributed the mention to
`ObjectTreeSchema`. The previous commit made that scanner parse, so the
convention held by discipline is no longer needed and the workaround is
retired: the docstring names `SpecGanttConfigSchema` in prose again, which is
what it wanted to say.
This doubles as the live positive proof for objectui#6705 — the pre-fix scanner
goes red on this file state, the fixed one stays green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
Empty frontmatter — the presence gate's own explicit exemption. The scanner
change is test-only and the one change under `packages/types/src/zod/` is a
docstring; no runtime, type or schema surface moves.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3160.2 KB3191.4 KB
Main entry chunk (gzip)142.6 KB350 KB
Entry fileindex-BOEfHVe7.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.33KB5.59KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.46KB117.29KB
core (index.js)5.55KB2.23KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.25KB61.73KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)70.02KB19.44KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)250.65KB63.91KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.78KB32.58KB
plugin-gantt (index.js)166.77KB40.76KB
plugin-grid (index.js)208.88KB56.59KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)29.34KB8.47KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

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

Projects

None yet

2 participants

@yinlianghui@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(types): read the SPEC_DERIVED_PAIRS re-check off the AST, not raw text - #7277

Merged
yinlianghui merged 4 commits into
mainfrom
claude/issue-6705-parity-scan-comments
Sep 2, 2026
Merged

fix(types): read the SPEC_DERIVED_PAIRS re-check off the AST, not raw text#7277
yinlianghui merged 4 commits into
mainfrom
claude/issue-6705-parity-scan-comments

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes#6705

What was wrong

The SPEC_DERIVED_PAIRS re-check decided which zod mirrors are spec-derived by slicing the source between export const boundaries and testing the slice against a \bSpec[A-Z] word pattern. Two independent defects, both of which one docstring fired:

  1. A comment counted as a reference. Raw text cannot tell SpecGanttConfigSchema.extend(...) from the same token in prose.
  2. The window was not the declaration. The slice ran from export const X to the nextexport, so everything in between — private consts and the next export's own docstring — was charged to X.

The fix

specReferencingExports(fileName, source) parses instead of scanning:

  • Spec… identifiers come off the AST, so comments and string literals are not references. Nothing strips anything, so there is no comment-stripping regex to get wrong on a string containing */ sequences — a fixture pins exactly that string.
  • Attribution is by declaration. File-local references through private consts are resolved to a fixed point, so a mirror built from a spec-derived field map keeps its dependency; exported mirrors are not hopped through, since each is a registered pair carrying its own entry.

Two entries leave the derived list — please read this part

This is the one thing in the PR that is not purely mechanical, and it is reported rather than absorbed. Neither mirror changed; the re-check stopped mis-reading them. Neither references a spec schema:

EntryWhy it was in the list
base.zod.ts#BaseSchemaIts file's onlySpec… token is in a comment: rather than calling `SpecSchema.omit({…}).partial()`. Prose.
objectql.zod.ts#ObjectViewSchemaBaseSchema.extend({…}) where every member is a local literal. Its declaration ends at line 212; the next export const is ~50 lines later, and the old window swallowed the private KanbanConfig = SpecKanbanConfigSchema… block that belongs to no export.

⚠️The ObjectViewSchema removal has a consequence this PR deliberately did not settle. This file's header records objectui#6058's remedy split, which routes ObjectViewSchema's unmirrored declared keys as SPEC-DERIVED — "the LOCAL declaration carries members the spec schema does not model, which is objectui#2231's unification question and NOT a local mirror edit". That routing rests on the false positive. Re-routing those keys is a remedy decision on the UnmirroredDeclared ledger, which this card was fenced out of, so the split is left standing as written, counts and all, with a ⚠️ note at the site telling whoever works it off to re-derive that side first. No ledger (KnownDrift / UnmirroredDeclared / RuntimeOnlyDeclared) and no corpus schema is touched.

Evidence — both directions, and the gate is not blinded

The card's fence is that a fix which stops seeing comments and stops seeing real references goes green on the old suite while destroying the gate.

Live reproduction (ablation 1). The pre-fix scanner, run against this branch's restored docstring, reproduces the reported failure exactly — and names the innocent neighbour:

AssertionError: a mirror gained or lost a spec dependency: expected [ …(11) ] to deeply equal [ …(10) ]
+ "objectql.zod.ts#ObjectTreeSchema"

Mutation proven on disk (old window code present = 1, new AST helper present = 0); restored by blob hash, 1a45bced… before and after.

Positive direction (ablation 2). Neutering the fixed helper to return an empty set reddens 5 tests — the live re-check plus every positive fixture:

AssertionError: ... expected [] to deeply equal [ …(8) ]
AssertionError: expected [] to deeply equal [ 'ObjectGanttSchema' ] (x3)
AssertionError: expected [] to include 'ObjectGanttSchema'
Tests 5 failed | 7 passed (12)

Mutation proven on disk (injected marker = 1, removed expression = 0); restored by blob hash with git diff HEAD empty. All eight surviving pairs are found by code reference alone.

Seven fixtures pin the shape in memory: comment-only mention yields nothing; code reference fires; a Spec… token inside a string literal carrying */ and // is not a reference; a private const between two exports charges its prose to neither neighbour; a private const with a real reference is charged to its user, not its neighbour; the next export's docstring is not charged to the previous export; plus a non-vacuity pin against the real objectql.zod.ts.

The docstring restoration

PR #6704 reworded a docstring to dodge the literal token and left a trap comment asking the next editor to remember. That convention is what a gate is supposed to replace, so with the scanner fixed the workaround is retired and the docstring says what it wanted to say. This is the only change under packages/types/src/zod/, it is comment-only, and it doubles as the live positive proof above.

Gates run — all verdict lines quoted, against final commit 85e014af2

GateVerdict
parity fileTest Files 1 passed (1) / Tests 12 passed (12)
packages/types suiteTest Files 84 passed (84) / Tests 1070 passed (1070)
@object-ui/typestype-checkexit 0 — tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json
check:control-bytes✅ check-control-bytes: OK (scanned 6005 tracked text file(s); skipped 85 binary).
check:vi-mock-specifiers✅ check-vi-mock-specifiers: OK (4129 tracked source file(s) …)
check:vi-mock-inherit✅ check-vi-mock-inherit: OK (…)
check:spec-symbols✅ spec symbol derivation: 1333 files scanned against 4959 spec export names; … 0 untriaged collisions
check:phantom-deps✅ Every in-scope import is declared by the package that publishes it.
changeset presence✅ 2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s) — empty frontmatter, "the explicit exemption and a complete answer to this gate"
check-changeset-fixed / -no-major / -overwriteall ✅

type-check is a real measurement, not a vacuous pass: tsc -p tsconfig.test.json --listFiles shows the edited test file (2 hits), objectql.zod.ts (1) and typescript/lib/typescript.d.ts (1) in the compiled set.

Declared narrowing — lint. Repo-wide turbo run lint is CI's run. Locally lint was narrowed to the affected package and the narrowing is measured, not assumed: ① the population came from eslint's own traversal under the root flat config, not a guess; ② --format json reports 148 files linted, 0 errors, 267 warnings, exit 0 — the package's own lint script (eslint .) verbatim; ③ eslint.config.js declares no projectService and no parserOptions.project, so linting is not type-aware and this diff cannot move the verdict of any file it does not contain.

Merge state.origin/main was merged at 1a1235c94 (picking up #7241, #7216, #7236, #7262), and every verdict above was taken after that merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

… text
`zod-mirror-parity.test.ts` decided which mirrors are spec-derived by slicing
the source between `export const` boundaries and testing the slice against a
`\bSpec[A-Z]` word pattern. That had two independent defects, and one docstring
fired both: a `Spec…` token in PROSE counted as a reference, and the slice ran
past its own declaration to the next `export`, so the red named the neighbouring
export instead of the edited one.
`specReferencingExports()` parses instead. `Spec…` identifiers come off the AST,
so comments and string literals are not references and nothing has to strip
them. Attribution is by declaration; file-local references through PRIVATE
consts are resolved to a fixed point so a mirror built from a spec-derived field
map keeps its dependency, while exported mirrors are not hopped through.
Two entries leave the derived list as false positives of exactly those two
defects — `base.zod.ts#BaseSchema` (its file's only `Spec…` token is a comment)
and `objectql.zod.ts#ObjectViewSchema` (a `BaseSchema.extend` of local literals
whose window swallowed a private `SpecKanbanConfigSchema` block ~50 lines past
its end). No corpus and no ledger changes.
Seven fixtures pin both directions: a comment-only mention yields nothing, a
code reference still fires.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
PR #6704 reworded this docstring to dodge a literal `Spec` + capital-letter
token and left a comment asking the next editor to remember, because the
`SPEC_DERIVED_PAIRS` re-check scanned raw text and misattributed the mention to
`ObjectTreeSchema`. The previous commit made that scanner parse, so the
convention held by discipline is no longer needed and the workaround is
retired: the docstring names `SpecGanttConfigSchema` in prose again, which is
what it wanted to say.
This doubles as the live positive proof for objectui#6705 — the pre-fix scanner
goes red on this file state, the fixed one stays green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
Empty frontmatter — the presence gate's own explicit exemption. The scanner
change is test-only and the one change under `packages/types/src/zod/` is a
docstring; no runtime, type or schema surface moves.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3160.2 KB3191.4 KB
Main entry chunk (gzip)142.6 KB350 KB
Entry fileindex-BOEfHVe7.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.33KB5.59KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.46KB117.29KB
core (index.js)5.55KB2.23KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.25KB61.73KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)70.02KB19.44KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)250.65KB63.91KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.78KB32.58KB
plugin-gantt (index.js)166.77KB40.76KB
plugin-grid (index.js)208.88KB56.59KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)29.34KB8.47KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

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

Projects

None yet

2 participants

@yinlianghui@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(types): read the SPEC_DERIVED_PAIRS re-check off the AST, not raw text - #7277

Merged
yinlianghui merged 4 commits into
mainfrom
claude/issue-6705-parity-scan-comments
Sep 2, 2026
Merged

fix(types): read the SPEC_DERIVED_PAIRS re-check off the AST, not raw text#7277
yinlianghui merged 4 commits into
mainfrom
claude/issue-6705-parity-scan-comments

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes#6705

What was wrong

The SPEC_DERIVED_PAIRS re-check decided which zod mirrors are spec-derived by slicing the source between export const boundaries and testing the slice against a \bSpec[A-Z] word pattern. Two independent defects, both of which one docstring fired:

  1. A comment counted as a reference. Raw text cannot tell SpecGanttConfigSchema.extend(...) from the same token in prose.
  2. The window was not the declaration. The slice ran from export const X to the nextexport, so everything in between — private consts and the next export's own docstring — was charged to X.

The fix

specReferencingExports(fileName, source) parses instead of scanning:

  • Spec… identifiers come off the AST, so comments and string literals are not references. Nothing strips anything, so there is no comment-stripping regex to get wrong on a string containing */ sequences — a fixture pins exactly that string.
  • Attribution is by declaration. File-local references through private consts are resolved to a fixed point, so a mirror built from a spec-derived field map keeps its dependency; exported mirrors are not hopped through, since each is a registered pair carrying its own entry.

Two entries leave the derived list — please read this part

This is the one thing in the PR that is not purely mechanical, and it is reported rather than absorbed. Neither mirror changed; the re-check stopped mis-reading them. Neither references a spec schema:

EntryWhy it was in the list
base.zod.ts#BaseSchemaIts file's onlySpec… token is in a comment: rather than calling `SpecSchema.omit({…}).partial()`. Prose.
objectql.zod.ts#ObjectViewSchemaBaseSchema.extend({…}) where every member is a local literal. Its declaration ends at line 212; the next export const is ~50 lines later, and the old window swallowed the private KanbanConfig = SpecKanbanConfigSchema… block that belongs to no export.

⚠️The ObjectViewSchema removal has a consequence this PR deliberately did not settle. This file's header records objectui#6058's remedy split, which routes ObjectViewSchema's unmirrored declared keys as SPEC-DERIVED — "the LOCAL declaration carries members the spec schema does not model, which is objectui#2231's unification question and NOT a local mirror edit". That routing rests on the false positive. Re-routing those keys is a remedy decision on the UnmirroredDeclared ledger, which this card was fenced out of, so the split is left standing as written, counts and all, with a ⚠️ note at the site telling whoever works it off to re-derive that side first. No ledger (KnownDrift / UnmirroredDeclared / RuntimeOnlyDeclared) and no corpus schema is touched.

Evidence — both directions, and the gate is not blinded

The card's fence is that a fix which stops seeing comments and stops seeing real references goes green on the old suite while destroying the gate.

Live reproduction (ablation 1). The pre-fix scanner, run against this branch's restored docstring, reproduces the reported failure exactly — and names the innocent neighbour:

AssertionError: a mirror gained or lost a spec dependency: expected [ …(11) ] to deeply equal [ …(10) ]
+ "objectql.zod.ts#ObjectTreeSchema"

Mutation proven on disk (old window code present = 1, new AST helper present = 0); restored by blob hash, 1a45bced… before and after.

Positive direction (ablation 2). Neutering the fixed helper to return an empty set reddens 5 tests — the live re-check plus every positive fixture:

AssertionError: ... expected [] to deeply equal [ …(8) ]
AssertionError: expected [] to deeply equal [ 'ObjectGanttSchema' ] (x3)
AssertionError: expected [] to include 'ObjectGanttSchema'
Tests 5 failed | 7 passed (12)

Mutation proven on disk (injected marker = 1, removed expression = 0); restored by blob hash with git diff HEAD empty. All eight surviving pairs are found by code reference alone.

Seven fixtures pin the shape in memory: comment-only mention yields nothing; code reference fires; a Spec… token inside a string literal carrying */ and // is not a reference; a private const between two exports charges its prose to neither neighbour; a private const with a real reference is charged to its user, not its neighbour; the next export's docstring is not charged to the previous export; plus a non-vacuity pin against the real objectql.zod.ts.

The docstring restoration

PR #6704 reworded a docstring to dodge the literal token and left a trap comment asking the next editor to remember. That convention is what a gate is supposed to replace, so with the scanner fixed the workaround is retired and the docstring says what it wanted to say. This is the only change under packages/types/src/zod/, it is comment-only, and it doubles as the live positive proof above.

Gates run — all verdict lines quoted, against final commit 85e014af2

GateVerdict
parity fileTest Files 1 passed (1) / Tests 12 passed (12)
packages/types suiteTest Files 84 passed (84) / Tests 1070 passed (1070)
@object-ui/typestype-checkexit 0 — tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json
check:control-bytes✅ check-control-bytes: OK (scanned 6005 tracked text file(s); skipped 85 binary).
check:vi-mock-specifiers✅ check-vi-mock-specifiers: OK (4129 tracked source file(s) …)
check:vi-mock-inherit✅ check-vi-mock-inherit: OK (…)
check:spec-symbols✅ spec symbol derivation: 1333 files scanned against 4959 spec export names; … 0 untriaged collisions
check:phantom-deps✅ Every in-scope import is declared by the package that publishes it.
changeset presence✅ 2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s) — empty frontmatter, "the explicit exemption and a complete answer to this gate"
check-changeset-fixed / -no-major / -overwriteall ✅

type-check is a real measurement, not a vacuous pass: tsc -p tsconfig.test.json --listFiles shows the edited test file (2 hits), objectql.zod.ts (1) and typescript/lib/typescript.d.ts (1) in the compiled set.

Declared narrowing — lint. Repo-wide turbo run lint is CI's run. Locally lint was narrowed to the affected package and the narrowing is measured, not assumed: ① the population came from eslint's own traversal under the root flat config, not a guess; ② --format json reports 148 files linted, 0 errors, 267 warnings, exit 0 — the package's own lint script (eslint .) verbatim; ③ eslint.config.js declares no projectService and no parserOptions.project, so linting is not type-aware and this diff cannot move the verdict of any file it does not contain.

Merge state.origin/main was merged at 1a1235c94 (picking up #7241, #7216, #7236, #7262), and every verdict above was taken after that merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

… text
`zod-mirror-parity.test.ts` decided which mirrors are spec-derived by slicing
the source between `export const` boundaries and testing the slice against a
`\bSpec[A-Z]` word pattern. That had two independent defects, and one docstring
fired both: a `Spec…` token in PROSE counted as a reference, and the slice ran
past its own declaration to the next `export`, so the red named the neighbouring
export instead of the edited one.
`specReferencingExports()` parses instead. `Spec…` identifiers come off the AST,
so comments and string literals are not references and nothing has to strip
them. Attribution is by declaration; file-local references through PRIVATE
consts are resolved to a fixed point so a mirror built from a spec-derived field
map keeps its dependency, while exported mirrors are not hopped through.
Two entries leave the derived list as false positives of exactly those two
defects — `base.zod.ts#BaseSchema` (its file's only `Spec…` token is a comment)
and `objectql.zod.ts#ObjectViewSchema` (a `BaseSchema.extend` of local literals
whose window swallowed a private `SpecKanbanConfigSchema` block ~50 lines past
its end). No corpus and no ledger changes.
Seven fixtures pin both directions: a comment-only mention yields nothing, a
code reference still fires.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
PR #6704 reworded this docstring to dodge a literal `Spec` + capital-letter
token and left a comment asking the next editor to remember, because the
`SPEC_DERIVED_PAIRS` re-check scanned raw text and misattributed the mention to
`ObjectTreeSchema`. The previous commit made that scanner parse, so the
convention held by discipline is no longer needed and the workaround is
retired: the docstring names `SpecGanttConfigSchema` in prose again, which is
what it wanted to say.
This doubles as the live positive proof for objectui#6705 — the pre-fix scanner
goes red on this file state, the fixed one stays green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
Empty frontmatter — the presence gate's own explicit exemption. The scanner
change is test-only and the one change under `packages/types/src/zod/` is a
docstring; no runtime, type or schema surface moves.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3160.2 KB3191.4 KB
Main entry chunk (gzip)142.6 KB350 KB
Entry fileindex-BOEfHVe7.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.33KB5.59KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.46KB117.29KB
core (index.js)5.55KB2.23KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.25KB61.73KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)70.02KB19.44KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)250.65KB63.91KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.78KB32.58KB
plugin-gantt (index.js)166.77KB40.76KB
plugin-grid (index.js)208.88KB56.59KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)29.34KB8.47KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

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

Projects

None yet

2 participants

@yinlianghui@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(types): read the SPEC_DERIVED_PAIRS re-check off the AST, not raw text - #7277

Merged
yinlianghui merged 4 commits into
mainfrom
claude/issue-6705-parity-scan-comments
Sep 2, 2026
Merged

fix(types): read the SPEC_DERIVED_PAIRS re-check off the AST, not raw text#7277
yinlianghui merged 4 commits into
mainfrom
claude/issue-6705-parity-scan-comments

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes#6705

What was wrong

The SPEC_DERIVED_PAIRS re-check decided which zod mirrors are spec-derived by slicing the source between export const boundaries and testing the slice against a \bSpec[A-Z] word pattern. Two independent defects, both of which one docstring fired:

  1. A comment counted as a reference. Raw text cannot tell SpecGanttConfigSchema.extend(...) from the same token in prose.
  2. The window was not the declaration. The slice ran from export const X to the nextexport, so everything in between — private consts and the next export's own docstring — was charged to X.

The fix

specReferencingExports(fileName, source) parses instead of scanning:

  • Spec… identifiers come off the AST, so comments and string literals are not references. Nothing strips anything, so there is no comment-stripping regex to get wrong on a string containing */ sequences — a fixture pins exactly that string.
  • Attribution is by declaration. File-local references through private consts are resolved to a fixed point, so a mirror built from a spec-derived field map keeps its dependency; exported mirrors are not hopped through, since each is a registered pair carrying its own entry.

Two entries leave the derived list — please read this part

This is the one thing in the PR that is not purely mechanical, and it is reported rather than absorbed. Neither mirror changed; the re-check stopped mis-reading them. Neither references a spec schema:

EntryWhy it was in the list
base.zod.ts#BaseSchemaIts file's onlySpec… token is in a comment: rather than calling `SpecSchema.omit({…}).partial()`. Prose.
objectql.zod.ts#ObjectViewSchemaBaseSchema.extend({…}) where every member is a local literal. Its declaration ends at line 212; the next export const is ~50 lines later, and the old window swallowed the private KanbanConfig = SpecKanbanConfigSchema… block that belongs to no export.

⚠️The ObjectViewSchema removal has a consequence this PR deliberately did not settle. This file's header records objectui#6058's remedy split, which routes ObjectViewSchema's unmirrored declared keys as SPEC-DERIVED — "the LOCAL declaration carries members the spec schema does not model, which is objectui#2231's unification question and NOT a local mirror edit". That routing rests on the false positive. Re-routing those keys is a remedy decision on the UnmirroredDeclared ledger, which this card was fenced out of, so the split is left standing as written, counts and all, with a ⚠️ note at the site telling whoever works it off to re-derive that side first. No ledger (KnownDrift / UnmirroredDeclared / RuntimeOnlyDeclared) and no corpus schema is touched.

Evidence — both directions, and the gate is not blinded

The card's fence is that a fix which stops seeing comments and stops seeing real references goes green on the old suite while destroying the gate.

Live reproduction (ablation 1). The pre-fix scanner, run against this branch's restored docstring, reproduces the reported failure exactly — and names the innocent neighbour:

AssertionError: a mirror gained or lost a spec dependency: expected [ …(11) ] to deeply equal [ …(10) ]
+ "objectql.zod.ts#ObjectTreeSchema"

Mutation proven on disk (old window code present = 1, new AST helper present = 0); restored by blob hash, 1a45bced… before and after.

Positive direction (ablation 2). Neutering the fixed helper to return an empty set reddens 5 tests — the live re-check plus every positive fixture:

AssertionError: ... expected [] to deeply equal [ …(8) ]
AssertionError: expected [] to deeply equal [ 'ObjectGanttSchema' ] (x3)
AssertionError: expected [] to include 'ObjectGanttSchema'
Tests 5 failed | 7 passed (12)

Mutation proven on disk (injected marker = 1, removed expression = 0); restored by blob hash with git diff HEAD empty. All eight surviving pairs are found by code reference alone.

Seven fixtures pin the shape in memory: comment-only mention yields nothing; code reference fires; a Spec… token inside a string literal carrying */ and // is not a reference; a private const between two exports charges its prose to neither neighbour; a private const with a real reference is charged to its user, not its neighbour; the next export's docstring is not charged to the previous export; plus a non-vacuity pin against the real objectql.zod.ts.

The docstring restoration

PR #6704 reworded a docstring to dodge the literal token and left a trap comment asking the next editor to remember. That convention is what a gate is supposed to replace, so with the scanner fixed the workaround is retired and the docstring says what it wanted to say. This is the only change under packages/types/src/zod/, it is comment-only, and it doubles as the live positive proof above.

Gates run — all verdict lines quoted, against final commit 85e014af2

GateVerdict
parity fileTest Files 1 passed (1) / Tests 12 passed (12)
packages/types suiteTest Files 84 passed (84) / Tests 1070 passed (1070)
@object-ui/typestype-checkexit 0 — tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json
check:control-bytes✅ check-control-bytes: OK (scanned 6005 tracked text file(s); skipped 85 binary).
check:vi-mock-specifiers✅ check-vi-mock-specifiers: OK (4129 tracked source file(s) …)
check:vi-mock-inherit✅ check-vi-mock-inherit: OK (…)
check:spec-symbols✅ spec symbol derivation: 1333 files scanned against 4959 spec export names; … 0 untriaged collisions
check:phantom-deps✅ Every in-scope import is declared by the package that publishes it.
changeset presence✅ 2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s) — empty frontmatter, "the explicit exemption and a complete answer to this gate"
check-changeset-fixed / -no-major / -overwriteall ✅

type-check is a real measurement, not a vacuous pass: tsc -p tsconfig.test.json --listFiles shows the edited test file (2 hits), objectql.zod.ts (1) and typescript/lib/typescript.d.ts (1) in the compiled set.

Declared narrowing — lint. Repo-wide turbo run lint is CI's run. Locally lint was narrowed to the affected package and the narrowing is measured, not assumed: ① the population came from eslint's own traversal under the root flat config, not a guess; ② --format json reports 148 files linted, 0 errors, 267 warnings, exit 0 — the package's own lint script (eslint .) verbatim; ③ eslint.config.js declares no projectService and no parserOptions.project, so linting is not type-aware and this diff cannot move the verdict of any file it does not contain.

Merge state.origin/main was merged at 1a1235c94 (picking up #7241, #7216, #7236, #7262), and every verdict above was taken after that merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

… text
`zod-mirror-parity.test.ts` decided which mirrors are spec-derived by slicing
the source between `export const` boundaries and testing the slice against a
`\bSpec[A-Z]` word pattern. That had two independent defects, and one docstring
fired both: a `Spec…` token in PROSE counted as a reference, and the slice ran
past its own declaration to the next `export`, so the red named the neighbouring
export instead of the edited one.
`specReferencingExports()` parses instead. `Spec…` identifiers come off the AST,
so comments and string literals are not references and nothing has to strip
them. Attribution is by declaration; file-local references through PRIVATE
consts are resolved to a fixed point so a mirror built from a spec-derived field
map keeps its dependency, while exported mirrors are not hopped through.
Two entries leave the derived list as false positives of exactly those two
defects — `base.zod.ts#BaseSchema` (its file's only `Spec…` token is a comment)
and `objectql.zod.ts#ObjectViewSchema` (a `BaseSchema.extend` of local literals
whose window swallowed a private `SpecKanbanConfigSchema` block ~50 lines past
its end). No corpus and no ledger changes.
Seven fixtures pin both directions: a comment-only mention yields nothing, a
code reference still fires.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
PR #6704 reworded this docstring to dodge a literal `Spec` + capital-letter
token and left a comment asking the next editor to remember, because the
`SPEC_DERIVED_PAIRS` re-check scanned raw text and misattributed the mention to
`ObjectTreeSchema`. The previous commit made that scanner parse, so the
convention held by discipline is no longer needed and the workaround is
retired: the docstring names `SpecGanttConfigSchema` in prose again, which is
what it wanted to say.
This doubles as the live positive proof for objectui#6705 — the pre-fix scanner
goes red on this file state, the fixed one stays green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
Empty frontmatter — the presence gate's own explicit exemption. The scanner
change is test-only and the one change under `packages/types/src/zod/` is a
docstring; no runtime, type or schema surface moves.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3160.2 KB3191.4 KB
Main entry chunk (gzip)142.6 KB350 KB
Entry fileindex-BOEfHVe7.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.33KB5.59KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.46KB117.29KB
core (index.js)5.55KB2.23KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.25KB61.73KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)70.02KB19.44KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)250.65KB63.91KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.78KB32.58KB
plugin-gantt (index.js)166.77KB40.76KB
plugin-grid (index.js)208.88KB56.59KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)29.34KB8.47KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

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

Projects

None yet

2 participants

@yinlianghui@claude