Skip to content

fix(types): collapse the redundant UserActionsSchema extension, correct its docblock (objectui#8992) - #9017

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-8992-useractions-collapse-and-docblock
Sep 10, 2026
Merged

fix(types): collapse the redundant UserActionsSchema extension, correct its docblock (objectui#8992)#9017
os-warren merged 2 commits into
mainfrom
claude/issue-8992-useractions-collapse-and-docblock

Conversation

@os-warren

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

Copy link
Copy Markdown
Collaborator

Fixes #8992

Repair round (contract review returned REWORK on the prose, not the code). The changeset is regraded minor, and two factual claims below were measured FALSE and are corrected: the cause of the lost descriptions, and "the other eight keys". The docblock, the collapse, the new pin and the parity row were re-measured by the reviewer and are unchanged. Head: 2f430a3152f1.

Three items from the card, in its order of urgency, plus the neighbouring floor question it handed the taker.

1. The false docblock — measured, corrected, pinned

packages/types/src/zod/objectql.zod.ts told its reader:

Note UserActionsConfigSchema is NOT .strict(), so before this extension an author writing userActions: { group: false } had it silently stripped — valid on parse, no effect at render.

Measured false at every published 17.x. Not read from source — parsed, against each version's own published tarball (npm pack, then import('./dist/ui/index.mjs'), no workspace resolution involved):

spec declared keys { oneUndeclaredKey: true }
17.0.0 8 REFUSED · unrecognized_keys: ['group']
17.2.0 8 REFUSED, key named
17.3.0 11 REFUSED, key named
17.4.0 (resolved) 11 REFUSED, key named, 1 issue

Independent corroboration from a second instrument, in objectui's OWN published artifact: the emitted packages/types/dist/zod/objectql.zod.d.ts declares this object z.core.$stricton both sides of this change. The compiler was already contradicting the comment.

The comment is now replaced by what the runtime does, and __tests__/user-actions-mirror-8992.test.ts pins it so it cannot rot back.

2. The collapse

stripImportedDefaults(Spec).extend({ group, hideFields, rowColor })stripImportedDefaults(Spec).

Premise re-derived rather than inherited: resolved @objectstack/spec is 17.4.0 (read off disk — node_modules/.pnpm/@objectstack+spec@17.4.0_.../package.json, exactly one copy installed — not off a declared range), and its UserActionsConfigSchema declares 11 keys including all three.

The accept set does not move. Extended and collapsed were built side by side in one process and parsed over a 33-document corpus — every declared key in both polarities, the full 11-key block, undeclared keys, objectui's legacy showGroup spelling, wrong types, null / [] / 'str':

CORPUS SIZE = 33  DIFFS = 0

Identical on every document: same success, same parsed output, same refusal code / keys / message. With a sentinel proving the comparison can see a difference when one exists (a .omit({group:true}) variant diverges immediately), and a firing control proving the refusal is about the key and not a constant.

3. The parity exemption

zod-mirror-parity.test.ts's EXCLUSIONS entry survives — the exclusion criterion still holds, because the TS name UserActionsConfig is re-exported FROM @objectstack/spec/ui by ../index.ts rather than restated in this package, so there is still no second definition to drift from. What changed is its REASON, which named an .extend(…) that no longer exists.

⚠️ What moves on the published surface

Both confined to those three keys, both measured by rebuilding packages/types/dist on each side:

  1. They end up carrying no .describe() metadata. ⛔ Not because the protocol leaves them undescribed — an earlier revision of this body said that and it is measured FALSE: the 17.3.0 and 17.4.0 tarballs describe all three (group reads "Allow users to change record grouping from the toolbar. …"). The cause is objectui's own import boundary: stripImportedDefaults unwraps each ZodDefault with .removeDefault() and re-optionalises the inner node, and the description sits on the OUTER node it discards. Measured on this object, on both sides of this change: all ten defaulted keys read description = undefined after the strip, while buttons — the one member that never carried a default — keeps its description through it (positive control). So the three stop being an exception: the local extension had been supplying descriptions the other ten defaulted keys never had. Nothing in this repository reads them.
  2. In the emitted .d.ts they move z.ZodOptional[z.ZodBoolean]z.ZodDefault[z.ZodBoolean]. z.input is unchanged; z.output for these three goes boolean | undefinedboolean, i.e. the keys become REQUIRED on the output type. This is breaking in the producer direction, measured with tsc over the two declarations built side by side: a value typed as the old output is NOT assignable to the new one (the three read as missing); the reverse compiles. The probe is not vacuous — injecting a @ts-expect-error where nothing was wrong made the same run fail with TS2578, proving the file was in the program. Readers of parsed output are unaffected; code that CONSTRUCTS an output-typed userActions must add the three keys or widen its annotation. That break is why the changeset is minor (this repo ships its own breaking changes as minor with the break spelled out), matching the 17.1.0 precedent for the identical ListColumnSchema collapse.

(2) is the one a contract reviewer should look at. It is a normalisation of the DECLARATION — and, separately, a real producer-side break, which is what the minor grade is for. stripImportedDefaults is deliberately typed T in / T out — stripping is "a property of the PARSE, not of the declaration" (decision batch #90, objectui#8317) — so every key that arrives from the spec declares ZodDefault while the runtime has the default removed. SEVEN of the other eight keys on this same object have declared exactly that all alongsort, search, filter, refresh, rowHeight, addRecordForm, editInline; buttons is z.ZodOptional[z.ZodArray[z.ZodString]] and never declared a default. The extension was making three keys the odd ones out of an object whose eleven members behave identically at runtime. ⛔ Nothing about what parses changes: an omitted key is still absent from the parsed output, measured, on all eleven.

Verification

Every command's exit code captured to a file before any pipe; each verdict quoted from the tool's own output. Heavy runs serialised through the container's shared verify lock (slot os-dev-8992) — the wall-clock seconds below are shared-box readings, not idle-box ones.

run result
pnpm exec vitest run packages/types/ Test Files 169 passed (169) · Tests 3350 passed (3350)
pnpm --filter @object-ui/types type-check exit 0 (all three tsc programs)
node scripts/check-changeset-presence.mjs ✅ 3 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)
node scripts/check-control-bytes.mjs ✅ OK (scanned 7210 tracked text file(s))
node scripts/check-spec-symbol-derivation.mjs on all three of its checks
node scripts/check-new-cross-file-line-citations.mjs VERDICT … 0 new citation(s) … exit 0
node scripts/check-unreferenced-sources.mjs OK Every shipped source file in every covered package is reachable

Non-vacuity of the suite run: 168 .test.ts files are tracked under packages/types at the merge base and 169 are on disk, and vitest reported exactly 169 — so the new pin is inside the package run, not merely inside its own targeted one.

Non-vacuity of the type-check: tsc -p tsconfig.test.json --listFiles enumerates 635 files and contains all three changed/added files by name, so type-check genuinely covered them.

Ablation — the new pin is shown to FAIL, twice, in both of its directions

Each leg: mutate → prove the mutation reached disk by counting its marker in the source → run → restore → prove the restore by comparing git hash-object against the HEAD blob and by git diff HEAD being empty. Restore points at HEAD (never a bare git checkout --), and the whole script carries trap … EXIT INT TERM with absolute paths.

leg mutation result
baseline none Tests 10 passed (10)
loose .loose() — recreate exactly the world the false comment described 4 failed | 6 passed — all four refusal assertions redden
widen .extend({ zzLocalWidening }) — a local key the protocol does not declare 1 failed | 9 passed — the key-set identity assertion reddens

HEAD blob 67f26263… restored byte-identical after each leg.

The dist marker, read both ways. packages/types/dist was built from HEAD and deliberately NOT rebuilt during the two ablation legs: the mutation markers count 0 in dist while the pin reddens, which is positive proof the run reads SOURCE and that a skipped rebuild cannot have measured a stale artifact here. (The root vitest.config.mts aliases @object-ui/typespackages/types/src explicitly, and the whole 169-file suite ran green before any dist existed at all.) The other direction was measured separately, WITH rebuilds, in the type-surface run above: the marker moves 0 → 2 → 0 across build-at-HEAD → build-at-pre-fix → build-at-restored.

CI on the first head (b4e86e41), stated as measured

Terminal and green: 34 check runs — 31 success, 3 skipped (Test (coverage), Test (coverage shard …), dependabot), 0 failure, 0 pending. All nine merge-queue required checks green, Type Check among them, which is the downstream type answer this PR declared to CI.

It was not green on the first attempt, and the honest account of that is narrower than an earlier revision of this body's report claimed:

  • Test (shard 2/4) started 18:07:31Z, its Run tests step went in_progress at 18:08:03Z and never ended, and the job was cancelled at 18:32:31Z — exactly 25m00s against a declared timeout-minutes: 20. ⛔ So "cancelled at the job bound" is not what the numbers say: the two do not match, this session issued no cancel, and only one CI run exists on the branch (so it was not cancel-in-progress). The cause is NOT MEASURED — the job log sits behind a blob host this container's proxy refuses.
  • The re-run of the same commit passed in 15m57s (18:34:28Z → 18:50:25Z), against 15m25s–15m50s for shards 1/3/4.
  • ⛔ That makes "infrastructure, not this diff" plausible, not proven. Re-deriving vitest's own sequencer (BaseSequencer.shard: sha1 of the root-relative path, sorted, then calculateShardRange) over the 2924 collected specs on this head puts zod-mirror-parity.test.tsa file this PR changes — in shard 2, the shard that stalled; the new pin lands in shard 1. Control: the computation yields 731 specs per shard, matching the 731 files CI reported for the shard-2 re-run, and a non-test path resolves to no shard at all.

⚠️ The @object-ui/core floor — decided: NOT bumped here, and filed instead

The card asked the taker to decide whether @object-ui/core's declared '@objectstack/spec': '^17.2.0' should move. It should — but not in this PR. Filed with the measurement as objectui#9012.

The finding is real and now measured rather than inferred: the range sits in dependencies (consumer-facing), core's fold emits userActions.group / .hideFields / .rowColor, and against the real 17.2.0 artifact that exact output comes back REFUSED refused-keys=["group","hideFields","rowColor"] while 17.3.0 accepts it — same firing control refused by both, so the difference is about the three keys and not the harness.

Why it is not a rider here:

  • It is a published-surface change to a different package than this card's (@object-ui/types), and raising a declared floor narrows what consumers may resolve.
  • This diff does not move that coupling in either direction — it is identical before and after.
  • No gate in this repository would hold it. check-spec-range-floors.mjs judges symbol presence in the published artifact, and UserActionsConfigSchema is exported by both 17.2.0 and 17.3.0 (verified in each tarball's dist/ui/index.d.ts); its workflow also only triggers on changes to the gate script itself. A bump landed quietly here would be prose-backed and could drift straight back.

⛔ objectui#5435 stays open and is not addressed by this PR; objectui#2231 (the legacy-vocabulary migration) is out of scope, as the card directs.

验收备注

Observations found on the way, none filed — recorded here so the reviewing seat sees them rather than the next dev rediscovering them:

  • Six sibling EXCLUSIONS rows carry the same stale reason this PR fixed. HttpMethodSchema, HttpRequestSchema, ViewDataSchema, ListColumnSchema, SelectionConfigSchema and PaginationConfigSchema are all plain stripImportedDefaults(Spec…) re-exports today, yet each is excluded "because the local .extend(…) adds renderer props". ListColumnSchema did once carry an .extend (two commits touch that spelling); the other five never did — the reason is a template that was true of the family it was written for and is now false of six of its seven members. ⛔ Not fixed here, and the reason an earlier revision of this body gave was wrong: PR fix(types): ObjectKanbanSchema.groupBy is optional, as the protocol declares it (objectui#8990) #9021 (objectui#8990) does not touch zod-mirror-parity.test.ts — measured off its file list, it touches packages/types/src/objectql.ts and packages/types/src/zod/objectql.zod.ts, so the only file the two PRs share is objectql.zod.ts, which is not where these rows live. The reason that does hold is scope: a seventh row's repair does not license rewriting six more under a different card's claim. The taker is the next collapse card in objectql.zod.ts — it will read the same template.
  • The card's own anchors had drifted by three lines on this base: the false sentence is at :607, not :604, and the declaration at :611, not :608. Re-derived rather than trusted, as the dispatch instructed. Everything else in the card held.
  • The card listed "whether collapsing changes any inferred type at a call site" as not measured. It is measured now — see "What moves on the published surface" — and UserActionsSchema turns out to have exactly one consumer, ListViewSchema.userActions in the same file; it is not re-exported from the @object-ui/types/zod barrel.

Not measured

  • Downstream packages' type-check (plugin-view, plugin-detail, app-shell read ListViewSchema). Deliberately declared to CI's Type Check, which is green on the farm. ⛔ An earlier revision of this body justified that with "assignment-safe in the direction consumers care about" — that over-claims: the movement is safe for READERS of parsed output and breaking for PRODUCERS of output-typed values (measured above). In-repo there is no z.output / z.infer over these schemas to break, which is why CI is green; external producers are not covered by any run here.
  • Whether any other workspace manifest has the same behavioural floor shape as @object-ui/core's — 26 declare ^17.0.0. Named as a separate job in objectui#9012.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w


Generated by Claude Code


Generated by Claude Code

…rect its docblock (objectui#8992)

`objectql.zod.ts`'s `UserActionsSchema` carried
`stripImportedDefaults(Spec).extend({ group, hideFields, rowColor })`, an
extension that existed only because `@objectstack/spec` did not declare those
three keys while `normalizeListViewSchema` folded objectui's legacy `showGroup`
/ `showHideFields` / `showColor` onto them. The protocol adopted all three in
17.3.0, so the extension is now a second local copy of a protocol declaration
and it collapses into the plain by-reference re-export its own note said it
would become.

The urgent half is the docblock, which told its reader that
`UserActionsConfigSchema` "is NOT `.strict()`, so ... an author writing
`userActions: { group: false }` had it silently stripped — valid on parse, no
effect at render". Measured against the published artifacts of 17.0.0, 17.2.0,
17.3.0 and the resolved 17.4.0, every one of them refuses an undeclared key and
names it (`unrecognized_keys`, one issue). A comment promising silent tolerance
in front of a loud-rejection runtime points an author — human or AI — at a
config that will fail the save gate while telling them that outcome is
impossible.

The accept set does not move: extended and collapsed were parsed side by side
over a 33-document corpus with an identical result — same success, same parsed
output, same refusal codes, keys and messages — with a sentinel proving the
comparison can see a difference when one exists. The one published byte that
moves is the loss of three local `.describe()` strings the extension carried,
which the protocol declares those keys without.

`__tests__/user-actions-mirror-8992.test.ts` pins the refusal and the key-set
identity, with firing controls in both directions, so the corrected paragraph
cannot rot back. The `zod-mirror-parity.test.ts` exclusion survives — the TS
name is re-exported from the spec, not restated here — but its reason no longer
names an `.extend(…)` that is gone.

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-Bp_bf9Dr.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) 7.95KB 3.19KB
create-plugin (index.js) 28.21KB 9.54KB
data-objectstack (index.js) 207.32KB 57.37KB
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.63KB 19.99KB
plugin-chatbot (index.js) 195.32KB 46.51KB
plugin-dashboard (index.js) 130.98KB 34.54KB
plugin-designer (index.js) 215.68KB 44.27KB
plugin-detail (index.js) 253.71KB 65.78KB
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) 210.87KB 57.30KB
plugin-kanban (index.js) 46.03KB 14.30KB
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) 2.32KB 1.24KB
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

Contract review

Head reviewed: b4e86e412e7c840dbde42b9df8e6a35d6f2b08f3 — branch claude/issue-8992-useractions-collapse-and-docblock, 1 commit, 4 files; re-read immediately before posting, unchanged. Merge-base with origin/main (0601af1a) at review time: 2596b1b8.

Tier fuse, read not asserted. CONTRACT_REVIEW_TIER = 'claude-fable-5-1' (objectstack origin/main scripts/pm/dispatch-gates.mjs:10505). My own transcript, located by a marker string I wrote first (subagents/agent-af3d2270df87bbe51.jsonl), carries harness model stamps of one value only, claude-fable-5-1 (12 of 12 at the first read, 98 of 98 at the last) — no fallback evidence. The implementer's transcript (agent-ac2435341307b8792.jsonl) carries 304 stamps, all claude-opus-5; its Co-Authored-By line is a constant, not a reading.

Instruments. Own blobless clone of the PR branch under my scratchpad, pnpm install --frozen-lockfile (resolved @objectstack/spec@17.4.0, one copy on disk); published tarballs @objectstack/spec 17.0.0 / 17.2.0 / 17.3.0 / 17.4.0 and @object-ui/types@17.6.0 fetched with npm pack; packages/types built at head; every rc captured to a file before any pipe. Nothing in the shared checkouts was touched; no file in the PR tree was edited.

① Derived judgments

1. The false docblock — CONFIRMED; the method is sound. Re-run myself: npm pack each version, import('./dist/ui/index.mjs') with zod@4.4.3 the only resolvable dependency and no workspace resolution. 17.0.0: 8 declared keys, {group:false} REFUSED unrecognized_keys ['group'], 1 issue. 17.2.0: 8 keys, REFUSED, 1 issue. 17.3.0: 11 keys, {group:false} ACCEPTED, {zzUndeclared:true} REFUSED with the key named, 1 issue. 17.4.0: same as 17.3.0. Positive control {sort:true} ACCEPTED on all four, so REFUSED is a reading and not a constant. Second instrument, both sides: the published @object-ui/types@17.6.0 dist/zod/objectql.zod.d.ts (pre-change) and my head build (post-change) both declare UserActionsSchema as z.core.$strict. The retired sentence was false at every published 17.x. The new docblock's own claims re-checked: @object-ui/types declares ^17.3.0 (packages/types/package.json:96); 8 vs 11 keys as measured above; the Option A ruling is dated 2026-08-22 (objectui#5435, comment 5377368469). The new pin runs green in my clone: 10 passed (10).

2. The equivalence proof — CONFIRMED; all three legs hold. Rebuilt side by side in one vitest process from the resolved spec: stripImportedDefaults(Spec), stripImportedDefaults(Spec).extend({...}) with the exact three described optionals the old code carried, and a sentinel collapsed.omit({ group: true }). My own corpus, 35 documents (every declared key in both polarities, the full 11-key block, undeclared keys, the legacy showGroup spelling, wrong types, null / [] / 'str' / 42, buttons variants, a mixed declared-plus-undeclared document): CORPUS SIZE = 35 DIFFS = 0, comparing success, parsed output and refusal code / keys / path / message. Control {zzUndeclared:true}: refused by both, key named. Sentinel: diverges on 6 of 35 documents — the comparison can see a difference, it is not a tautology. The head's exported UserActionsSchema is reference-equal to the collapsed build (memoised boundary); the extended build is not. {} parses to {} on both sides (the raw spec writes ten defaults; the boundary strips all ten).

3. The two moves on the published surface — the ruling covers the destination shape; two factual claims in the changeset are wrong; the z.output narrowing is producer-side breaking at the type level.

  • Measured. Head-built .d.ts: group / hideFields / rowColor are z.ZodDefault[z.ZodBoolean]; the published 17.6.0 .d.ts has them z.ZodOptional[z.ZodBoolean]. A tsc probe against both builds: z.input{} assignable on both sides; z.output['group'] — accepts undefined on the extended side, refuses it on head (TS2322); a complete extended-output value is NOT assignable to a head-output value (TS2322, missing group / hideFields / rowColor); a head-output value IS assignable to an extended-output value. Readers of parsed output are safe; producers of output-typed values break. The accept set and parse output do not move (item 2).
  • Ruling coverage. Decision batch [WIP] Fix action run issue in CI/CD pipeline #90 (objectui#8317) strips every imported default at the boundary; the T-in / T-out static typing is documented as deliberate in imported-defaults.ts:311-318 ("a property of the PARSE, not of the declaration"). After the collapse the three keys are spec-defaulted keys — the class that ruling governs — so the destination declaration (ZodDefault in the .d.ts, omissible at runtime) is the ruled shape, and I do not re-open it. What the ruling does not do is grade the transition for consumers typed against the old ZodOptional declaration; that is a semver question and it is answered under ②, not by re-litigating the ruling.
  • "The other eight keys on this same object have declared exactly that all along" — INEXACT. Seven have (sort, search, filter, refresh, rowHeight, addRecordForm, editInline are ZodDefault in the published 17.6.0 .d.ts); buttons is ZodOptional[ZodArray[ZodString]] and never declared a default. Seven of eight.
  • "The protocol declares those keys without descriptions of its own" — FALSE. The 17.3.0 and 17.4.0 tarballs both carry .describe() on all three (group: "Allow users to change record grouping from the toolbar. …"; hideFields, rowColor likewise). The three do end up description-less after the collapse, but because objectui's own boundary discards them: stripImportedDefaults unwraps every ZodDefault with .removeDefault() and re-optionalises the inner node (imported-defaults.ts:200-202), and the description sits on the outer node it discards. Measured on both builds: all ten defaulted keys read description = undefined after the strip; buttons keeps its. The outcome sentence is true; the stated cause is false, and it is written into the changeset (which ships into CHANGELOG.md), the PR body and the commit message. "Nothing in this repository reads them" holds: the only in-repo z.toJSONSchema consumers (app-shell metadata-admin view-schema.ts / page-schema.ts) convert the spec's own ViewSchema / PageSchema, not the mirror.
  • Downstream. In-repo: none (no z.output / z.infer over UserActionsSchema or ListViewSchema anywhere in packages/ or apps/; ListViewInferred = z.input[...]; CI Type Check green on the farm). Externally the movement is reachable through ListViewSchema, which is in the ./zod barrel (index.zod.ts:307); UserActionsSchema itself is not in the barrel — verified, the dev's claim holds.

4. The EXCLUSIONS row — CONFIRMED. UserActionsConfig is exported at packages/types/src/index.ts:1284 inside an export type { … } from '@objectstack/spec/ui' block (1281-1286): no local restatement exists, so there is nothing to pair it with in MIRRORS, and deleting the row would fail the unaccounted-export census (zod-mirror-parity.test.ts:3318). Keeping it is right. The rewritten reason names no live .extend(…); each of its claims (plain re-export, keys declared from 17.3.0, TS name re-exported) measured true. The reason is only length-checked (:3360), so its truth rests on review — which this is.

5. CI — terminal and green on this head; the disposition is right, its wording is not.

  • Read myself: 34 check runs on b4e86e41… — 31 success, 3 skipped (Test (coverage), Test (coverage shard …), dependabot), 0 failure, 0 pending. Workflow run 34512385055: attempt 2 success; attempt 1 cancelledTest (shard 2/4) job 102989437972 started 18:07:31Z, its Run tests (shard 2/4) step in_progress from 18:08:03Z with no end, job cancelled 18:32:31Z. Re-run job 102998639047 (rerun_failed_jobs, issued 18:34:21Z per the implementer's transcript): 18:34:28Z → 18:50:25Z success, 730 files passed | 1 skipped (731).
  • "Cancelled at the job bound" — not what the numbers say. The job's declared bound is timeout-minutes: 20 (ci.yml:722); the job ran exactly 25m00s. It was not cancelled by the implementer (its transcript holds zero cancel calls, REST or MCP, and exactly one actions_run_trigger, the rerun_failed_jobs), and not by cancel-in-progress (only one CI run exists on the branch). The job log is unreachable from this container (302 to a blob host the proxy refuses; the MCP job-log fetch answers 404 for that id), so the cause stays NOT MEASURED. The re-run on the current head is consistent with the review checklist (which forbids re-running only a cancel on a non-current head); "the one re-run the rules allow" is not a rule I could find in either repo's AGENTS.md or the skill.
  • Shard membership — measured, where the dev stopped. vitest 4.1.10 shards by sha1 of the root-relative test path, sorted, then sliced (BaseSequencer.shard, calculateShardRange); with the 2924 files on this head (731 per shard — the CI shard-2 count) the new pin lands in shard 1/4; zod-mirror-parity.test.ts, which this PR touches, lands in shard 2/4 — the shard that stalled. That moves "infrastructure, not this diff" from proven to plausible: the stalled shard did run a file this PR changed, and the remaining evidence is the same commit passing that shard in 15m57s against 15m25s–15m50s for shards 1/3/4.
  • Reproduced the dev's control: vitest list --shard=1/4 --filesOnly and --shard=2/4 both print 2924 files, comm difference 0 — the flag is inert for list.

② semver — MISMATCH

The changeset declares patch. Repo precedent, not inference: the identical operation on the same file — ListColumnSchema's local .extend() collapsed into a plain by-reference re-export — shipped under 17.1.0 Minor Changes (packages/types/CHANGELOG.md:2208-2216, "one behavior change rides along"); the batch #90 boundary strip is pending as minor (.changeset/8317-strip-imported-defaults.md); the sibling PR of this batch, #9021 (objectui#8990), also moves a published @object-ui/types face and declares minor. Policy (AGENTS.md:251): objectui's own breaking changes ship as minor with the break spelled out in the body. This changeset itself says two things move on the published surface, and the .d.ts movement measures as non-assignable in the producer direction (① 3). patch is under-declared; minor matches both precedent and policy. The fixed group would cut the same version either way (151 pending minor entries), but the section the entry lands in is the contract statement.

③ Boundary flags

  • objectui#9012 (core's ^17.2.0) — the "no gate" reading CONFIRMED, one correction; not bumping here ACCEPTED. check-spec-range-floors.mjs judges the symbols the built artifact references (header lines 5-11, 73-97); UserActionsConfigSchema exists in 17.2.0 (8 keys, measured), so ^17.2.0 and ^17.3.0 are both green to it. Its workflow has no pull_request trigger — but "only triggers on changes to the gate script" is incomplete: it also runs nightly (11 4 * * *), on workflow_dispatch, and its blocking copy runs on pnpm changeset:publish. On the decision: core's declared range is untouched by this diff, true; but the behavioural shield — the local extension carrying the three keys whatever spec resolved — is removed by this PR, as the new docblock itself says. The floor that becomes load-bearing is @object-ui/types' own ^17.3.0, which core depends on ("@object-ui/types": "workspace:*"), so a consumer reaches 17.2.x only by overriding. Floor-honesty defect on core, filed separately: correct.
  • Six sibling EXCLUSIONS rows — restraint ACCEPTED, one of its reasons is wrong. All six are plain stripImportedDefaults(Spec…) re-exports today (objectql.zod.ts:92-138). I parsed every historical blob of the file (76 commits): ListColumnSchema's declaration carried .extend( in 8 of them, the other five in 0 — the history claim holds. But "objectui#8990 holds the same two files" is not true of the parity file: PR fix(types): ObjectKanbanSchema.groupBy is optional, as the protocol declares it (objectui#8990) #9021 touches objectql.ts and objectql.zod.ts, not zod-mirror-parity.test.ts (the dispatch's "same two files" were the former pair). The reason that holds is scope: a seventh row's repair does not license six more under another card's claim.
  • vitest list --shard does not partition — CONFIRMED (reproduced above). The self-limitation was honest but unnecessary: membership is computable from the sequencer, and it puts the new pin in shard 1 and the parity test in shard 2.
  • MCP issue-create strips the footer — not worth a card, because it is already documented. This repo's AGENTS.md, section "GitHub 会改写你写进 issue/PR 正文的字节", item ④ (:454), records exactly this behaviour with the sentinel method, and :462 prescribes the mitigation (write attribution as prose in issue bodies; do not rely on the footer block). The dev's "no fix, no footer loop" matches :471 (check the rendered page; a rewrite destroys a correct card). The seat's append-only REST repair of objectui#8968 / capability(components): the destructive (red) confirm button on alert-dialog — batch #70 wanted it, confirmVariant was never a vehicle for it #8978 / finding(types): NamedListView declares none of 17 members the protocol declares live on the same surface — objectui is NARROWER than the spec, the direction the principle forbids #8980 with read-back is consistent with item ③'s measured PATCH behaviour. What the dev owed and did not give is the citation — a documented behaviour was reported as a new finding. objectui#9012's stored body still carries no footer (read back).

Out-of-scope finding of my own (a card, not a rider): stripImportedDefaults silently drops .describe() metadata from every ZodDefault it unwraps — measured, 10 of 11 keys on this object lose the spec's descriptions — while its own comment (imported-defaults.ts:165-167) names description loss as the silent-loss shape its clone rule exists for, and the 8317 pin's "nothing else changes" property covers keys, node types and checks but not descriptions. No in-repo consumer reads the mirror's descriptions today.

Independence

Implemented-by: claude/issue-8992-useractions-collapse-and-docblock (mode:subagent)
Reviewed-by: session_01Jmxdo7bmeqCQHLSfmLVX9w subagent agent-af3d2270df87bbe51 (mode:subagent, harness model claude-fable-5-1)

SELF-REVIEW. The implementer (transcript agent-ac2435341307b8792, claude-opus-5) and this reviewer are subagents of the same dispatch session, session_01Jmxdo7bmeqCQHLSfmLVX9w. Under contract-review.md this record is a self-review, ⛔ not an independent clause-② review: it must not be counted as one, and the seat must not clear needs:contract-review on its strength alone. The dispatch order and the seat's disposition were visible to me (both live on the card); every judgment above was re-measured rather than inherited, and none was softened on that account.

Required rework — confined to the changeset file and the PR body

  1. .changeset/8992-user-actions-collapse-and-docblock.md: patchminor (②).
  2. Same file, item 1 of the "TWO THINGS" list: replace "because the protocol declares those keys without descriptions of its own" with the measured cause — the protocol declares descriptions on all three; objectui's boundary (stripImportedDefaults: .removeDefault() then re-optionalise) does not carry a defaulted key's description through, so after the collapse the three carry none, like the seven other defaulted keys.
  3. Same file, item 2: "the OTHER EIGHT keys" → seven of the other eight (buttons is ZodOptional, never defaulted).
  4. PR body: the same two corrections, and "cancelled at the job bound" → cancelled at 25m00s against a 20-minute bound, cause unread.

No change is required to objectql.zod.ts, the new pin, or the parity row — all three measured correct.

VERDICT: REWORK

What I did not measure

  • The cause of the attempt-1 cancellation: the job log sits behind a blob host this container's proxy refuses, and the MCP job-log fetch answers 404 for that job id.
  • check-clause2-carriers.mjs --pair 9017 and check-widening-tells.mjs — landing pre-checks, the landing seat's act.
  • Downstream type-check beyond CI's Type Check job; no external consumer code was inspected.
  • The dev's ablation legs (loose / widen) and its dist-marker 0 → 2 → 0 run — not re-executed; what I did read is the pin's green and the dist marker on the two published/built artifacts ('Allow users to group records': 1 in the 17.6.0 tarball, 0 in the head build).
  • The dev's 33-document corpus as it built it — I built and ran my own 35-document corpus instead.
  • Whether the other 26 workspace manifests share objectui#9012's behavioural-floor shape.
  • The exact byte count of the MCP footer strip (5203 / 5144); I read only that objectui#9012's stored body has no footer.

Attribution in prose, per this repo's AGENTS.md mitigation: generated by Claude Code in session session_01Jmxdo7bmeqCQHLSfmLVX9w (contract-review subagent agent-af3d2270df87bbe51).


Generated by Claude Code

… in it (objectui#8992)

Contract review returned REWORK on the changeset prose and the declared bump. The
code — the docblock, the `.extend()` collapse, the new pin and the parity row — was
re-measured by the reviewer and found correct, and is untouched here. Only
`.changeset/8992-user-actions-collapse-and-docblock.md` moves.

1. `patch` -> `minor`. Re-derived rather than taken on the reviewer's word, and every
   anchor found by content: `packages/types/CHANGELOG.md` records the IDENTICAL
   operation on this same file — `ListColumnSchema`'s local `.extend()` collapsing into
   a plain by-reference re-export — under `## 17.1.0` / `### Minor Changes`;
   `.changeset/8317-strip-imported-defaults.md`, the boundary ruling this change
   enrolls three more keys into, is `minor`; the sibling PR of this batch (objectui#8990)
   declares `minor`; and AGENTS.md states objectui's own breaking changes ship as
   `minor` with the break spelled out in the body. My own `tsc` probe, built from the
   two declarations side by side, confirms the break is real and producer-side: a value
   typed as the OLD output is not assignable to the NEW one (the three keys read as
   missing), while the reverse direction compiles. The probe is not vacuous — injecting
   a `@ts-expect-error` where nothing is wrong made the same run fail with TS2578, so
   the file was genuinely in the program.

2. ⛔ CORRECTING THIS COMMIT'S PARENT. The previous commit message, the changeset and
   the PR body all said the three keys lose their descriptions "which the protocol
   declares those keys without". THAT IS FALSE, and it is corrected here rather than by
   rewriting the parent (a force-push is not available to this branch). The protocol
   describes all three — `group` reads "Allow users to change record grouping from the
   toolbar. …" in the 17.3.0 and 17.4.0 tarballs. The real mechanism is objectui's own
   import boundary: `stripImportedDefaults` unwraps each `ZodDefault` with
   `.removeDefault()` and re-optionalises the inner node, and the description sits on
   the outer node it discards. Measured on this object: all ten defaulted keys read
   `description = undefined` after the strip, on both sides of the change, while
   `buttons` — the only member that never carried a default — keeps its description
   through it, which is the positive control. The OUTCOME sentence was right; the cause
   was invented.

3. "the OTHER EIGHT keys have declared that all along" -> SEVEN of the other eight.
   `buttons` is `z.ZodOptional[z.ZodArray[z.ZodString]]` and never declared a default.

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

Copy link
Copy Markdown
Collaborator Author

Reviewer's out-of-scope finding is filed as objectui#9034 — do not ride it into this PR.

The contract review's closing line was stripImportedDefaults drops .describe() on every unwrapped ZodDefault — a card, not a rider. It is now objectui#9034, with the seat's own re-measurement rather than the PR-scoped one:

  • 110 of 110 described ZodDefault members across the whole published @objectstack/spec 17.4.0 surface lose their description through the strip; 0 survive. (246 schema-shaped exports, 811 object shape members, 17 of 18 subpath exports loaded — the 18th is ./openapi.json.)
  • All 110 losses happen at .removeDefault(), not at the z.optional() re-wrap — one mechanism, because the protocol spells .describe() outside .default().
  • Pull is currently zero, measured and recorded on the card: nothing in objectui reads a zod node's .description in shipping code, and all five z.toJSONSchema emitters under metadata-admin/ import from @objectstack/spec directly, bypassing the boundary. That refutes the consumer I hypothesised, so types(imported-defaults): stripImportedDefaults drops the .describe() of every imported default — 110 of 110 described ZodDefault members lose it across spec 17.4.0, 0 survive #9034 is filed priority:p3 and is not scheduled.

The repair round dispatched for this PR is confined to the changeset bump and the prose corrections (items 1–3 of the verdict). The code — docblock, .extend() collapse, pin, parity row — was re-measured correct by the reviewer and is not being touched.

needs:contract-review stays on: the review that produced the verdict declared itself a SELF-REVIEW and does not count as the independent clause-② review.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3489.9 KB 3512.7 KB
Main entry chunk (gzip) 144.2 KB 350 KB
Entry file index-BctBumc6.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) 7.95KB 3.19KB
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.64KB 19.98KB
plugin-chatbot (index.js) 195.32KB 46.51KB
plugin-dashboard (index.js) 130.98KB 34.54KB
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.36KB 57.45KB
plugin-kanban (index.js) 46.03KB 14.30KB
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) 2.32KB 1.24KB
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

Contract review

Head reviewed: 2f430a3152f1a32a77a8b4ad8291f1405bf14586 — branch claude/issue-8992-useractions-collapse-and-docblock, 2 commits, 4 files; branch ref re-read immediately before posting, unchanged. Merge-base with origin/main at review time: 2596b1b8. Round 2: the round-1 record (comment 5624170546, on b4e86e41…) returned REWORK on the changeset grade and two prose claims; this record judges whether that rework was delivered and re-measures the whole diff rather than inheriting round 1.

Charter read at objectstack origin/main efa2533dd88a6c8304761253c9916f81699c3dc9 (contract-review.md, landing-operations.md, true-green.md, platform-readings.md, SKILL.md 〈入队与落地〉).

Tier fuse, read not asserted. CONTRACT_REVIEW_TIER = 'claude-fable-5-1' (objectstack origin/main scripts/pm/dispatch-gates.mjs:10507). My own transcript, located by a marker string I wrote first (subagents/agent-a14702d545cfdaa53.jsonl), carries harness per-message model stamps of one value only, claude-fable-5-1 — 30/30 at the first read, 70/70 re-counted immediately before posting. No fallback evidence, so the fuse does not void this record.

Instruments. Own blobless clone of the PR branch at the head sha under my scratchpad, pnpm install --frozen-lockfile (resolved @objectstack/spec@17.4.0, exactly one copy on disk); published tarballs @objectstack/spec 17.0.0 / 17.2.0 / 17.3.0 / 17.4.0 and @object-ui/types@17.6.0 via npm pack, imported with no workspace resolution; packages/types built at head; every rc captured to a file before any pipe. The shared checkouts were not touched; no file in the PR tree was edited; my probe files were untracked and are deleted.

Round-1 rework, item by item

demanded delivered on this head measured
1. changeset patchminor yes — '@object-ui/types': minor see ②
2. description-loss cause: protocol describes the three; objectui's boundary discards it yes — changeset item 1 now names .removeDefault() + re-optionalise as the cause probe: spec describes all three; head mirror description = undefined on all ten defaulted keys; buttons keeps its (positive control)
3. "the other eight" → seven of the other eight yes 17.6.0 .d.ts: seven ZodDefault, buttons ZodOptional
4. PR body: same two corrections, and "cancelled at the job bound" → 25m00s against a 20-minute bound, cause unread yes job 102989437972: 18:07:31Z → 18:32:31Z cancelled, Run tests step in_progress with no end; ci.yml:722 timeout-minutes: 20; re-run 102998639047 15m57s success

The repair commit touches exactly one file (.changeset/8992-user-actions-collapse-and-docblock.md, +40/−22) — "no source file moved in the repair" holds. One NEW miscount was written into the repaired text; see ① 5.

① Derived judgments — every accept-set and public-surface change the diff produces

1. Accept set of UserActionsSchema (and of ListViewSchema.userActions through it) — UNCHANGED; judged RIGHT. Extended (the exact pre-change .extend({…}) text from the merge-base side of the diff) and collapsed were rebuilt side by side in one vitest process from the resolved spec and parsed over my own 55-document corpus (every declared key in both polarities, wrong types, null, the full block, buttons variants, undeclared keys, the legacy showGroup spelling, a mixed declared-plus-undeclared document, and the non-objects null / [] / 'str' / 42 / undefined): CORPUS SIZE = 55 DIFFS = 0, comparing success, parsed output and refusal code / keys / path / message. Sentinel collapsed.omit({ group: true }) diverges on 7 of 55 — the comparison can see a difference. Control { sort: true } ACCEPTED, { zzUndeclared: true } REFUSED with the key named, on both. The head's exported UserActionsSchema is reference-equal to the collapsed build and not to the extended one. {} parses to {} on head (the raw spec writes ten defaults; the boundary strips all ten).

2. Key set — UNCHANGED, and now derived from the protocol by reference; judged RIGHT. Eleven keys on both sides, equal to UserActionsConfigSchema's. Not narrower than the protocol anywhere: keys, strictness and accept set are the spec's own after the strip. The widen ablation (.extend({ zzLocalWidening })) reddens the pin's key-set identity assertion (1 failed | 9 passed).

3. Strictness — UNCHANGED (catchall: never on spec, collapsed, extended and head; z.core.$strict in the emitted .d.ts on both sides); the corrected docblock is TRUE and the retired sentence was FALSE; judged RIGHT. Re-run myself against each published tarball, import('./dist/ui/index.mjs') with zod@4.4.3 the only resolvable dependency: 17.0.0 — 8 declared keys, {group:false} REFUSED unrecognized_keys ['group'], 1 issue; 17.2.0 — 8 keys, same; 17.3.0 — 11 keys, {group:false} ACCEPTED, {zzUndeclared:true} REFUSED with the key named, 1 issue; 17.4.0 — same as 17.3.0. Positive control {sort:true} ACCEPTED on all four. The loose ablation (.loose(), the world the old comment described) reddens all four refusal assertions (4 failed | 6 passed); source restored byte-identical to the HEAD blob 67f26263… after each leg, git diff HEAD empty. The new docblock's other claims: @object-ui/types declares ^17.3.0 (packages/types/package.json:96); 8 vs 11 keys as measured; the Option A ruling is dated 2026-08-22 (objectui#5435 comment 5377368469). The card's anchors did drift on this base: the false sentence sat at :607 and the declaration at :611 of the merge-base file, as the dev said.

4. Published .d.ts: group / hideFields / rowColor move z.ZodOptional<z.ZodBoolean>z.ZodDefault<z.ZodBoolean> — a producer-direction type break; judged RIGHT to make and correctly declared. Head build vs the published 17.6.0 declaration, tsc 6.0.3 over the two side by side: z.input assignable in both directions and {} assignable on both; reader direction (head output → old output) compiles; producer direction (old output → head output) TS2322 "Type 'boolean | undefined' is not assignable to type 'boolean'" for group; undefined refused on HeadOut['group']; the same break is reachable through the barrel-exported ListViewSchema (z.output<typeof ListViewSchema>['userActions'], TS2322). Controls: a @ts-expect-error placed where nothing is wrong fails the same program with exactly one TS2578; with every suppression removed the three TS2322s surface; with them in place the program compiles clean, so nothing unexpected is hidden. The destination shape is the ruled one — stripImportedDefaults is typed T in / T out (imported-defaults.ts:311-318, batch #90) and seven of the other eight keys already declare ZodDefault — and after the collapse the mirror's declaration IS the protocol's declaration, which is the direction the maintainer's principle points. UserActionsSchema itself is not in the ./zod barrel (index.zod.ts:288-310; 17.6.0 index.zod.d.ts:45 agrees); ListViewSchema is, and it is the one in-file consumer (objectql.zod.ts:716). In-repo: no z.output / z.infer over either schema outside tests; ListViewInferred is z.input; CI Type Check green on this head.

5. .describe() metadata of the three keys leaves the published zod object; judged RIGHT, and the cause is now stated correctly — with one new miscount. Measured per key: the spec describes all eleven (group: "Allow users to change record grouping from the toolbar. …"); after the strip all ten defaulted keys read description = undefined on head and on the pre-change strip output alike, while buttons — the only never-defaulted member — keeps its description through it. Pre-change, the extension re-described only the three with objectui's own strings ("Allow users to group records", …); those strings were objectui's, not the protocol's, so dropping them moves toward the protocol, and the general loss on every defaulted key is objectui#9034 (filed, p3). The dist marker reads 1 in the 17.6.0 tarball and 0 in the head build. "Nothing in this repository reads them" holds: every non-test z.toJSONSchema emitter (app-shell metadata-admin view-schema.ts, page-schema.ts, dashboard-schema.ts, report-schema.ts, package-schema.ts) imports from @objectstack/spec directly, and no shipping code reads a mirror node's .description. ⚠️ Miscount, changeset item 1, last sentence: "before, the local extension supplied descriptions the other ten defaulted keys did not have" — the ten defaulted keys INCLUDE the three; the other defaulted keys are seven (sort, search, filter, refresh, rowHeight, addRecordForm, editInline). The PR body carries the same sentence ("the other ten defaulted keys never had"). It is the same class of error round 1 corrected ("the other eight"). The sentence before it in the same paragraph states the true count ("all ten defaulted keys read description = undefined"), the outcome and the mechanism are right, and the miscount decorates a public-surface change judged right — so I grade it non-blocking rather than a rework item; the seat should know it ships into CHANGELOG.md as written.

6. zod-mirror-parity.test.ts EXCLUSIONS row kept, reason rewritten — judged RIGHT. UserActionsConfig is re-exported inside export type { … } from '@objectstack/spec/ui' at packages/types/src/index.ts:1284; no local restatement exists, so there is nothing for MIRRORS to pair, and deleting the row would fail the unaccounted-export census (:3318). Each claim in the new reason measured true (plain re-export; three keys declared from 17.3.0; TS name re-exported). The reason is only length-checked (:3360), so its truth rests on this review. Parity file green: 42/42 across it and the pin.

7. New pin user-actions-mirror-8992.test.ts — judged RIGHT and non-vacuous. 10/10 green in my clone; reddens in both ablation directions (items 2 and 3); each population asserted non-empty before use; the firing controls in both directions are real.

② semver — CONSISTENT

The changeset declares minor. Measured: a published-surface change in @object-ui/types (item 4 is non-assignable in the producer direction; item 5 removes metadata from a published object). Policy AGENTS.md:251: objectui's own breaking changes ship as minor with the break spelled out (and major is CI-refused by check-changeset-no-major.mjs, the "Changeset Bump Policy" check, green here). Precedent: the identical operation on the same file — ListColumnSchema's .extend() collapsing into a by-reference re-export — is recorded under ## 17.1.0 / ### Minor Changes (packages/types/CHANGELOG.md:1775-1777, entry at :2214); .changeset/8317-strip-imported-defaults.md is minor; sibling PR #9021 (objectui#8990) is minor. The break is spelled out in the changeset body as policy requires. minor is the right grade; patch was under-declared.

③ Boundary flags — every dev flag and open_questions entry

open_questions: [] — nothing to answer there. Dev flags and out-of-scope findings, one by one:

  • @object-ui/core's ^17.2.0 floor → objectui#9012, not bumped here — ACCEPTED. A published-surface change to a different package; this diff moves that coupling in neither direction; the floor that carries the three keys is @object-ui/types' own ^17.3.0, which the new docblock names (8 keys at 17.2.0, 11 at 17.3.0, measured). One residual inexactness in the PR body, unchanged from round 1 and not a rework item: "its workflow also only triggers on changes to the gate script itself" — spec-range-floors.yml also runs nightly (11 4 * * *), on workflow_dispatch, and the script runs blocking inside changeset:publish (package.json:119). The conclusion ("no gate would hold a bump") still holds, because the gate judges symbol presence and UserActionsConfigSchema exists in both versions.
  • Six sibling EXCLUSIONS rows with the stale template reason — restraint ACCEPTED. All six are plain stripImportedDefaults(Spec…) re-exports (objectql.zod.ts:92-138) and all six rows still read "the local .extend(…) adds renderer props" (zod-mirror-parity.test.ts:3011-3022). The round-1 correction is delivered: the PR body no longer claims fix(types): ObjectKanbanSchema.groupBy is optional, as the protocol declares it (objectui#8990) #9021 holds the parity file (its file list: objectql.ts, objectql.zod.ts, no parity test) and gives scope as the reason. Taker named (the next collapse card in objectql.zod.ts).
  • Card anchors drifted three lines — CONFIRMED (:607 / :611 at the merge-base).
  • vitest list --shard does not partition; sequencer-derived membership puts the parity file in the shard that stalled — NOT re-measured by me; the PR body's disposition ("plausible, not proven"; cause unread) is the honest one and stands. Attempt-1 timings re-read from the job itself: exactly 25m00s, cancelled, test step never ended; attempt 2 on the same commit 15m57s.
  • MCP issue-create strips the footer — no card needed; already documented at AGENTS.md:445 (section) / :454 (item ④) / :462 (prose-attribution mitigation).
  • The two corrected claims (description cause; seven of eight) — DELIVERED, measured under ① 5 and ① 4.
  • Round 1's own out-of-scope finding — filed as objectui#9034 (open, pm:queue, p3); not ridden into this PR, correctly.
  • Card report inexactness (not in the PR): "feat(scripts): gate documented SchemaExample ids against the catalog registry #9009 landed on origin/main at 18:17:42Z" — that is the merge commit's date; the PR's merged_at is 18:52:15Z. Either way it lies between the two CI runs (18:07:28Z and 19:31Z), so the "35th check" explanation holds.
  • The two "what moves on the published surface" flags addressed to this reviewer — DISPOSED under ① 4 and ① 5: both are the ruled boundary shape, both move the mirror toward the protocol, both are graded and spelled out under ②.

CI on this head — terminal and green, all checks

35 check runs on 2f430a31…: 32 success, 3 skipped (Test (coverage), Test (coverage shard …), dependabot), 0 failure, 0 cancelled, 0 pending; combined status success. All four test shards, Type Check, Lint, Build & E2E, Build Docs, Changeset Declaration, Changeset Bump Policy, Governed Surface Queue Guard among them. The 35th run is Doc Example Id Check from #9009 (merged 18:52:15Z, between the two runs).

Independence

Implemented-by: claude/issue-8992-useractions-collapse-and-docblock
Reviewed-by: session_01Jmxdo7bmeqCQHLSfmLVX9w

Implementer recorded by BRANCH per contract-review.md:36 — the dev ran mode:subagent (both commits authored from subagent transcript agent-ac2435341307b8792, a child of the dispatching session) and a subagent has no session of its own. Reviewer recorded as the seat that produced the verdict per :35 — the domain:spec seat, session session_01Jmxdo7bmeqCQHLSfmLVX9w, through its context-isolated review subagent agent-a14702d545cfdaa53 (harness model claude-fable-5-1).

Same-session test (:37, "两者同 session ⇒ 报 SELF-REVIEW"), applied literally to the two identities as :35-36 define them: the implementer identity is a branch and carries no session; the reviewer identity is a session id. A branch is not a session, so the test does not return "same session" — this record is NOT a SELF-REVIEW under the charter's text. For the record, stated plainly rather than hidden: the dev subagent and this review subagent are both children of the same dispatching session, and round 1 reported SELF-REVIEW by widening the test to that dispatching session. Under the charter that widening is not the test: :33 has the spec seat itself spawn the context-isolated review subagent, :36 records a subagent dev by branch precisely because it has no session, and :53 says that in mode:subagent a session reading measures the dispatching session and is not mutual attestation. The independence the charter relies on in this configuration is context isolation (:33-34), which I attest to: I was fed the card, the existing rulings and the PR body; the seat's dispositions comment lives on the card and was therefore visible, the dispatch order likewise; the dispatching seat's conclusions about this change and about round 1 were not given to me and I did not seek them; every judgment above was re-measured with a control, none inherited.

Verdict

The code was right in round 1 and is unchanged; the grade is now right; every round-1 rework item is delivered and measured. One new miscount in the changeset prose (① 5) and one residual inexactness in the PR body (③, first bullet) are named and graded non-blocking. Landing is the seat's act, per contract-review.md:38-45 and landing-operations.md.

VERDICT: PASS

What I did not measure

  • The cause of the attempt-1 shard-2 cancellation — the job log was not fetched; I read the job's step timeline only.
  • check-clause2-carriers.mjs --pair 9017 and check-widening-tells.mjs — landing pre-checks, the seat's act.
  • Downstream type-check beyond CI's Type Check job; no external consumer code was inspected.
  • The dev's 33-document corpus as it built it (I built my own, 55 documents) and its dist-marker 0 → 2 → 0 run with rebuilds (I built head once: marker 0 in head dist, 1 in the 17.6.0 tarball).
  • A base-side build of packages/types — the published 17.6.0 declaration served as the pre-change side; the merge-base source was verified to carry the exact .extend({…}) the diff removes.
  • vitest list --shard inertness and the sequencer-derived shard membership.
  • ListColumnSchema's .extend history ("two commits touch that spelling") — my clone is depth-limited.
  • Whether the other 26 workspace manifests share objectui#9012's floor shape; objectui#9012 / types(imported-defaults): stripImportedDefaults drops the .describe() of every imported default — 110 of 110 described ZodDefault members lose it across spec 17.4.0, 0 survive #9034 bodies beyond title, state and labels.
  • The MCP footer-strip byte counts (5203 / 5144).

Attribution in prose, per this repo's AGENTS.md mitigation: generated by Claude Code in session session_01Jmxdo7bmeqCQHLSfmLVX9w (round-2 contract-review subagent agent-a14702d545cfdaa53).


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Provenance — carriers cleared, landing

Clause-② contract review: VERDICT: PASS — record 5624735304 (#9017 (comment)), judged on head 2f430a3152f1a32a77a8b4ad8291f1405bf14586, which is still the head at this write. Round 2, after the round-1 REWORK whose four items were all delivered; the repair commit touches only the changeset file (+40/−22).

Reviewer ran at CONTRACT_REVIEW_TIER = 'claude-fable-5-1' (scripts/pm/dispatch-gates.mjs:10507), transcript-verified 70/70 stamps immediately before posting, one distinct value, no fallback. Charter read at objectstack origin/main efa2533dd88a6c8304761253c9916f81699c3dc9.

Independence — NOT self-review:

Implemented-by: claude/issue-8992-useractions-collapse-and-docblock
Reviewed-by: session_01Jmxdo7bmeqCQHLSfmLVX9w

Applied literally to the identities as contract-review.md:35-36 define them: the implementer is a mode:subagent dev, which the charter records by its branch because such a dev carries no session; the reviewer is a session id. Round 1 reported SELF-REVIEW by widening the test to the dispatching session, which :33 (the spec seat spawns the isolated review subagent), :36 and :53 do not support. Both parties are subagents of one dispatch session and the record says so plainly. This is the second review to reach that reading independently today.

Landing pre-checks — all measured just now

check reading
① in-seat clause-② review PASS on record, 同形, ceiling tier ✅ record 5624735304 on the head
check-clause2-carriers.mjs --pair 9017 rc 0 — both carriers agree, declaration readable in the fixed spelling
check-widening-tells.mjs --declaration yes --files rc 0 — a yes already routes to review, so a tell decides nothing
③ every check on the head, ⛔ not a required subset 35 check runs, 32 success + 3 skipped, 0 failed, 0 pending; total_count 35 = array length 35; combined commit status success
governed surface check-governed-merges.mjs --pr objectstack-ai/objectui#9017 → 0 of 4 paths hit the register ⇒ NOT governed

⚠️ Gate-usage note for whoever reads this next: a bare --pr N on check-governed-merges.mjs resolves the number against the repo of the checkout you run it from (runPullModeremote.origin.url). Run from an objectstack clone, --pr 9017 reads objectstack's #9017 and still prints a confident ✅. Neither PM_SWEEP_REPO nor --repos redirects it — the qualified form --pr owner/repo#N does, and is what produced the row above. I took the bare reading first, discarded it, and re-took it qualified.

⚠️ Adopted verbatim, including one non-blocking finding that ships as written

The reviewer found a new miscount in the repaired changeset and PR body: "the other ten defaulted keys" — the correct number is seven (the ten include the three this PR moves). It graded this non-blocking because the preceding sentence carries the true count and the mechanism and outcome are right, and returned PASS.

The charter gives the dispatching seat exactly two moves on a subagent verdict — adopt verbatim, or void it entirely — and ⛔ never rewrite one. Sending another repair round would move the head and void this PASS, costing a full ceiling-tier review to fix one word. So: adopted, landing, and the changeset text is being corrected by a separate follow-up before release, since .changeset/*.md is release-note input and this would otherwise reach the CHANGELOG as written.

Other dispositions recorded by the reviewer: spec-range-floors.yml also runs nightly / dispatch / changeset:publish, so the PR body's "only on gate-script changes" is inexact (conclusion unaffected); objectui#9009 "landed 18:17:42Z" is the merge commit date, merged_at is 18:52:15Z; the reviewer's own out-of-scope finding is filed as objectui#9034.

Both carriers (needs:contract-review on this PR and on card #8992) are stripped in this same act, and the PR flipped ready and enqueued.

⛔ Base is not being hand-merged. The merge queue rebuilds each PR on current main itself; hand-merging would move the head and void the record above.


Generated by Claude Code

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

Projects

None yet

2 participants