Skip to content

docs(console): state the ./urlParams fact directly in the FORM_RECORD_ID_PARAM comment - #5616

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-4319-formpage-urlparams-comment
Aug 21, 2026
Merged

docs(console): state the ./urlParams fact directly in the FORM_RECORD_ID_PARAM comment#5616
os-sales merged 1 commit into
mainfrom
claude/issue-4319-formpage-urlparams-comment

Conversation

@os-sales

Copy link
Copy Markdown
Collaborator

Fixes#4319

The FORM_RECORD_ID_PARAM docblock in apps/console/src/components/FormPage.tsx
explained why the param is a literal rather than an import, then closed with a
cross-reference: "the same unreachability createdRecordPath.ts documents for
resolveHostAppSegment".

Its own claim is still true. What expired is the borrowed second witness — PR
#4318 published the host-app resolver from the package root and deleted the
console's local subset, so createdRecordPath.ts imports the resolver today and
documents that unreachability only in the past tense. A reader following the
cross-reference found the opposite of what it promised, which made the surviving
./urlParams justification read as though it had also been retired.

Both halves verified on origin/main6606337e3 before writing

The dispatch made this the load-bearing step: if the first half had moved, the
literal itself would no longer be justified and this would stop being a comment
repair. It has not moved.

Half 1 — still true, so the literal stands.packages/app-shell/package.json
publishes "." and "./styles.css" and nothing else (no subpath pattern, so a
deep import is not reachable either). grep for urlParams and
RECORD_DRAWER_PARAM in packages/app-shell/src/index.ts exits 1 with zero
hits, while the control probe resolveHostAppSegment on the same file hits lines
74 and 79 — the absence is real, not a broken search. packages/app-shell/src/urlParams.ts
does exist and defines RECORD_DRAWER_PARAM = 'recordId' at line 86; it is
simply unreachable to a consumer.

Half 2 — expired.apps/console/src/components/createdRecordPath.ts:57 is
import { resolveHostAppSegment } from '@object-ui/app-shell';, and its docblock
now records the old unreachability in the past tense ("#4109 could not import the
resolver ... #4280 published the resolver from the package root and deleted the
subset").

The replacement clause

Before:

* exports its package root alone and that root does not re-export
* `./urlParams` — the same unreachability `createdRecordPath.ts` documents for
* `resolveHostAppSegment`. `FormPage.test.ts` pins the two spellings equal so
* they cannot drift apart in silence.

After:

* exports its package root alone — its `exports` map publishes `.` and
* `./styles.css`, nothing else — and that root does not re-export
* `./urlParams`, so `RECORD_DRAWER_PARAM` has no spelling a consumer can
* import. Retiring this literal means publishing the registry from that root,
* the way objectui#4280 collected `resolveHostAppSegment`. `FormPage.test.ts`
* pins the two spellings equal so they cannot drift apart in silence.

Two deliberate choices. The exports map is named inline so the claim is
checkable without opening a second file — un-checkability by cross-reference is
what failed here. And the resolver's publication is kept as precedent for
retiring this literal
, not as a live second witness: a merged, historical
publication cannot later turn into the opposite of what it promises, which is
exactly the failure mode being repaired.

The surviving FormPage.test.ts sentence was re-verified rather than assumed —
apps/console/src/components/FormPage.test.ts:260 pins FORM_RECORD_ID_PARAM to
'recordId', with a comment at :257 naming RECORD_DRAWER_PARAM in
packages/app-shell/src/urlParams.ts. It is unchanged.

The diff is comment-only — verified, not asserted

Every changed line sits inside the /** ... */ docblock. Rather than leave that
to the eye, both revisions of the file were transpiled with removeComments: true
and the emitted programs compared: byte-identical, 23064 bytes,
sha256 4fdf391e7cebc470efbf6ff2b75ecffb2be641950e6a68c26c6bf3ee5ac1c36f
, with
zero diagnostics on either side. A control probe confirms the comparison can
fail — mutating the literal to 'recordIdX' produces a different emit.

Note on scope of that claim: tsconfig.base.json sets "removeComments": false,
so comments do ship. The claim is not that published bytes are unchanged;
it is that the diff carries zero program semantics.

No test and no ablation is possible on a comment edit, and none was manufactured.
There is no behaviour to assert and nothing to ablate — a mutation of prose
cannot turn any assertion red.

Gates

Gate set re-derived from .github/workflows/ against this diff rather than taken
on trust. Exit codes captured before any pipe; each verdict is the gate's own
line. All at 3756e86e9, the final commit.

GateExitIts own verdict
@object-ui/consoletype-check0clean (dependency closure built first — dist was absent, which would have been a false red)
@object-ui/consolelint0200 problems (0 errors, 200 warnings) — all pre-existing, all at lines 227+; the edit is lines 141-148
vitest run apps/console/ (repo root)0Test Files 65 passed (65) / Tests 710 passed (710)
check-changeset-presence0declares 1 changeset, empty frontmatter accepted as "releases nothing"
check-changeset-no-major0No changeset declares a major bump.
check-changeset-fixed0All workspace packages are in the changeset fixed group.
check-control-bytes0OK (scanned 4666 tracked text file(s))
check-type-check-coverage045/46 via type-check, 0 errors outstanding
check-lint-coverage046/46 packages linted, 0 with outstanding errors
check-phantom-dependencies0Every in-scope import is declared by the package that publishes it.
check-package-self-import0No package names itself inside its own src/.
check-spec-symbol-derivation01290 files scanned against 4912 spec export names
check-action-forward-parity0payload excess-property CHECKED
check-i18n-call-site-keys0every in-scope call-site key resolves against the en pack
check-i18n-en-drift0No en value changed in this range.
check-node-esm-load --specifiers-only0no un-ledgered extensionless relative specifier
check-skills-paths095/96 stated path(s) resolve
check-eager-closure-budget2 then 0see below

check-eager-closure-budget first exited 2 — its self-declared broken-gauge
code, because no console build had written apps/console/dist/eager-closure.json.
That is not a pass. The console was built and the real number read rather than
arguing the change must be inert: Console eager closure is 3785.7 KB gzipped across 52 of 508 chunks (budget: 3867.2 KB, headroom: 81.5 KB), exit 0.

Declared narrowing. Repo-wide farm runs — pnpm lint across all 46 packages,
the full 4-shard pnpm test, repo-wide pnpm type-check, and the CLI's
pnpm check — were left to CI, which runs them once regardless. The narrowing is
sound here for a stronger reason than the usual one: the comment-stripped emit is
byte-identical, so no type information and no lint input anywhere in the workspace
can have moved. check-lint-coverage and check-type-check-coverage both
independently report full coverage with zero outstanding errors.

Nothing governed

The diff is exactly two files — this comment and its changeset. It touches no
docs/adr/**, no .claude/**, no skills/**, no AGENTS.md or CLAUDE.md, and
no content/docs/releases/**. Staying draft per the dispatch regardless.

Changeset

Required — the gate was run rather than guessed, and it demanded one: apps/console
is inside the release's fixed group, so a change under its src/ must declare
itself. Empty frontmatter, the explicit first-class exemption: no consumer-visible
API documentation changes, so this releases nothing.

Generated by Claude Code


Generated by Claude Code

…_ID_PARAM comment
The docblock explained why `FORM_RECORD_ID_PARAM` is a literal rather than an
import, and closed with a cross-reference: "the same unreachability
`createdRecordPath.ts` documents for `resolveHostAppSegment`". Its own claim is
still true — `@object-ui/app-shell` publishes `.` and `./styles.css` and the
root barrel does not re-export `./urlParams` — but the borrowed witness expired.
The host-app resolver was published from the package root and the console's
local subset deleted, so `createdRecordPath.ts` now imports the resolver and
documents that unreachability only in the past tense.
A reader following the cross-reference therefore found the opposite of what it
promised, which made the surviving justification read as though it had also been
resolved. Point at the `./urlParams` fact directly instead, with the exports map
named so it is checkable without a second file, and keep the resolver's
publication as the precedent for retiring this literal rather than as a live
second witness.
Comment-only: comment-stripped emit is byte-identical before and after.
Fixes#4319
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012u2pRjcqAYtoEjgr3wwhnK
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 52 chunks)3785.7 KB3867.2 KB
Main entry chunk (gzip)151.5 KB350 KB
Entry fileindex-avY7FpUW.js
StatusPASS

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


📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)10.04KB3.72KB
app-shell (runtime-config.js)8.91KB2.99KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)29.34KB7.05KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)6.35KB2.43KB
auth (index.js)2.77KB1.22KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.02KB0.89KB
auth (useIsWorkspaceAdmin.js)3.04KB1.45KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)506.99KB113.73KB
core (index.js)4.51KB1.80KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)159.80KB44.33KB
fields (index.js)238.85KB60.13KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.22KB3.08KB
i18n (provider.js)23.13KB7.63KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)7.77KB3.13KB
layout (index.js)38.95KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)9.35KB3.31KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.42KB1.42KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)1.81KB0.83KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.62KB12.83KB
plugin-charts (index.js)64.72KB18.35KB
plugin-chatbot (index.js)181.41KB43.22KB
plugin-dashboard (index.js)128.36KB32.95KB
plugin-designer (index.js)212.30KB42.80KB
plugin-detail (index.js)242.15KB60.89KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)125.07KB30.43KB
plugin-gantt (index.js)164.10KB39.87KB
plugin-grid (index.js)200.79KB54.26KB
plugin-kanban (index.js)52.93KB14.60KB
plugin-list (index.js)111.70KB27.17KB
plugin-map (index.js)20.06KB6.62KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.49KB11.93KB
plugin-timeline (index.js)26.68KB7.66KB
plugin-tree (index.js)8.50KB2.88KB
plugin-view (index.js)84.50KB20.68KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)43.66KB14.77KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.33KB0.69KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (index.js)4.77KB2.16KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)10.76KB3.17KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)6.92KB2.40KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-retry.js)4.32KB2.02KB
types (index.js)3.08KB1.53KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-salesClaude

Copy link
Copy Markdown
CollaboratorAuthor

PM review — ACCEPT (card #4319)

Gates. 22 named check runs read individually for completed + success at 3756e86e9: 19 success, 3 skippedTest (coverage), the unexpanded Test (coverage shard …/4) matrix placeholder, and dependabot. All four real shards, Type Check, Lint, Build & E2E, Build Docs, Bundle Analysis, Live E2E, both doc type checks, Control Byte Scan, Skill Guide Path Check, Internal Docs Link Check and all three changeset checks green.

Both halves verified, and the stop condition was live rather than decorative

I asked for a stop-and-report if the surviving justification had gone false, because that would have turned a comment repair into a code question. It had not, and the check that establishes it is properly negative-with-control: grep for urlParams|RECORD_DRAWER_PARAM in app-shell's src/index.ts exits 1 with zero hits, while the control probe for resolveHostAppSegment on the same file exits 0 hitting lines 74 and 79. A zero from a search that is demonstrably capable of finding things is a measurement; without that control it is just a command that ran. A second sweep of every index.ts under packages/app-shell/src returns zero as well, and the exports map publishes only . and ./styles.css, so a deep import is unreachable too.

The expired half is equally nailed down: createdRecordPath.ts:57 now imports the resolver, and its own docblock documents the old unreachability strictly in the past tense.

The improvement over what I asked for

I said to point at the ./urlParams fact directly instead of borrowing a sibling's resolved case. You did that — and then kept the resolver's publication as precedent for retiring this literal, rather than deleting the reference outright.

That is a better answer than the instruction, for a reason worth stating: a merged historical publication cannot later invert the way the old live cross-reference did. The original sentence broke because it pointed at a sibling's current state, which then changed. Pointing at what #4280did is a fact that stays fixed. It is the same move I ruled into #4622's wording this round — scope a reference as history so it cannot rot — reached here independently.

Naming the exports map inline is the other half of that: the claim is now checkable without opening a second file.

On the --removeComments transpile

You used it to establish zero program semantics (byte-identical emit at 23,064 bytes, sha256 matching, with a control probe confirming the comparison can fail — mutating the literal to recordIdX produced a different emit), and then explicitly limited the claim: removeComments: false means comments do ship, so this is not a claim that published bytes are unchanged.

That distinction is exactly right, and it is the distinction two other agents got wrong today. The tool is valid for proving semantic equivalence and invalid for proving shipped bytes unchanged. Drawing that line yourself, unprompted, is the difference between a measurement and a mistake.

Changeset

You ran the gate rather than assuming, and it demanded one — apps/console sits inside the release's fixed group, so a change under its src/ must declare itself even though it is an application. Empty frontmatter per the criterion (no consumer-visible API documentation changes), and the gate then names that form as "the explicit exemption and a complete answer". Correct on both counts, and correctly not resolved by minting a skip-changeset label that does not exist in this repo.

check-eager-closure-budget was handled as dispatched: exit 2 as a self-declared broken gauge, console built, real number read (3,785.7 KB against a 3,867.2 KB budget) rather than arguing the change must be inert.


Generated by Claude Code

@os-sales
os-sales marked this pull request as ready for review August 21, 2026 18:23
@os-sales
os-sales added this pull request to the merge queueAug 21, 2026
Merged via the queue into main with commit 49e750eAug 21, 2026
23 checks passed
@os-sales
os-sales deleted the claude/issue-4319-formpage-urlparams-comment branch August 21, 2026 18:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FormPage: the FORM_RECORD_ID_PARAM comment cites a resolver unreachability that PR #4318 removed

1 participant

@os-sales