Skip to content

docs(types): the four objectql mirror docblocks say "not defaulted on parse" (objectui#8735) - #9077

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-8735-stale-default-docblocks
Sep 10, 2026
Merged

docs(types): the four objectql mirror docblocks say "not defaulted on parse" (objectui#8735)#9077
os-warren merged 2 commits into
mainfrom
claude/issue-8735-stale-default-docblocks

Conversation

@os-warren

@os-warren os-warren commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Fixes #8735

objectui#8317 made @object-ui/types' zod mirrors stop substituting imported @objectstack/spec defaults on parse, via stripImportedDefaults. Four docblocks in packages/types/src/zod/objectql.zod.ts still described the OLD behaviour in the present tense, so each stated the opposite of what its own export does. Each now says the key is declared and accepted but NOT defaulted on parse.

Comment-only. No default, no behaviour and no accept set moves.

Premise re-derived on my own base

Re-measured on c3a427378 (my clone's origin/main, not the card's numbers). All four survive; anchors are the end-of-sentence lines.

key anchor the sentence that was false
HttpRequestSchema.method :98 "method now defaults to 'GET' on parse"
ListColumnSchema.prefix.type :121-122 "defaults to 'text' on parse ... so the renderer always gets a value"
SelectionConfigSchema.type :128-129 "type now defaults to 'none' on parse"
PaginationConfigSchema.pageSize :135-136 "positive-int with a default of 25 on parse"

⚠️ The line-wrap trap is real and I reproduced it. A line-scoped grep -F on these sentences returns a false 0 for three of the four, because they wrap across a line break — it reads exactly like "already fixed". Flattening the docblock line-prefix first gives flattened_count=1 for all four:

A method-GET        flattened_count=1   line_scoped_count=1
B prefix.type-text  flattened_count=1   line_scoped_count=0   <- false 0
C selection-none    flattened_count=1   line_scoped_count=0   <- false 0
D pageSize-25       flattened_count=1   line_scoped_count=0   <- false 0

Live-query controls on that same corpus: "on parse" occurs 5 times, an absent sentinel 0 times.

Each rewrite measured against the code, with a firing control

For every key: the spec leg still substitutes the default; the mirror leg does not; an authored value is preserved; an illegal value is refused. The firing control is a key whose value does survive the same parse, so an absent key is a real absence and not an empty probe.

A  HttpRequestSchema.method            [key: method]
  L1 spec   parse(omit method)   = {"url":"/x","method":"GET"}
  L2 mirror parse(omit method)   = {"url":"/x"}
  AC mirror parse(author method) = {"url":"/x","method":"POST"}
  FC control url on the omitted leg = "/x"
  RJ mirror safeParse(illegal method).success = false

B  ListColumnSchema.prefix.type        [key: prefix.type]
  L1 spec   parse(omit prefix.type)   = {"field":"f","prefix":{"field":"icon","type":"text"}}
  L2 mirror parse(omit prefix.type)   = {"field":"f","prefix":{"field":"icon"}}
  AC mirror parse(author prefix.type) = {"field":"f","prefix":{"field":"icon","type":"badge"}}
  FC control prefix.field on the omitted leg = "icon"
  RJ mirror safeParse(illegal prefix.type).success = false

C  SelectionConfigSchema.type          [key: type]
  L1 spec   parse(omit type)   = {"type":"none"}
  L2 mirror parse(omit type)   = {}
  AC mirror parse(author type) = {"type":"multiple"}
  FC control type on the authored leg = "multiple"
  RJ mirror safeParse(illegal type).success = false

D  PaginationConfigSchema.pageSize     [key: pageSize]
  L1 spec   parse(omit pageSize)   = {"pageSize":25,"pageSizeOptions":[10,20]}
  L2 mirror parse(omit pageSize)   = {"pageSizeOptions":[10,20]}
  AC mirror parse(author pageSize) = {"pageSize":50,"pageSizeOptions":[10,20]}
  FC control pageSizeOptions on the omitted leg = [10,20]
  RJ mirror safeParse(illegal pageSize).success = false

⚠️ One asymmetry, stated rather than hidden. SelectionConfigSchema's shape has exactly one key, so no sibling can serve as its control; C's firing control is the authored leg of the same key. A/B/D use a genuine sibling key in the same parse output. My first attempt at C put the control on the omitted leg, where it could not fire — that run is discarded, not reported.

The probe was a scratch file. It is not in this diff.

The changeset question, answered by reading the gate

scripts/check-changeset-presence.mjs, isPublishedSource:

export function isPublishedSource(relative, pkg) {
  if (relative.startsWith('src/')) return true;
  ...

Clause (a) answers first and unconditionally, and the gate does no content-level inspection anywhere — it classifies by path. So a comment-only edit inside packages/types/src/ is guarded exactly like any other source change, provided the package is in the fixed group. @object-ui/types is: versioned: true, ignored: false (read from the gate's own discoverPackages; control: README.md returns false from the same function).

a changeset is owed. The gate's own verdict lines, both directions:

with the changeset (rc=0)
  ✅  1 source file(s) of 1 released package(s) changed, and this change declares
      1 changeset(s): .changeset/8735-objectql-mirror-docblocks-not-defaulted.md.

ablated, changeset removed (rc=1)
  ❌  1 source file(s) of 1 released package(s) changed, and this change adds no changeset:
        @object-ui/types
            packages/types/src/zod/objectql.zod.ts

Restore leg: byte-identical (git hash-object matches before and after), git diff HEAD empty, gate back to rc=0.

Independently — and this is why the entry is a patch rather than an empty declaration — the corrected text really ships. Built @object-ui/types and matched the emitted declarations (flattened, since the emitted text wraps too):

dist/zod/objectql.zod.d.ts
  4  "declared and accepted but NOT defaulted on parse"     (new)
  1  "an omitted `pageSize` stays omitted"                  (new)
  0  "now defaults to `'GET'` on parse"                     (old, gone)
  0  "so the renderer always gets a value"                  (old, gone)
controls: "Selection Config Schema" 1, "HTTP Request Schema" 1, absent sentinel 0

files for this package is ["dist", ...], private unset, so those declarations are in the npm tarball. This matches the gate header's own recorded finding (objectui#5666) that comment text in src reaches a published .d.ts.

Verification

check result
pnpm --filter @object-ui/types test rc=0 — 173 files, 3401 tests passed
pnpm --filter @object-ui/types run type-check rc=0 (script name echoed; 31s of real work)
pnpm --filter @object-ui/types build rc=0 — 128 emitted files verified
check:changeset-presence rc=0 ✅ (and rc=1 when ablated)
check:spec-symbols rc=0 ✅ — 1372 files vs 5085 spec export names
check:control-bytes rc=0 ✅ — 7264 tracked text files scanned
check:comment-mask-corpus rc=0 — 4774 files, 1 disagree, at the objectui#7882 ceiling and not this file
check:new-line-citations rc=0 — 0 new citations
governed-surface guard NOT GOVERNED, 2 paths vs 5 surfaces (control: AGENTS.md matches)

Every rc was captured to a file before any pipe, and each line quotes the gate's own verdict rather than a bare exit code.

Lint, as a proven narrowing (the repo-wide baseline is red on main, so a repo-wide run is not the measurement):

  1. Population read from eslint's own resolution, not guessed: 243 files in packages/types.
  2. Counts read from --format json: package total 0 errors, 278 warnings (pre-existing); my file: 0 errors, 0 warnings.
  3. Invariance: eslint.config.js sets languageOptions to ecmaVersion + globals only — no parserOptions.project, no projectService, no type-checked preset (each queried and 0, against a file that exists; control term rules returns 12). Type-aware linting is off, so a comment-only edit in one file cannot move any untouched file's verdict.

Scope

Note for the reviewer

One behavior change rides along: prefix.type defaults to 'text' on parse also appears in three CHANGELOG.md files (packages/core, packages/react, packages/types). Those are left alone deliberately: they are historical release records that were true when written, and check-changeset-presence.mjs's header records a maintainer ruling (2026-08-24) that those compiled copies are never hand-edited.


Generated by Claude Code


Generated by Claude Code

… parse" (objectui#8735)

objectui#8317 made `@object-ui/types`' zod mirrors stop substituting imported
`@objectstack/spec` defaults on parse, via `stripImportedDefaults`. Four
docblocks in `zod/objectql.zod.ts` still described the OLD behaviour in the
present tense, so each stated the opposite of what its own export does:

  :98      `method` "now defaults to `'GET'` on parse"
  :121-122 `prefix.type` "defaults to `'text'` on parse ... so the renderer
           always gets a value"
  :128-129 `type` "now defaults to `'none'` on parse"
  :135-136 `pageSize` "with a default of 25 on parse"

Each is rewritten to say the key is declared and accepted but NOT defaulted on
parse. Measured per key against the code, spec leg vs mirror leg, each with a
firing control (a key whose authored value does survive the same parse):

  method       spec {"url":"/x","method":"GET"}  mirror {"url":"/x"}
  prefix.type  spec prefix{field,type:"text"}    mirror prefix{field}
  type         spec {"type":"none"}              mirror {}
  pageSize     spec {"pageSize":25,...}          mirror {"pageSizeOptions":[10,20]}

Comment-only: no default, no behaviour and no accept set moves. Each key is
still declared, still accepted, and still checked (an illegal value is refused
on all four).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
…jectui#8735)

`scripts/check-changeset-presence.mjs` guards `<pkg>/src/**` of every package in
the `fixed` group; `isPublishedSource` clause (a) returns true for anything under
`src/` first and unconditionally, with no content-level inspection, so a
comment-only edit inside `packages/types/src/` is guarded like any other source
change. Independently, the corrected text is emitted verbatim into the published
`dist/zod/objectql.zod.d.ts`, so it really does ship.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3490.1 KB 3512.7 KB
Main entry chunk (gzip) 144.2 KB 350 KB
Entry file index-CBNW8r8T.js
Status PASS

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

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) 500.20KB 114.67KB
core (index.js) 8.28KB 3.31KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 207.56KB 57.44KB
fields (index.js) 247.01KB 62.29KB
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) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
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.84KB 10.94KB
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.03KB 13.93KB
plugin-charts (index.js) 71.50KB 19.97KB
plugin-chatbot (index.js) 195.32KB 46.51KB
plugin-dashboard (index.js) 131.21KB 34.62KB
plugin-designer (index.js) 215.68KB 44.27KB
plugin-detail (index.js) 251.44KB 65.17KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.65KB 40.91KB
plugin-grid (index.js) 211.56KB 57.50KB
plugin-kanban (index.js) 46.07KB 14.32KB
plugin-list (index.js) 112.52KB 27.64KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.42KB 20.80KB
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) 83.34KB 27.61KB
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) 20.57KB 5.88KB
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.27KB 5.47KB
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

Copy link
Copy Markdown
Collaborator Author

Landing provenance — PR #9077 (card objectui#8735)

Reviewed / landed head: 6249be354fe6a0b6ae6af1129bbf59e4f96e6c6a.
Gate checkout: fresh objectstack clone os-fresh2, main = f721ef0ff2880dd3fc5389d5d4585b825252122e.

① in-seat clause-② contract review — NOT OWED, and that is a non-applicability, not a PASS

The card declares Clause-②: no, and both carriers are absent — they agree at absent, which
check-clause2-carriers --pair read mechanically rather than my recalling it. ⛔ No PASS record is
cited here because none exists and none is owed; a reader should not read this row as a cleared review.

② the enqueue gates

gate exact command exit
carriers + C5 PM_SWEEP_REPO=objectstack-ai/objectui node scripts/pm/check-clause2-carriers.mjs --pair 9077 0
widening tells PM_SWEEP_REPO=objectstack-ai/objectui node scripts/pm/check-widening-tells.mjs --declaration no --diff /tmp/9077.diff 0
governed surface node scripts/pm/check-governed-merges.mjs --pr objectstack-ai/objectui#9077 0

Verbatim from the carriers run — it names its own board, so the reading is not about the wrong repo:

check-clause2-carriers: every row below is read from objectstack-ai/objectui (source: PM_SWEEP_REPO).
✓ PR #9077 / card #8735 — the clause-② declaration is readable in the fixed spelling and both carriers agree, and its diff carries no widening tell.

check-governed-merges derived its own file list (GET /pulls/9077/files, 2 paths over 1 page) and
found 0 of 2 hitting the 5-surface register ⇒ ordinary queue landing applies.

The widening-tells run reports one file NOT MEASURED and I am not counting that as clean. The tool
says so itself: no declared surface covers .changeset/8735-objectql-mirror-docblocks-not-defaulted.md,
so no tell could have fired on it whatever it contained. The measured half is the one that matters here —
packages/types/src/zod/objectql.zod.ts was judged against a declared surface and carried no tell.

⭐ the control — why that clean reading is a real negative

A gate that returns 0 proves nothing until it is known to fire in the region under test. Same tool, same
declaration, same path, a diff that adds a key and an enum member:

✗ T1 packages/types/src/zod/objectql.zod.ts:97  — a new key on a Zod object schema
✗ T2 packages/types/src/zod/objectql.zod.ts:100 — a new member of a closed set
exit 4

It fires on this exact path. The 0 on the real diff is therefore a measurement, not a silence.

③ every check, not a required subset

35 check-runs on 6249be35…: 32 success + 3 skipped, 0 non-green. The API's reported
total_count is 35 and the returned array length is 35, so nothing hid behind pagination. The separate
commit-status API (which check-runs does not cover) reports state: success, 1 context, Vercel.

charter provenance, with its own control

The governing files are byte-identical between the clone's ad715aca57b44bbe745a20cf20f339cd5195df14
and current main f721ef0ff…contract-review.md, landing-operations.md, SKILL.md,
dispatch-gates.mjs, check-clause2-carriers.mjs, check-widening-tells.mjs, check-governed-merges.mjs
all resolve to the same blob at both revs. That empty path-scoped diff is controlled twice: the same two
commits diff to 20 files / +1412 / -25 with no path filter, and each of the seven paths resolves to a
real blob (not MISSING) at both revs. ⛔ I am not writing "no output above = clean" for it.

carriers stripped

Neither carrier holds needs:contract-review (the PR's labels are package: types only), so there is
nothing to strip. Landing by squash through the merge queue.


Generated by Claude Code

@os-warren
os-warren marked this pull request as ready for review September 10, 2026 23:24
@os-warren
os-warren added this pull request to the merge queue Sep 10, 2026
Merged via the queue into main with commit f1190b0 Sep 10, 2026
37 checks passed
@os-warren
os-warren deleted the claude/issue-8735-stale-default-docblocks branch September 10, 2026 23:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding(types): four zod/objectql.zod.ts docblocks now state the opposite of what the code does, after objectui#8317 stripped the imported defaults

2 participants