fix(scripts): type js-comment-mask.mjs's parameters so consumer call sites are checked - #9423
Conversation
…sites are checked `tsconfig.scripts.json` compiles `scripts/**` with `allowJs: true` and `checkJs: false` on purpose (objectui#3494), so a `.ts` consumer's types for these helpers come from inference over the `.mjs` source, steered by JSDoc and by nothing else. Four exports take parameters; only `scanSource` documented them. `blank` had a one-line prose docblock, `stripComments` and `maskComments` had prose-only multi-line ones -- so their parameters inferred `any` and every consumer call site was accepted unchecked. Measured on this tree before the change: `maskComments(12345)` -- a number passed to a string parameter -- type-checked clean at exit 0. The error was not merely unreported, it was unreachable, which is why objectui#9322 had to carry a hand-written `const mask: (source: string) => string = maskComments;` to keep its one call site checked. `scanSource` is the control that proved the gap was the missing tag rather than `allowJs` failing to type anything: it is imported alongside `maskComments` in the same files, under the same config, and it was checked. It is left exactly as it was. No masking behaviour changes. All four exports were run over a nine-file corpus of real repo sources (299,880 characters, 141,075 of them flagged as comment) before and after, and every output digest is identical -- which matters because several gates read this module as the single authority on "is this span a comment, or code?". The new test is what keeps the tags from being decorative. It compiles real calls against the real module with the real project options and asserts the DIAGNOSTIC rather than the tag's spelling: a wrong-typed argument must produce TS2345 and a correctly-typed one must produce nothing. Deleting a `@param` again makes the wrong-typed half stop erroring, which is the direction a grep for the tag cannot see. Part of objectui#9324 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
|
| Step | Outcome |
|---|---|
| Build packages | success |
| Check console performance budget | failure |
Which half objected:
| Eager-closure half | Verdict |
|---|---|
| Aggregate closure ceiling | ✅ pass |
| Per-chunk ceilings | ✅ pass |
| Ceiling sensitivity (headroom) | |
| Ceiling freshness (checkout vs. base branch) | ✅ pass |
⚠️ A broken gauge half is a verdict about the ceiling, not about the bundle: that line has drifted out of range of the regression it exists to catch, or the report behind it cannot be trusted. It does not say anything grew. TheCheck console performance budgetstep log carries the ceiling and the number it was compared against.
Reason: The entry chunk measured 144.5 KB, but the eager-closure half of this gate returned no trustworthy VERDICT: the report could not be read, a ceiling has drifted out of range of the regression it must catch, or (objectui#6245) a ceiling was replaced on the base branch after this checkout was made. The step log says which. This is not a passing budget — and it is not a size regression either.
See the workflow run for details.
📦 Bundle Size Report
| Package | Size | Gzipped |
|---|---|---|
| app-shell (consoleActionDispatch.js) | 0.20KB | 0.19KB |
| app-shell (index.js) | 16.69KB | 6.21KB |
| app-shell (runtime-config.js) | 20.68KB | 7.36KB |
| app-shell (types.js) | 0.01KB | 0.04KB |
| app-shell (urlParams.js) | 10.06KB | 3.86KB |
| auth (ActiveOrganizationStorage.js) | 25.05KB | 9.16KB |
| auth (AuthContext.js) | 0.31KB | 0.24KB |
| auth (AuthGuard.js) | 2.07KB | 1.00KB |
| auth (AuthProvider.js) | 40.18KB | 10.59KB |
| auth (AuthShell.js) | 3.49KB | 1.40KB |
| auth (ForgotPasswordForm.js) | 12.21KB | 3.45KB |
| auth (LoginForm.js) | 18.15KB | 5.39KB |
| auth (PreviewBanner.js) | 0.90KB | 0.50KB |
| auth (RegisterForm.js) | 6.65KB | 2.22KB |
| auth (SocialSignInButtons.js) | 9.61KB | 3.89KB |
| auth (UserMenu.js) | 3.41KB | 1.23KB |
| auth (auth-gate-events.js) | 1.29KB | 0.66KB |
| auth (authStyles.js) | 5.04KB | 1.72KB |
| auth (createAuthClient.js) | 40.21KB | 10.80KB |
| auth (createAuthenticatedFetch.js) | 8.46KB | 3.43KB |
| auth (index.js) | 3.19KB | 1.44KB |
| auth (invitation-status.js) | 1.22KB | 0.70KB |
| auth (org-roles.js) | 6.66KB | 2.78KB |
| auth (phone-identifier.js) | 1.11KB | 0.66KB |
| auth (types.js) | 0.59KB | 0.35KB |
| auth (useAuth.js) | 5.30KB | 1.02KB |
| auth (useWorkspaceAdminStatus.js) | 11.08KB | 4.58KB |
| collaboration (CommentThread.js) | 26.08KB | 7.56KB |
| collaboration (LiveCursors.js) | 3.17KB | 1.27KB |
| collaboration (PresenceAvatars.js) | 6.49KB | 2.64KB |
| collaboration (PresenceProvider.js) | 2.79KB | 1.13KB |
| collaboration (index.js) | 1.68KB | 0.73KB |
| collaboration (useCollaborationTranslation.js) | 6.05KB | 2.52KB |
| collaboration (useCommentSearch.js) | 1.98KB | 0.88KB |
| collaboration (useConflictResolution.js) | 7.75KB | 1.86KB |
| collaboration (useMentionNotifications.js) | 1.81KB | 0.68KB |
| collaboration (usePresence.js) | 6.33KB | 1.84KB |
| collaboration (useRealtimeSubscription.js) | 7.91KB | 2.01KB |
| components (index.js) | 502.03KB | 115.16KB |
| core (index.js) | 8.52KB | 3.41KB |
| create-plugin (index.js) | 27.94KB | 9.51KB |
| data-objectstack (index.js) | 213.54KB | 59.33KB |
| fields (index.js) | 247.89KB | 62.50KB |
| i18n (LocalizationContext.js) | 1.76KB | 0.96KB |
| i18n (builtinAggregateLabels.js) | 0.86KB | 0.49KB |
| i18n (currency.js) | 1.22KB | 0.64KB |
| i18n (fallbackInterpolation.js) | 6.25KB | 2.77KB |
| i18n (i18n.js) | 8.87KB | 3.64KB |
| i18n (index.js) | 5.22KB | 2.26KB |
| i18n (pickLocalized.js) | 9.86KB | 3.95KB |
| i18n (provider.js) | 32.15KB | 10.49KB |
| i18n (useDisplayLocale.js) | 2.85KB | 1.45KB |
| i18n (useObjectLabel.js) | 34.34KB | 9.17KB |
| i18n (useSafeTranslation.js) | 5.60KB | 2.33KB |
| layout (index.js) | 38.83KB | 10.95KB |
| mobile (MobileProvider.js) | 0.92KB | 0.49KB |
| mobile (ResponsiveContainer.js) | 0.94KB | 0.38KB |
| mobile (breakpoints.js) | 1.51KB | 0.70KB |
| mobile (createOfflineDataSource.js) | 5.61KB | 1.75KB |
| mobile (index.js) | 1.99KB | 0.87KB |
| mobile (offlineQueue.js) | 3.91KB | 1.35KB |
| mobile (pwa.js) | 0.97KB | 0.49KB |
| mobile (serviceWorker.js) | 1.48KB | 0.62KB |
| mobile (serviceWorkerSource.js) | 3.41KB | 1.48KB |
| mobile (useBreakpoint.js) | 1.54KB | 0.65KB |
| mobile (useGesture.js) | 6.96KB | 1.98KB |
| mobile (useOfflineSync.js) | 1.99KB | 0.72KB |
| mobile (usePullToRefresh.js) | 2.53KB | 0.85KB |
| mobile (useResponsive.js) | 0.72KB | 0.42KB |
| mobile (useSpecGesture.js) | 4.39KB | 1.66KB |
| mobile (useTouchTarget.js) | 1.01KB | 0.54KB |
| permissions (MePermissionsProvider.js) | 13.52KB | 4.88KB |
| permissions (PermissionContext.js) | 0.31KB | 0.25KB |
| permissions (PermissionGuard.js) | 0.89KB | 0.45KB |
| permissions (PermissionProvider.js) | 6.24KB | 2.16KB |
| permissions (discardProofCache.js) | 1.04KB | 0.55KB |
| permissions (evaluator.js) | 8.39KB | 3.10KB |
| permissions (index.js) | 0.93KB | 0.41KB |
| permissions (store.js) | 0.91KB | 0.42KB |
| permissions (useFieldPermissions.js) | 1.28KB | 0.53KB |
| permissions (usePermissions.js) | 4.83KB | 2.27KB |
| plugin-ai (index.js) | 14.81KB | 3.63KB |
| plugin-calendar (index.js) | 49.25KB | 13.99KB |
| plugin-charts (index.js) | 71.34KB | 19.90KB |
| plugin-chatbot (index.js) | 195.34KB | 46.51KB |
| plugin-dashboard (index.js) | 131.44KB | 34.65KB |
| plugin-designer (index.js) | 215.94KB | 44.33KB |
| plugin-detail (index.js) | 253.46KB | 65.85KB |
| plugin-editor (index.js) | 2.23KB | 1.05KB |
| plugin-form (index.js) | 136.71KB | 34.16KB |
| plugin-gantt (index.js) | 166.95KB | 41.04KB |
| plugin-grid (index.js) | 211.68KB | 57.52KB |
| plugin-kanban (index.js) | 46.00KB | 14.30KB |
| plugin-list (index.js) | 112.53KB | 27.63KB |
| plugin-map (index.js) | 21.48KB | 6.99KB |
| plugin-markdown (index.js) | 13.88KB | 4.80KB |
| plugin-report (index.js) | 43.41KB | 11.93KB |
| plugin-timeline (index.js) | 30.07KB | 8.74KB |
| plugin-tree (index.js) | 9.55KB | 3.32KB |
| plugin-view (index.js) | 84.36KB | 20.78KB |
| providers (DataSourceProvider.js) | 0.75KB | 0.39KB |
| providers (MetadataProvider.js) | 1.37KB | 0.59KB |
| providers (ThemeProvider.js) | 1.90KB | 0.85KB |
| providers (UploadProvider.js) | 11.66KB | 3.50KB |
| providers (index.js) | 0.45KB | 0.23KB |
| providers (types.js) | 0.01KB | 0.04KB |
| react-runtime (index.js) | 5.62KB | 2.34KB |
| react (LazyPluginLoader.js) | 4.47KB | 1.63KB |
| react (SchemaRenderer.js) | 96.00KB | 31.71KB |
| react (data-invalidation.js) | 5.05KB | 2.08KB |
| react (index.js) | 4.63KB | 2.18KB |
| react (schema-input.js) | 4.25KB | 2.04KB |
| react (spec-input.js) | 0.20KB | 0.18KB |
| sdui-parser (codegen.js) | 6.58KB | 2.74KB |
| sdui-parser (dashboard-widget-options.js) | 3.08KB | 1.30KB |
| sdui-parser (index.js) | 5.66KB | 2.50KB |
| sdui-parser (input-type.js) | 2.84KB | 1.40KB |
| sdui-parser (kanban-quick-add.js) | 3.89KB | 1.87KB |
| sdui-parser (parse.js) | 25.28KB | 7.80KB |
| sdui-parser (provenance.js) | 3.66KB | 1.82KB |
| sdui-parser (types.js) | 0.28KB | 0.23KB |
| sdui-parser (validate.js) | 14.82KB | 4.99KB |
| types (ai.js) | 0.20KB | 0.17KB |
| types (api-types.js) | 0.20KB | 0.18KB |
| types (app.js) | 2.87KB | 1.00KB |
| types (base.js) | 0.20KB | 0.18KB |
| types (blocks.js) | 0.20KB | 0.18KB |
| types (complex.js) | 2.93KB | 1.49KB |
| types (crud.js) | 0.20KB | 0.18KB |
| types (dashboard-filter-alias.js) | 6.23KB | 2.74KB |
| types (data-display.js) | 3.75KB | 1.85KB |
| types (data-protocol.js) | 0.20KB | 0.19KB |
| types (data.js) | 0.20KB | 0.18KB |
| types (designer.js) | 1.85KB | 0.85KB |
| types (disclosure.js) | 0.20KB | 0.18KB |
| types (error-code.js) | 1.54KB | 0.88KB |
| types (expression.js) | 0.20KB | 0.18KB |
| types (feedback.js) | 0.20KB | 0.18KB |
| types (field-types.js) | 0.20KB | 0.18KB |
| types (form.js) | 0.20KB | 0.18KB |
| types (http-inflight.js) | 8.87KB | 3.73KB |
| types (http-retry.js) | 4.32KB | 2.02KB |
| types (icon-key-migration.js) | 4.26KB | 1.63KB |
| types (index.js) | 4.74KB | 2.25KB |
| types (layout.js) | 0.20KB | 0.18KB |
| types (managed-by.js) | 0.19KB | 0.18KB |
| types (mobile.js) | 4.73KB | 2.28KB |
| types (navigation.js) | 0.20KB | 0.18KB |
| types (objectql.js) | 0.20KB | 0.18KB |
| types (overlay.js) | 0.20KB | 0.18KB |
| types (permissions.js) | 0.20KB | 0.18KB |
| types (plugin-scope.js) | 0.20KB | 0.18KB |
| types (record-components.js) | 0.20KB | 0.19KB |
| types (record-semantics.js) | 1.28KB | 0.67KB |
| types (registry.js) | 0.20KB | 0.18KB |
| types (reports.js) | 0.20KB | 0.18KB |
| types (select-option.js) | 0.20KB | 0.19KB |
| types (spec-report.js) | 5.05KB | 1.93KB |
| types (spec-ui-namespace.js) | 0.20KB | 0.19KB |
| types (strict-authoring-face.js) | 14.04KB | 5.36KB |
| types (system-fields.js) | 3.33KB | 1.54KB |
| types (theme.js) | 6.28KB | 2.87KB |
| types (ui-action.js) | 8.11KB | 3.32KB |
| types (views.js) | 0.20KB | 0.18KB |
| types (widget.js) | 0.20KB | 0.18KB |
Size Limits
- ✅ Core packages should be < 50KB gzipped
- ✅ Component packages should be < 100KB gzipped
⚠️ Plugin packages should be < 150KB gzipped
…am-type test `Lint` was red on the repo-wide root run, and the single error in it was this file's line 2: an unused `import fs`. `object-ui/no-unused-imports` is error-level and `@typescript-eslint/no-unused-vars` is warning-level, so the same finding was reported twice and only the first one failed the gate. Both declarations are leftovers from an earlier draft that read the masker from disk, before the harness switched to resolving it through the case file's own relative import. Neither is scaffolding: counted over this file with comments stripped, `fs` and `maskerPath` each occurred exactly once -- their own declaration, with zero uses -- while `path`, `ts`, `repoRoot` and `configPath` all hit as controls on the same input. So they are removed rather than renamed; prefixing an underscore would have silenced the report without retiring the dead code, and `maskerPath`'s report was only a warning and was failing nothing. `scripts/js-comment-mask.mjs` is untouched, verified by blob hash. Nothing else in the run is touched: the 32 warnings that remain are pre-existing, on unrelated files. `pnpm lint:root` exit 1 -> 0 (35 problems / 1 error -> 32 problems / 0 errors). `pnpm type-check:scripts` exit 0. The param-type test and its neighbours still pass: 4 files, 75 tests. Part of objectui#9324 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
|
| Step | Outcome |
|---|---|
| Build packages | success |
| Check console performance budget | failure |
Which half objected:
| Eager-closure half | Verdict |
|---|---|
| Aggregate closure ceiling | ✅ pass |
| Per-chunk ceilings | ✅ pass |
| Ceiling sensitivity (headroom) | |
| Ceiling freshness (checkout vs. base branch) | ✅ pass |
⚠️ A broken gauge half is a verdict about the ceiling, not about the bundle: that line has drifted out of range of the regression it exists to catch, or the report behind it cannot be trusted. It does not say anything grew. TheCheck console performance budgetstep log carries the ceiling and the number it was compared against.
Reason: The entry chunk measured 144.5 KB, but the eager-closure half of this gate returned no trustworthy VERDICT: the report could not be read, a ceiling has drifted out of range of the regression it must catch, or (objectui#6245) a ceiling was replaced on the base branch after this checkout was made. The step log says which. This is not a passing budget — and it is not a size regression either.
See the workflow run for details.
📦 Bundle Size Report
| Package | Size | Gzipped |
|---|---|---|
| app-shell (consoleActionDispatch.js) | 0.20KB | 0.19KB |
| app-shell (index.js) | 16.69KB | 6.21KB |
| app-shell (runtime-config.js) | 20.68KB | 7.36KB |
| app-shell (types.js) | 0.01KB | 0.04KB |
| app-shell (urlParams.js) | 10.06KB | 3.86KB |
| auth (ActiveOrganizationStorage.js) | 25.05KB | 9.16KB |
| auth (AuthContext.js) | 0.31KB | 0.24KB |
| auth (AuthGuard.js) | 2.07KB | 1.00KB |
| auth (AuthProvider.js) | 40.18KB | 10.59KB |
| auth (AuthShell.js) | 3.49KB | 1.40KB |
| auth (ForgotPasswordForm.js) | 12.21KB | 3.45KB |
| auth (LoginForm.js) | 18.15KB | 5.39KB |
| auth (PreviewBanner.js) | 0.90KB | 0.50KB |
| auth (RegisterForm.js) | 6.65KB | 2.22KB |
| auth (SocialSignInButtons.js) | 9.61KB | 3.89KB |
| auth (UserMenu.js) | 3.41KB | 1.23KB |
| auth (auth-gate-events.js) | 1.29KB | 0.66KB |
| auth (authStyles.js) | 5.04KB | 1.72KB |
| auth (createAuthClient.js) | 40.21KB | 10.80KB |
| auth (createAuthenticatedFetch.js) | 8.46KB | 3.43KB |
| auth (index.js) | 3.19KB | 1.44KB |
| auth (invitation-status.js) | 1.22KB | 0.70KB |
| auth (org-roles.js) | 6.66KB | 2.78KB |
| auth (phone-identifier.js) | 1.11KB | 0.66KB |
| auth (types.js) | 0.59KB | 0.35KB |
| auth (useAuth.js) | 5.30KB | 1.02KB |
| auth (useWorkspaceAdminStatus.js) | 11.08KB | 4.58KB |
| collaboration (CommentThread.js) | 26.08KB | 7.56KB |
| collaboration (LiveCursors.js) | 3.17KB | 1.27KB |
| collaboration (PresenceAvatars.js) | 6.49KB | 2.64KB |
| collaboration (PresenceProvider.js) | 2.79KB | 1.13KB |
| collaboration (index.js) | 1.68KB | 0.73KB |
| collaboration (useCollaborationTranslation.js) | 6.05KB | 2.52KB |
| collaboration (useCommentSearch.js) | 1.98KB | 0.88KB |
| collaboration (useConflictResolution.js) | 7.75KB | 1.86KB |
| collaboration (useMentionNotifications.js) | 1.81KB | 0.68KB |
| collaboration (usePresence.js) | 6.33KB | 1.84KB |
| collaboration (useRealtimeSubscription.js) | 7.91KB | 2.01KB |
| components (index.js) | 502.03KB | 115.16KB |
| core (index.js) | 8.52KB | 3.41KB |
| create-plugin (index.js) | 27.94KB | 9.51KB |
| data-objectstack (index.js) | 213.54KB | 59.33KB |
| fields (index.js) | 247.89KB | 62.50KB |
| i18n (LocalizationContext.js) | 1.76KB | 0.96KB |
| i18n (builtinAggregateLabels.js) | 0.86KB | 0.49KB |
| i18n (currency.js) | 1.22KB | 0.64KB |
| i18n (fallbackInterpolation.js) | 6.25KB | 2.77KB |
| i18n (i18n.js) | 8.87KB | 3.64KB |
| i18n (index.js) | 5.22KB | 2.26KB |
| i18n (pickLocalized.js) | 9.86KB | 3.95KB |
| i18n (provider.js) | 32.15KB | 10.49KB |
| i18n (useDisplayLocale.js) | 2.85KB | 1.45KB |
| i18n (useObjectLabel.js) | 34.34KB | 9.17KB |
| i18n (useSafeTranslation.js) | 5.60KB | 2.33KB |
| layout (index.js) | 38.83KB | 10.95KB |
| mobile (MobileProvider.js) | 0.92KB | 0.49KB |
| mobile (ResponsiveContainer.js) | 0.94KB | 0.38KB |
| mobile (breakpoints.js) | 1.51KB | 0.70KB |
| mobile (createOfflineDataSource.js) | 5.61KB | 1.75KB |
| mobile (index.js) | 1.99KB | 0.87KB |
| mobile (offlineQueue.js) | 3.91KB | 1.35KB |
| mobile (pwa.js) | 0.97KB | 0.49KB |
| mobile (serviceWorker.js) | 1.48KB | 0.62KB |
| mobile (serviceWorkerSource.js) | 3.41KB | 1.48KB |
| mobile (useBreakpoint.js) | 1.54KB | 0.65KB |
| mobile (useGesture.js) | 6.96KB | 1.98KB |
| mobile (useOfflineSync.js) | 1.99KB | 0.72KB |
| mobile (usePullToRefresh.js) | 2.53KB | 0.85KB |
| mobile (useResponsive.js) | 0.72KB | 0.42KB |
| mobile (useSpecGesture.js) | 4.39KB | 1.66KB |
| mobile (useTouchTarget.js) | 1.01KB | 0.54KB |
| permissions (MePermissionsProvider.js) | 13.52KB | 4.88KB |
| permissions (PermissionContext.js) | 0.31KB | 0.25KB |
| permissions (PermissionGuard.js) | 0.89KB | 0.45KB |
| permissions (PermissionProvider.js) | 6.24KB | 2.16KB |
| permissions (discardProofCache.js) | 1.04KB | 0.55KB |
| permissions (evaluator.js) | 8.39KB | 3.10KB |
| permissions (index.js) | 0.93KB | 0.41KB |
| permissions (store.js) | 0.91KB | 0.42KB |
| permissions (useFieldPermissions.js) | 1.28KB | 0.53KB |
| permissions (usePermissions.js) | 4.83KB | 2.27KB |
| plugin-ai (index.js) | 14.81KB | 3.63KB |
| plugin-calendar (index.js) | 49.25KB | 13.99KB |
| plugin-charts (index.js) | 71.34KB | 19.90KB |
| plugin-chatbot (index.js) | 195.34KB | 46.51KB |
| plugin-dashboard (index.js) | 131.44KB | 34.65KB |
| plugin-designer (index.js) | 215.94KB | 44.33KB |
| plugin-detail (index.js) | 253.46KB | 65.85KB |
| plugin-editor (index.js) | 2.23KB | 1.05KB |
| plugin-form (index.js) | 136.71KB | 34.16KB |
| plugin-gantt (index.js) | 166.95KB | 41.04KB |
| plugin-grid (index.js) | 211.68KB | 57.52KB |
| plugin-kanban (index.js) | 46.00KB | 14.30KB |
| plugin-list (index.js) | 112.53KB | 27.63KB |
| plugin-map (index.js) | 21.48KB | 6.99KB |
| plugin-markdown (index.js) | 13.88KB | 4.80KB |
| plugin-report (index.js) | 43.41KB | 11.93KB |
| plugin-timeline (index.js) | 30.07KB | 8.74KB |
| plugin-tree (index.js) | 9.55KB | 3.32KB |
| plugin-view (index.js) | 84.36KB | 20.78KB |
| providers (DataSourceProvider.js) | 0.75KB | 0.39KB |
| providers (MetadataProvider.js) | 1.37KB | 0.59KB |
| providers (ThemeProvider.js) | 1.90KB | 0.85KB |
| providers (UploadProvider.js) | 11.66KB | 3.50KB |
| providers (index.js) | 0.45KB | 0.23KB |
| providers (types.js) | 0.01KB | 0.04KB |
| react-runtime (index.js) | 5.62KB | 2.34KB |
| react (LazyPluginLoader.js) | 4.47KB | 1.63KB |
| react (SchemaRenderer.js) | 96.00KB | 31.71KB |
| react (data-invalidation.js) | 5.05KB | 2.08KB |
| react (index.js) | 4.63KB | 2.18KB |
| react (schema-input.js) | 4.25KB | 2.04KB |
| react (spec-input.js) | 0.20KB | 0.18KB |
| sdui-parser (codegen.js) | 6.58KB | 2.74KB |
| sdui-parser (dashboard-widget-options.js) | 3.08KB | 1.30KB |
| sdui-parser (index.js) | 5.66KB | 2.50KB |
| sdui-parser (input-type.js) | 2.84KB | 1.40KB |
| sdui-parser (kanban-quick-add.js) | 3.89KB | 1.87KB |
| sdui-parser (parse.js) | 25.28KB | 7.80KB |
| sdui-parser (provenance.js) | 3.66KB | 1.82KB |
| sdui-parser (types.js) | 0.28KB | 0.23KB |
| sdui-parser (validate.js) | 14.82KB | 4.99KB |
| types (ai.js) | 0.20KB | 0.17KB |
| types (api-types.js) | 0.20KB | 0.18KB |
| types (app.js) | 2.87KB | 1.00KB |
| types (base.js) | 0.20KB | 0.18KB |
| types (blocks.js) | 0.20KB | 0.18KB |
| types (complex.js) | 2.93KB | 1.49KB |
| types (crud.js) | 0.20KB | 0.18KB |
| types (dashboard-filter-alias.js) | 6.23KB | 2.74KB |
| types (data-display.js) | 3.75KB | 1.85KB |
| types (data-protocol.js) | 0.20KB | 0.19KB |
| types (data.js) | 0.20KB | 0.18KB |
| types (designer.js) | 1.85KB | 0.85KB |
| types (disclosure.js) | 0.20KB | 0.18KB |
| types (error-code.js) | 1.54KB | 0.88KB |
| types (expression.js) | 0.20KB | 0.18KB |
| types (feedback.js) | 0.20KB | 0.18KB |
| types (field-types.js) | 0.20KB | 0.18KB |
| types (form.js) | 0.20KB | 0.18KB |
| types (http-inflight.js) | 8.87KB | 3.73KB |
| types (http-retry.js) | 4.32KB | 2.02KB |
| types (icon-key-migration.js) | 4.26KB | 1.63KB |
| types (index.js) | 4.74KB | 2.25KB |
| types (layout.js) | 0.20KB | 0.18KB |
| types (managed-by.js) | 0.19KB | 0.18KB |
| types (mobile.js) | 4.73KB | 2.28KB |
| types (navigation.js) | 0.20KB | 0.18KB |
| types (objectql.js) | 0.20KB | 0.18KB |
| types (overlay.js) | 0.20KB | 0.18KB |
| types (permissions.js) | 0.20KB | 0.18KB |
| types (plugin-scope.js) | 0.20KB | 0.18KB |
| types (record-components.js) | 0.20KB | 0.19KB |
| types (record-semantics.js) | 1.28KB | 0.67KB |
| types (registry.js) | 0.20KB | 0.18KB |
| types (reports.js) | 0.20KB | 0.18KB |
| types (select-option.js) | 0.20KB | 0.19KB |
| types (spec-report.js) | 5.05KB | 1.93KB |
| types (spec-ui-namespace.js) | 0.20KB | 0.19KB |
| types (strict-authoring-face.js) | 14.04KB | 5.36KB |
| types (system-fields.js) | 3.33KB | 1.54KB |
| types (theme.js) | 6.28KB | 2.87KB |
| types (ui-action.js) | 8.11KB | 3.32KB |
| types (views.js) | 0.20KB | 0.18KB |
| types (widget.js) | 0.20KB | 0.18KB |
Size Limits
- ✅ Core packages should be < 50KB gzipped
- ✅ Component packages should be < 100KB gzipped
⚠️ Plugin packages should be < 150KB gzipped
PM pre-merge probe —
|
| sha | |
|---|---|
| base this PR records | cb725e78fb |
ACTUAL origin/main at probe time |
0a6372e3a9 — docs(prompts): renumber ui-library prompt sections (#9421) |
⭐ That is this seat's own previous landing — the fourth time today the thing that moved the base under a PR was my own merge. Measured: git diff cb725e78fb 0a6372e3a9 -- scripts/js-comment-mask.mjs is EMPTY, so the subject file is unmoved by every advance and the pre-values below stand. <merge>^ in the LANDED note.
Leg 1 — @param per exported function, BOTH SIDES, populations named
Population: every export function in scripts/js-comment-mask.mjs, paired with the @param count of the docblock immediately above it.
| export | params | pre | post |
|---|---|---|---|
scanSource |
(source) |
1 | 1 ← control, already correct, must not move |
blank |
(source, flags) |
0 | 2 |
stripComments |
(source) |
0 | 1 |
maskComments |
(source) |
0 | 1 |
selfTest |
() |
0 | 0 ← takes no parameters; correctly gained nothing |
⭐ selfTest staying at 0 is the leg that shows this was not a blanket sweep: a change that "adds @param everywhere" would have touched it.
Leg 2 — the control is BYTE-IDENTICAL
scanSource body, L222..437 on both sides: sha256[:16] = 8e715c3a20dc63fc, identical. Its docblock likewise (32b5471d114abcbc). Guard is non-empty and is ⛔ not e3b0c44298fc1c14 (sha256 of the empty string).
scanSource as CHANGED. My segment ran from its docblock to the next export function, so it swallowed blank's docblock — which the dev legitimately grew from 1 line to 7. A segment boundary drawn at "the next declaration" measures the next thing's docblock too. Bounding the read at scanSource's own closing brace gives the identity above.
Leg 3 — ⭐ the ACCEPTANCE, verified with the compiler rather than by reading tags
The card's acceptance is 「call sites become CHECKED」, ⛔ not 「the tag text exists」. I built a minimal program with this PR's own option set (allowJs: true, checkJs: false, strict) against the pre and post copies of the module, calling all three functions with wrong-typed arguments:
PRE : TS2345 = 0 (total diagnostics 0) <- wrong-typed calls pass SILENTLY
POST: TS2345 = 3 (total diagnostics 3) <- all three now caught
Argument of type 'number' is not assignable to parameter of type 'string'. x3
⇒ the tags are load-bearing, not decorative. TypeScript 6.0.2, ts.createProgram + getPreEmitDiagnostics.
// @ts-ignore-nothing as a label above the first call. TypeScript parses that as @ts-ignore and suppressed the very diagnostic I was measuring — verified directly against a guaranteed type error (0 diagnostics). Removing it gives 3, matching the dev exactly. ⭐ A comment I wrote as documentation silently became a suppression directive.
Leg 4 — why the .mjs call sites are NOT the population
tsconfig.scripts.json is include: ["scripts/**/*.ts"] with allowJs: true, checkJs: false. ⇒ the .mjs callers are consumed for inferred types but never type-checked; the call sites that become checked are the .ts suites — 13 across 7 files (1+4+2+1+2+1+2). .mjs invocations: a different population, ⛔ not a discrepancy in the dev's figure. The card's own 12/6 is genuine drift from landed importers, and the dev stated that rather than quietly adopting it.
Leg 5 — the cross-package TS2578 hazard, which is objectui#3535's failure mode
31 files under packages/** import this module, 31 of 31 behind // @ts-expect-error — plain-JS shared helper, intentionally untyped. Had these tags made those imports type cleanly, all 31 directives would go unused → TS2578, reddening files this PR never touched. It cannot happen, and the robust reason is ⛔ not allowJs:
- every
packages/*/tsconfig.jsonexcludes**/__tests__/**and**/*.test.ts; all 31 importers match those globs ⇒ 0 are in any package program. A directive in a file the compiler never opens cannot go unused. ⭐ This survives a futureallowJs: trueon the root config. - (The
allowJsargument is true but weaker, and⚠️ the file usually cited for it —tsconfig.base.json— governs nothing here: nopackages/*config extends it.allowJsis absent from the resolved options entirely, i.e. at TypeScript's default. That is objectui#9330's thesis in the wild.)
js-comment-mask; 31 carry a real import. The 32nd mentions it in a docblock. I first read that file as 「an importer with no directive」 — ⛔ wrong, prose scored as code.
CI on b82347722d
36 runs: 32 success, 3 skipped, 1 failure. All 9 required contexts green: Lint · Type Check · Build & E2E · Test (shard 1/4 … 4/4) · Build Docs · Changeset Declaration.
The one failure is Bundle Analysis — ⛔ not a required context (established on objectui#9155, which merged with it red), red across this lane, ⛔ not caused by this diff and ⛔ not chased.
Lint was RED on the first head (4a3873fe13) and it was this PR's own: an unused import fs in its new test file — the single error among 34 pre-existing warnings. Fixed in commit 2 (-2/+0, one file), no amend, no force-push, 0 eslint-disable in the diff (control expect( → 5).
⇒ Flipping ready and enqueueing.
Generated by Claude Code
✅ LANDED —
|
pre (0a6372e3a9) |
post (fcdc4d0150, on main) |
|
|---|---|---|
scanSource @param |
1 | 1 ← control, unmoved |
blank |
0 | 2 |
stripComments |
0 | 1 |
maskComments |
0 | 1 |
selfTest (takes none) |
0 | 0 |
scanSource body sha256[:16] |
8e715c3a20dc63fc |
8e715c3a20dc63fc ← identical across the merge |
What actually landed, and why the tags are not decorative
The acceptance was 「call sites become CHECKED」, ⛔ not 「the tag text exists」. Verified with the compiler under this PR's own option set (allowJs: true, checkJs: false, strict): wrong-typed calls to all three functions go TS2345 = 0 → 3. Before this PR, stripComments(someNumber) compiled silently.
The population that becomes checked is the .ts suites — tsconfig.scripts.json is include: ["scripts/**/*.ts"] with checkJs: false, so the .mjs callers are never type-checked. 13 call sites across 7 files.
⚠️ Three corrections this seat made AGAINST ITSELF while reviewing, recorded because the PR's value depends on them
- I read
scanSourceas CHANGED. Wrong — my segment ran from its docblock to the nextexport functionand swallowedblank's docblock, which the dev legitimately grew from 1 line to 7. ⭐ A segment boundary drawn at 「the next declaration」 measures the next thing too. - I read 6 call sites across 6 files against the dev's 13/7. Wrong — I counted
.mjsinvocations, a populationcheckJs: falseexcludes from checking entirely. The dev's figure was right; the card's 12/6 is genuine drift. - My first acceptance run read TS2345 = 2, not 3. Wrong — I had written
// @ts-ignore-nothingas a LABEL above the first case, and TypeScript parses that as@ts-ignoreand silently suppressed the diagnostic I was measuring (verified directly: 0 diagnostics on a guaranteed type error). ⭐ A comment written as documentation became a real directive.
⇒ Three defective legs from the reviewer, against one off-by-one from the dev (32 vs 31 masker consumers — a docblock mention scored as an import, which they owned unprompted). ⛔ None of mine changed the outcome; all three are recorded so the numbers above can be trusted for the right reasons.
⭐ The hazard the dev went looking for unprompted — objectui#3535's failure mode
31 files under packages/** import this module, 31 of 31 behind // @ts-expect-error — plain-JS shared helper, intentionally untyped. Had these tags made those imports type cleanly, every directive would go unused → TS2578, reddening 31 files this PR never touched. That is precisely the collision objectui#3535 documents (and which I closed as measured-not-reachable earlier today).
It cannot bite, and ⭐ the robust reason is not the one first cited: all 31 importers sit under __tests__/ or *.test.ts, which every packages/*/tsconfig.json excludes — tsc never opens them (31 of 31 excluded, 0 in-program). A directive in a file the compiler never reads cannot go unused, whatever allowJs does. allowJs argument is true but weaker, and the file usually cited for it — tsconfig.base.json — governs nothing here: no packages/* config extends it, so allowJs sits at TypeScript's default. That is objectui#9330's thesis in the wild, found from the other side.
Not this PR's
Bundle Analysis red — ⛔ not a required context (objectui#9155 merged with it red), red across this lane. check:node-esm-load red — the dev established it is inherited using a pristine control worktree detached at their exact base with none of their edits, which failed identically (37 refused vs 2). ⭐ That is how you prove a red is not yours.
Generated by Claude Code
Part of objectui#9324.
tsconfig.scripts.jsoncompilesscripts/**withallowJs: trueandcheckJs: falsedeliberately (objectui#3494), so a.tsconsumer's types forscripts/js-comment-mask.mjscome from inference over the.mjssource, steered by JSDoc and by nothing else. Four exports take parameters; onlyscanSourcedocumented them.What was wrong, re-measured on the ref
git showagainstfb4ec65daf— the card's own line citations were corrected once already, and the correction was re-checked too. The ref is named beside each reading.fb4ec65daf@paramscanSource:222@param {string} source— the control, untouchedblank:440anystripComments:469maskComments:485blankas having no docblock at all. It has one — a single-line prose docblock on the line above the declaration. The blob is byte-identical at the filing base and at this ref, so that cell was never right. The substance is unaffected: whatblanklacked is the@param, not the docblock.The measurement that makes it more than a style nit
pnpm type-check:scriptsis the command that coversscripts/— per-packagetype-checkdoes not reach it. The masker is in that program (tsc --listFilesnamesscripts/js-comment-mask.mjsamong 935 files), so a green there is a measurement and not a vacuous pass.A probe file inside the program, holding a number passed to each string parameter:
⇒ Before this PR the type error was not merely unreported, it was unreachable. That is why objectui#9322 had to carry a hand-written
const mask: (source: string) => string = maskComments;to keep its one call site checked.Consumer population, re-derived
Counted over
scripts/**/*.ts— the filestsconfig.scripts.jsonactually type-checks — reading each file's code only, with comments removed by the repo's ownstripComments, so a mention in prose is not scored as a call.fb4ec65dafselfTesttakes none)@paramscanSource)anyBroken out:
maskComments10 unannotated calls across 5 files;blank3 across 3 files;stripComments0 (no importer — latent only). One furthermaskCommentscall, incheck-spec-range-floors.test.ts, goes through the hand-written alias above and was already checked, so it is excluded from the unchecked count.⇒ The card said 12 across 6. It does not match, and the difference is ordinary drift, not error — the card named its base, and importers have landed since.
Behaviour is unchanged, and that is pinned
Several gates read this module as the single authority on "is this span a comment, or code?", and objectui#9183 routed 29 test files onto it for that reason. All four exports were run over a nine-file corpus of real repo sources — 299,880 characters, 141,075 of them flagged as comment, both non-zero as a guard — before and after the change. Every output digest is identical:
Nothing about what counts as a comment is widened or narrowed.
blankandmaskCommentsagree becausemaskComments(s)isblank(s, scanSource(s).comment); the guard above is what keeps that from being two empty strings agreeing.The new test, and why it is not a spelling check
Asserting that the tag is present asserts the spelling, and a docblock saying
@paramis worth nothing unless TypeScript acts on it.js-comment-mask-param-types-9324.test.tscompiles real calls against the real module with the real project options and asserts the diagnostic: a wrong-typed argument must produce TS2345, a correctly-typed one must produce nothing. The correctly-typed half is load-bearing too — without it, "the wrong call errors" is also satisfied by a signature so narrow that every real call errors.Ablation. Removing the three
@paramblocks from the committed tree and re-running:⭐ The
scanSourcecase is absent from that exhaustive four-item failure list — it kept firing while the other three went quiet. That is the discriminator the test was shaped for: three going quiet points at those three tags, whereas all four going quiet would point atallowJsor at the harness. The mutation was proved to reach disk by blob hash before the run (@paramlines 5 → 1), and the restore was proved by the file's blob hash returning to itsHEADvalue.Checks run
pnpm type-check:scriptsvitest runover the masker + all 10 consumer/pin test filescheck:control-bytes,check:comment-mask-corpus,check:new-line-citations,check:esm-specifiers,check:test-path-roots,check:changeset-claimscheck:governed-queue-guard --teston both changed pathscheck-changeset-presencecheck:node-esm-loadpackages/orapps/.🤖 Generated with Claude Code
https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
Generated by Claude Code