Repository navigation
feat(app-shell,types): cloud:plan-status marks the organization's current plan card (objectui#10919) - #10990
Conversation
…rent plan card
The Cloud pricing page is static metadata and cannot see the organization's
plan. `cloud:plan-status` reads it from the org-scoped entitlements summary
(through `useEnvironmentEntitlements`) and renders a "Current plan" badge on
the card whose `properties.plan` equals it, and nothing otherwise.
- app-shell: the widget, registered as `cloud:plan-status` only
(`namespace: 'cloud'`, `skipFallback: true`), wired into the barrel and the
`sideEffects` array.
- types: a local `CloudPlanStatusSchema` arm on `AnyComponentSchema` - a
required strict `properties: { plan }` bag, both content channels refused.
- cli: `known-schema-types.ts` regenerated; the namespaced ratchet stays 397.
- i18n: `cloudPlanStatus.current` in all ten locale packs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015AUunPkX7UTkCH9e7AdZo1
…o explicit any Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015AUunPkX7UTkCH9e7AdZo1
…'s warning The objectui#10928 population pin requires every reads-NEITHER refusal to carry the parser-tier clause; the registration declares no `children` input, so `validateTree` does warn `not-a-container` for this node. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015AUunPkX7UTkCH9e7AdZo1
…oud-plan-status Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015AUunPkX7UTkCH9e7AdZo1
|
changeset-claim-re-read
|
✅ Console Performance Budget
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
Size Limits
|
|
Contract reviewServed-tier: Card #10919 · PR #10990 · ① Derived judgmentsThe arm Refusal codes and paths, as The registry key — right, and better than the precedent. Widget behaviour — it never guesses. Hook reuse and the envelope — right. Exports and wiring — right. i18n — complete. Shipped prose, checked sentence by sentence against the diff and the named inputs.
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
…objectui#10992 for the Spec Main Shape Gate) Claude-Session: https://claude.ai/code/session_015AUunPkX7UTkCH9e7AdZo1 Co-authored-by: Claude <noreply@anthropic.com>
❌ Console Performance Budget
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. Which half objected:
📦 Bundle Size Report
Size Limits
|
|
From
Details are on the card: Generated by Claude Code |
…objectui#10996's eager-closure re-pin) Claude-Session: https://claude.ai/code/session_015AUunPkX7UTkCH9e7AdZo1 Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
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
Size Limits
|
…re-qualify objectstack#3546 (objectui#10803, batch 8) (objectstack-ai#11012) Fixes objectstack-ai#10803 Clause-②: no Dispatched implementation of the `domain:ui` seat objectstack-ai#1 claim (comment `5873413663`) on objectui#10803, batch 8, session `https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk`. Citations only: no sentence's claim moves, the one edited pending changeset keeps a byte-identical frontmatter, and no runtime string moves, so the only new changeset is an empty-frontmatter sweep. No test pins any changed text (**The literal-anchor sweep**), so no test file is edited. This batch carries release `5872268200`'s two lists (the seat's ruling A in ACCEPT `5870953708`, which review `5870922323` read the same way): - the **10 in-class lines that cite a dead objectstack number written bare**: `objectstack-ai#9934` seven times, "PR `objectstack-ai#6281`" twice, "objectstack PR `objectstack-ai#8452`" once; - the **6 live-but-wrong `objectstack-ai#3546` lines** that mean objectstack#3546: five written bare, one written with this repository's name. Numbers are written in code spans in this body when they are the dead or wrong spelling, so GitHub does not link them to the unrelated objectui items they would resolve to. ## Why `Fixes` Both lists read **0** after this batch (**Census**), and the closure census over the card's two classes (pending `.changeset/*.md` prose and non-test `packages/*/src`) finds nothing else dead: - **Bare numbers**, batch 7's instrument (printed under **Census**): 965 distinct at the branch point, 961 at this head. The 916 between 100 and 25000 at the branch point were each read once as objectstack issues: 877 answer 200 and 39 answer 404. The 39 were read again as objectstack issues and as pulls (404 each) and as objectui issues (200 each). Three of them are this batch's (`objectstack-ai#9934`, `objectstack-ai#6281`, `objectstack-ai#8452`), gone at this head. The other 36 remain, and each sentence was read: every one cites an objectui card or pull request, and each answers 200 in objectui with the subject its sentence names. - **The instrument's blind spot** (a number written after `/`, `-` or `.`, as in `objectui#9469/objectstack-ai#8649`): 46 numbers between 100 and 25000 appear only in that form at this head. Read as objectstack issues: 45 answer 200; one answers 404, `objectstack-ai#8649`, which sits in two lines as `objectui#9469/objectstack-ai#8649` and means objectui#8649 (200). - **Qualified citations** at this head: every distinct `objectui#N` and objectui issue or pull URL (2723 numbers) answers 200; every distinct `objectstack#N` and objectstack issue or pull URL (345 numbers) answers 200, except objectstack#14026, which answers 301 (transferred to objectui#10102, re-qualified by batch 6). So nothing dead is left in the card's classes, and nothing in them resolves to the wrong repository as far as these instruments see. **Acceptance notes** 1 and 2 record what is outside the classes, and what could not be measured. ## Premise, re-measured on `origin/main` `665025908` (the branch point) - **The three dead numbers.** objectstack `issues/N` and `pulls/N` answer 404 for 9934, 6281 and 8452 (the in-range census read each as an issue a second time). Lit controls in the same run: objectstack issues 3546, 3391, 5775 and 8326 answer 200; objectstack `pulls/13267` and `pulls/16920` answer 200. - **What they resolve to here.** objectui `objectstack-ai#9934` is a CI finding about the `needs:contract-review` label; objectui PR `objectstack-ai#6281` is an app-shell Studio grid change; objectui `objectstack-ai#8452` is a `not_contains` operator bug. None is what its sentence meant. - **`objectstack-ai#3546`, both repositories.** objectstack#3546 (200) is "detail/form 面的 edit/delete 按钮接入服务端 effective 操作集", the effective operation set on detail and form surfaces, which each of the six sentences describes. objectui `objectstack-ai#3546` (200) is the missing-i18n-keys card, unrelated. The 96 `objectui#3546` lines in the locale packs and the "slice seven" line in `.changeset/3880-es-done-listo.md` are that i18n card, and are untouched. - **The landing commits**, each read over REST (`commits/SHA`) and checked with `compare/SHA...main`, which answers `behind_by 0` for each, i.e. each is an ancestor of objectstack `main`: - `79c46da90`, "feat(contract): producer-side user-facing marking for hook refusal messages — userMessage channel (objectstack-ai#9934) (objectstack-ai#9992)", 2026-08-19: `ApiErrorSchema.userMessage`, and the dispatcher door (`packages/runtime/src/http-dispatcher.ts`) emits it as a declared sibling of `code` and `message`; - `85ec26d28`, "spec: SDUI props — enforce or remove (objectstack-ai#5775) (objectstack-ai#6281)", 2026-08-07: retires `page:card.body` in favour of `children`, with an ADR-0087 D2 conversion; - `27358d517`, "feat(spec,rest): add batch recordIds to security/explain (objectstack-ai#8326) (objectstack-ai#8452)", 2026-08-13. - These are the landings release `5872268200` names; review `5870922323` read all three against their messages. Batch 7 already cites `79c46da90` and `85ec26d28` at other sites in these classes. - The one edited changeset, `.changeset/7980-agent-key-envelope-read.md`, is pending: it is present in `.changeset/` on `main`. This checkout is not shallow. ## Census (the enumeration pin for this batch) The two lists (REF = a commit or tree): ``` git grep -nP '(?:^|(?<=[^\w#&/.\-]))#(9934|6281|8452|3546)(?![0-9A-Za-z_])' REF -- '.changeset/*.md' 'packages/*/src/**' ':!**/__tests__/**' ':!**/*.test.*' ':!**/*.spec.*' ':!**/test/**' ':!**/tests/**' | wc -l git grep -n 'objectui#3546' REF -- '.changeset/*.md' 'packages/*/src/**' ':!**/__tests__/**' ':!**/*.test.*' ':!**/*.spec.*' ':!**/test/**' ':!**/tests/**' ':!packages/i18n/src/locales/**' | wc -l ``` | REF | bare four | `objectui#3546` outside the locale packs | |:--|:--|:--| | `665025908` (branch point) | **15**: `objectstack-ai#9934` 7, `objectstack-ai#6281` 2, `objectstack-ai#8452` 1, `objectstack-ai#3546` 5 | **2**: the `plugin-detail` line and the i18n card's own line in `3880-es-done-listo.md` | | `8fe632d96` (this head) | **0** | **1**: the `3880-es-done-listo.md` line, which is the i18n card and stays | - Lit controls at this head: `objectstack#3546` reads 10 lines against 3 at the branch point (the 6 re-qualified here, and one in this PR's sweep changeset); the 96 `objectui#3546` lines in the locale packs are unchanged. - The bare-number instrument, batch 7's (the census of **Why `Fixes`**): ``` git grep -hoP '(?:^|(?<=[^\w#&/.\-]))#\d+(?![0-9A-Za-z_])' REF -- '.changeset/*.md' 'packages/*/src/**' ':!**/__tests__/**' ':!**/*.test.*' ':!**/*.spec.*' ':!**/test/**' ':!**/tests/**' | sort -u | wc -l ``` 965 at the branch point, 961 at this head: exactly `objectstack-ai#3546`, `objectstack-ai#6281`, `objectstack-ai#8452` and `objectstack-ai#9934` are gone, and nothing is new. The blind-spot reading replaces the lookbehind class with `[^\w#&]`. ## Citation form - **An objectstack commit** is written as batches 6 and 7 wrote their stand-ins: objectstack and the 9-character backticked sha of the commit on objectstack `main` that landed the change the sentence rests on. - **A bare `objectstack-ai#3546`** becomes `objectstack#3546`, including inside the `[objectstack-ai#3546]` tags that open four comments; the one written with this repository's name is re-qualified the same way. ## Mapping | site | before | after | |:--|:--|:--| | `.changeset/7980-agent-key-envelope-read.md` | "(the `objectstack-ai#9934` channel, whose presence *is* the marking)" | "(the objectstack `79c46da90` channel, …)" | | app-shell `index.ts`, the note above the `readEnvelopeFailureText` export | "(the `objectstack-ai#9934` channel, which rides through the 5xx prose withhold untouched)" | "(the objectstack `79c46da90` channel, …)" | | app-shell `utils/apiErrorEnvelope.ts`, the `userMessage` bullet | "the END USER (`objectstack-ai#9934`)" | "the END USER (objectstack `79c46da90`)" | | app-shell `utils/apiErrorEnvelope.ts`, the `errorFromThrown` bullet | "… has emitted the channel since `objectstack-ai#9934`" (a quotation) | "… has emitted the channel since [objectstack `79c46da90`]" (**Special cases** 1) | | app-shell `PackageFormDialog.tsx`, `readEnvelopeUserMessage`'s docblock | "(`error.userMessage`, `objectstack-ai#9934`)" | "(`error.userMessage`, objectstack `79c46da90`)" | | app-shell `StudioDesignSurface.tsx`, the package-switcher failure read | "the producer's `objectstack-ai#9934` channel" | "the producer's channel (objectstack `79c46da90`)" | | app-shell `packages-io.ts`, `duplicatePackage`'s failure arm | "has emitted since `objectstack-ai#9934`" | "has emitted since objectstack `79c46da90`" | | components `containers.tsx`, the `PageCardRenderer` `body` read | "objectstack#5775 (PR `objectstack-ai#6281`, ADR-0087 D2)" | "objectstack#5775 (objectstack `85ec26d28`, ADR-0087 D2)" | | components `containers.tsx`, the `card` registration's `children` input | "objectstack#5775 (PR `objectstack-ai#6281`)" | "objectstack#5775 (objectstack `85ec26d28`)" | | plugin-grid `hooks/useRecordCrudVerdicts.ts` | "shipped in objectstack PR `objectstack-ai#8452`" | "shipped in objectstack `27358d517`" | | app-shell `RecordDetailView.tsx`, two comments | "[`objectstack-ai#3546`] …" | "[objectstack#3546] …" | | app-shell `RelatedRecordActionsBridge.tsx` | "[`objectstack-ai#3546`] Intersect the child object's …" | "[objectstack#3546] …" | | plugin-detail `renderers/record-details.tsx` | "[`objectstack-ai#3546`] Also AND inline-editability …" | "[objectstack#3546] …" | | plugin-form `fieldWriteGate.ts` | "`objectstack-ai#3546` intersects that with the server's effective API operation set" | "objectstack#3546 intersects …" | | plugin-detail `index.tsx`, the `inlineEdit` input comment | "(`objectui#3546`)" | "(objectstack#3546)" | Each `objectstack-ai#9934` sentence is about the `userMessage` channel that `79c46da90` added; each `objectstack-ai#6281` sentence is about the `page:card` `body` retirement that `85ec26d28` carries; the `objectstack-ai#8452` sentence is about the batch `recordIds` form that `27358d517` shipped. ## Special cases (the judgement calls) 1. **A quotation.** The `apiErrorEnvelope.ts` bullet quotes the note beside `sendThrownError` in objectstack's `packages/rest/src/package-routes.ts`, verbatim. That note still reads "since objectstack-ai#9934" on objectstack `main` (`e956924` at the time of this PR); there the number means objectstack's card, which answers 404, and here it resolves to the unrelated objectui item. The quote keeps its words and takes the commit in square brackets, the editorial mark for a substituted word. The note's claim is the one `79c46da90` carries: its `http-dispatcher.ts` hunk adds the `userMessage` spread to the dispatcher door's `extra`. 2. **Line breaks moved** only where the stand-in made a line markedly longer: app-shell `index.ts`, `StudioDesignSurface.tsx` and `packages-io.ts`. Elsewhere the one line just grows, as batch 7's `objectstack#3391/objectstack#3546` lines did. 3. **Wording kept.** "the `objectstack-ai#9934` channel" becomes "the objectstack `79c46da90` channel" in two places, and "the producer's channel (objectstack `79c46da90`)" in `StudioDesignSurface.tsx`, where "the producer's objectstack … channel" would read as if the producer were objectstack. No other word moves. ## The literal-anchor sweep (both test-pin classes, ruling `5861900779`) - **Instrument.** Every string, template and regex literal in the 4108 tracked test and script files (104609 distinct strings, 2339 regexes), read with the TypeScript scanner. - **Test.** Does its occurrence count DROP between `665025908` and this head in any of the 14 changed files, raw or comment-flattened? - **Result.** 30 distinct literals drop, every one generic: digits, punctuation, whitespace and character classes (`#\d+`, `\d{3}`, `[:#]`, `\s+`), the bare words `objectui` and `objectui#`, `/objectui#\d+/`, and comma-and-line-break fragments. None is a changed phrase, a dead number or a `objectstack-ai#3546` tag. A search of tests for the changed spellings finds them only in test doc comments (listed in **Acceptance notes** 1), which no assertion reads. ## Held **By the serial rule: nothing.** Open PRs were mapped at the branch point before the first edit (7 open) and again before the push (7 open; objectui#11003 and objectstack-ai#10997 have since merged, and objectui#11010 and objectstack-ai#11006 have opened). - **objectui#10990** and **objectui#11010** share `packages/app-shell/src/index.ts`. objectui#10990's hunk is in the side-effect import list, near the Cloud widgets; objectui#11010's is one line in the `views` type export list. This PR's two changed lines are in the note above the `readEnvelopeFailureText` export, more than ninety lines above both hunks. - **objectui#5400** (Version Packages) carries `.changeset/7980-agent-key-envelope-read.md`, which it consumes at release; it regenerates and is not a hold. If it lands first, that file's one changed line has already published, and this PR's hunk there conflicts with the deletion. - Trial merges with this head (`git merge-tree --write-tree`): clean for objectui#11010, objectstack-ai#11006, objectstack-ai#10990, objectstack-ai#10930 and objectstack-ai#10777. objectui#10278 conflicts in `ObjectGrid.tsx`, `plugin-grid/README.md` and `content/docs/plugins/plugin-grid.mdx`, and conflicts in the same three files against the branch point alone; none is in this diff. ## Changesets - `.changeset/10803-dead-citation-sweep-eighth-batch.md`, EMPTY frontmatter. It covers the comment-only edits in 5 released packages (`app-shell`, `components`, `plugin-detail`, `plugin-form`, `plugin-grid`) and the one edited pending changeset; no published behaviour changes through them. It writes no bare number itself. - No runtime string moves, so there is no `patch` changeset (amendment `5860244997` Q1 = A applies to none of these lines: the `plugin-detail` `index.tsx` line is a comment beside the `inlineEdit` input, not its `description` string). ## Proof of prose-only (C4), against `665025908` - **Source.** Each of the 12 touched `.ts` / `.tsx` files was parsed at `665025908` and at this head with TypeScript 6.0.3's `createSourceFile`, and re-printed by `createPrinter({ removeComments: true })`: 12 of 12 prints are identical, with 0 parse diagnostics. Lit controls on the same instrument: editing a string literal moves the print; re-spacing a comment does not. - **Changesets.** The frontmatter block of `.changeset/7980-agent-key-envelope-read.md` is byte-identical at `665025908` and this head (md5); the overwrite gate below agrees. - **Scope of the diff:** 14 files, +39 / −20: 1 edited and 1 new changeset, and 12 non-test source files in 5 released packages. No test file. ## Gates, on this head `8fe632d96` Each line is the gate's own verdict and exit code, captured by redirect-then-`$?`. - `node scripts/check-changeset-presence.mjs`, exit 0: "12 source file(s) of 5 released package(s) changed, and this change declares 1 changeset(s): .changeset/10803-dead-citation-sweep-eighth-batch.md." - `pnpm changeset:check`, exit 0: "All workspace packages are in the changeset fixed group." / "No changeset declares a `major` bump." - `node scripts/check-changeset-overwrite.mjs` (report-only), exit 0: "1 changeset(s) added, 1 modified, 0 deleted"; for the modified one, `declared at base` equals `declares now` ("@object-ui/app-shell: minor, @object-ui/console: patch"). - `pnpm check:changeset-claims` (report-only), exit 0: "Every package declared across those 1 body(ies) is either not negated …", and the standing notice "15 pending changeset(s) describe a file this change touches". Read against the diff: a pending changeset quoting a replaced spelling would carry a bare number the census sees, and the census reads 0 in `.changeset/`. - `pnpm check:control-bytes`, exit 0: "check-control-bytes: OK (scanned 9291 tracked text file(s); skipped 85 binary)." A `grep -P` control-byte self-scan of the 14 files finds none. - `pnpm check:new-line-citations`, exit 0: "VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0". - `pnpm check:pending-changeset-literals`, exit 0: "No test source names a pending changeset." - Also run over the touched comments, on the tree this head carries: `pnpm check:spec-symbols` exit 0 ("spec member citations: 1424 sources + 184 documentation pages; nothing cites a key its spec symbol does not declare."); `pnpm check:installed-pin-claims` exit 0 ("OK"); `pnpm check:comment-mask-corpus` exit 0 (1 disagreeing file, within the ceiling objectui#7882 holds open); `node scripts/check-hand-rolled-comment-mask.mjs` exit 0 ("OK every carrier is a DEBT entry, and every DEBT entry still carries one."); `pnpm check:handler-key-reads` exit 0 ("every judged read is a declared member of its arm"). - The governed-surface predicate over the 14 paths, exit 0: "NOT GOVERNED — 14 path(s) checked against 5 governed surface(s); none matched." Lit control `AGENTS.md`: exit 3. **Tests and type-check**, through the shared verify lock, on `8fe632d96`. - `scripts/__tests__/`, the whole directory, whose whole-tree scanners read the touched files and changesets: `Test Files 177 passed | 2 skipped (179)`, `Tests 5350 passed | 2 skipped (5352)`, `VERDICT command-exit 0`. - `packages/components/`, `plugin-detail/`, `plugin-form/` and `plugin-grid/`, whole packages, in one run: `Test Files 847 passed | 2 skipped (849)`, `Tests 8441 passed | 33 skipped (8474)`, `VERDICT command-exit 0`; the source files were byte-identical to this head's. - `packages/app-shell/`, whole package: `Test Files 1 failed | 863 passed | 1 skipped (865)`, `Tests 1 failed | 8835 passed | 9 skipped (8845)`. The one failure is `AppContent.inaccessibleAppStrand.test.tsx`, 'bounces to the DECLARED landing where the deployment declares one', a network-escape guard trip (a real socket at `localhost:3000/api/v1/meta`) under the full suite on a shared box. That file is not in this diff, and C4 shows no code token moves; re-run alone three times on this head it reads `Tests 6 passed (6)` each, and the CI test shards are green on this head. - Type-check: `turbo run build` of the 28-package dependency closure (`Tasks: 28 successful, 28 total`), then `pnpm --workspace-concurrency=2` with the 5 package filters `run type-check`: 5 script echoes, 5 `Done`, `VERDICT command-exit 0`. - No red leg: the sweep found no anchor to move, so there is no pin whose old copy should fail. CI on `8fe632d96`: 43 check-runs, 39 success, 3 skipped, 1 failure; `Spec Main Shape Gate` success. The failure is `Bundle Analysis`, step "Check console performance budget", which fails the same way on `main` at this PR's branch point `665025908` and at `d6d8fb9d7` and `2049b03df` after it; this diff moves no code token. ## Acceptance notes 1. **Outside the card's two classes, as it stands** (whole tree at this head, bare spelling): `objectstack-ai#9934` in 3 test doc-comment lines in 2 files and in 1 `apps/console` source line (`AgentConnectSection.tsx`, "the objectstack-ai#9934 channel"); `objectstack-ai#6281` in 1 test doc-comment line; `objectstack-ai#8452` in 1 test doc-comment line; bare `objectstack-ai#3546` in 23 test lines in 14 files and 19 published `CHANGELOG.md` lines in 8 files. `apps/console`, tests and `CHANGELOG` history are outside the classes this card carries (triage `5858031244`; release `5861553562`: recorded, not carried). 2. **Not measured: `framework#N` and `cloud#N`.** The two classes also cite 82 distinct `framework#N` and 43 distinct `cloud#N` numbers. Neither repository is on this card's lists, and both answer 403 through this container's proxy, so no reading of them is offered. 3. **Left as the brief allows.** The two test comments that call objectui#8652 `pm:blocked` (it reads `pm:on-hold`) and batch 7's sweep-changeset phrase "declared separately below" are in files this PR does not touch, and stay. --- _Generated by [Claude Code](https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk)_ Co-authored-by: Claude <noreply@anthropic.com>
Fixes #10919
Clause-②: yes
What this does
The Cloud pricing page is static metadata, so it cannot tell which of its plan cards the organization is already on (objectstack-ai/cloud#2434 item 5). This PR adds the SDUI widget
cloud:plan-status. A page places one node on each plan card and names that card's plan code inproperties.plan. The widget reads the org-scopedGET /cloud/environment-entitlementssummary and renders a localized "Current plan" badge when the summary'splanequalsproperties.plan. It renders nothing in every other state: another plan, loading, a failed request, a body that is not the{ success, data }envelope, or a node that names no plan. It never guesses a plan.The snippet cloud places in each
planCard(...)column ofpackages/service-tenant/src/pages/pricing.page.ts, for the cloud#2434 item 5 follow-up:The card names
free,teamandenterpriseare the summary's plan codes for those cards. The endpoint can also reportsolo,business,starter,proandcustom, and an organization on one of those gets no card marked, because the page lists no such card.Routing: the triage answer
5867937466, quotedWhat the accept set gains (Clause-②: yes)
AnyComponentSchemagains exactly onetypeliteral,cloud:plan-status, through one new arm,CloudPlanStatusSchema, exported by name from@object-ui/types/zod. So dosafeValidateSchemaandobjectui validate, which judge with it. A document with that type is accepted only when all of these hold:propertiesis present and is exactly{ plan }. The bag is strict: any other key isunrecognized_keysatproperties.planis a non-empty string:invalid_typeortoo_smallatproperties.planotherwise. It is not an enum, because the plan catalog belongs to the control plane.bodyand nochildren. Both are refused by name (invalid_type), because the widget reads neither channel.BaseSchema's declared keys by their types; an undeclared key passes the tolerant face and is refused by the strict face (StrictAnyComponentSchema).Nothing that parsed before is refused now.
NAMESPACED_REFUSED_AT_TYPEstays 397 andREFUSED_AT_TYPEstays 73: the one new registered key lands with its arm.What changed
packages/app-shell/src/console/home/CloudPlanStatus.tsx(new): the widget and its registration. It reads the plan through the existinguseEnvironmentEntitlementshook, which is not edited. The hook'sauthFetchcomes fromuseState, notuseMemo, because the hook keys its fetch effect on it (AGENTS.md [WIP] Enhance every detail of the designer #10). The node'sclassNamereaches the badge, so the page'sresponsiveStylesscope class does too.packages/types/src/zod/cloud.zod.ts(new):CloudPlanStatusSchema,BaseSchemaplus thecloud:plan-statusliteral, a required strictproperties: { plan }bag, and the two content-channel refusals. The refusal text carries the objectui#10928 parser-tier clause: the registration declares nochildreninput, sovalidateTreewarnsnot-a-containerfor this node.packages/types/src/zod/index.zod.ts: the barrel export, the import, and the one union line.origin/mainis merged in (19fc41a), per the claim's note on the draft PR objectui#10962.packages/types/src/zod/README.md: a "Cloud Widgets" group lists the arm.packages/cli/src/utils/known-schema-types.ts: regenerated bynode scripts/regenerate-known-schema-types.mjs. It gains the one linecloud:plan-status.packages/i18n/src/locales/*.ts:cloudPlanStatus.current("Current plan") in all ten packs.EXCLUSIONSrow inzod-mirror-parity.test.ts(no TS declaration in@object-ui/typesrestates the node),cloud-plan-status-arm-10919.test.ts,CloudPlanStatus.test.tsx, and one ratchet row namingcloud:plan-statusas armed..changeset/10919-cloud-plan-status.md: app-shell and typesminor, i18n and clipatch.PM assumptions, measured: three falsified, two confirmed
{ type, plan }. The measured node is{ type, properties: { plan } }. Cloud's pages are typedPagefrom@objectstack/spec/ui, andPageComponentSchemais astrictObjectwhose only props channel isproperties, so a top-levelplanis refused by the spec parse before objectui sees it.cloud:onboarding-nextis authored the same way on the welcome page, and it readsproperties.*. The widget readsproperties.planand nothing else, and the arm declares exactly that.register('cloud:onboarding-next', …, { namespace: 'app-shell' })makes TWO registry keys,app-shell:cloud:onboarding-nextand the fallbackcloud:onboarding-next, and the namespaced ratchet counts both. Following it would have needed the arm to accept a second literal nobody authors. So the widget registers asregister('plan-status', …, { namespace: 'cloud', skipFallback: true }): one key, one arm literal, and one generated line.cloudPlanStatus.current, notcloud.planStatus.current, following the sibling blockscloudOnboarding.*andaiModelStatus.*.useEnvironmentEntitlementssends?organizationId=fromuseAuth().activeOrganization.id, as the precedent does, and the endpoint resolves an absent parameter to the session's active organization. Its row-derived fallback carries no plan, so the widget marks a card only onsource: 'summary'.planvalues. The endpoint'splanis cloud'splanKey(...)overPLAN_CODES(free,solo,team,business,starter,pro,enterprise,custom). ObjectUI names none of them.Surface supplement: two files outside the claim's declared surface
packages/app-shell/src/index.ts(one side-effect import line) andpackages/app-shell/package.json(twosideEffectsentries). A registration cannot ship without both. Without the barrel import, the console never evaluates the module. Without the array entries,scripts/check-side-effects-array.mjsgoes red, because every registering module in the entry graph must be named there, and a bundler would be free to drop the registration. Both edits have the same shape asCloudOnboardingNext's. Overlap, read on the open PR list (RESTpulls/N/files, 2026-09-28): none of the ten non-release open PRs touches either file. The changesets release PR rewrites only theversionfield. Seat: please add these two paths to the claim's file surface.Verification, at
19fc41a(the merge oforigin/main42687ba)pnpm exec vitest run --maxWorkers=2, through the verify lock):packages/types/,packages/cli/,packages/i18n/, plus a declared narrowing ofpackages/app-shell/(below). Result:Test Files 407 passed (407),Tests 8074 passed | 13 skipped.cloud-plan-status-arm-10919.test.ts12/12;CloudPlanStatus.test.tsx10/10; the ratchet file 15/15, including the newcloud:plan-statusrow.turbo run build --filter='@object-ui/app-shell^...'(28/28):@object-ui/types,@object-ui/i18n,@object-ui/cliand@object-ui/app-shellall exit 0.--listFilesOnlyshows each package's test program includes the new test files..ts/.tsxfiles,--format json, 19 files, 0 errors and 0 warnings. The narrowing is a measurement: the packages lint under the rooteslint.config.jsalone (no package-level config), and that config enables no type-aware linting (0 hits forprojectService,parserOptions,TypeCheckedortsconfigRootDir), so this diff cannot move the verdict on any untouched file.regenerate-known-schema-types.mjs --check,check:side-effects-array,check:i18n-keys,check:i18n-drift,check:i18n-dead-keys(report),check:registry-bare-names,check:doc-types,check:prompt-keys,check:unreferenced-sources,check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shape,check:test-path-roots,check:new-line-citations(0 new),check:control-bytes,check:handler-key-reads,check:changeset-claims(report),check:pending-changeset-literals,changeset:check,check-changeset-presence(4 released packages, 1 changeset),check-changeset-overwrite,check-type-check-coverage,check:phantom-deps,check:esm-specifiers,check-lint-coverage, andcheck-governed-queue-guard --testover the 22 paths: NOT GOVERNED.5c4a1d2(the merge changed neither mutated file). Each leg was committed first, mutated throughablation-replace.mjswith an EXIT/INT/TERM restore trap, then restored withgit checkout HEAD --:index.zod.ts). The ratchet goes red: "refuses 398 registered namespaced key(s) attype; the pin is 397", and the Refused list namescloud:plan-status. The arm pins go red too (14 failed). Restored: blob6fa23ee31c7cequals HEAD, andgit diff HEADis empty.entitlements.plan === planbecomestrue). The widget goes red on "renders nothing on a card whose plan is not the organization's" and "compares the plan code verbatim". Restored: blob08d10662137cequals HEAD, andgit diff HEADis empty.vitest.config.mtsaliases@object-ui/types/zodtosrc), so nodistrebuild was part of either leg.NOT MEASURED, each with its reason:
packages/app-shell/full suite (856 files): at--maxWorkers=2it exceeds the foreground cap (exit 124 at 560 s, and exit 124 on shard 1/6 at 270 s). The run was narrowed to the 40 files that read what this diff touches: the two widget directories (console/home/__tests__,environment/__tests__),src/__tests__/, and the 13 tests that readpackage.json,src/index.tsor the barrel. Result: 40/40 files, 575 tests. CI runs the whole suite.check:sdui-registration-pins,check:eager-closureandcheck:eager-locale-cataloguesneed a built console (PREREQUISITE NOT MET, exit 2). CI'sperformance-budget.ymlruns them onpackages/**.check:readme-exports(exit 1) andcheck:doc-examples(exit 2) could not judge the READMEs of packages not built here (app-shell, cli, plugin-*). Among what was judged: 0 wrong-path and 0 fabricated.Acceptance notes (not filed; no carrier)
useEnvironmentEntitlements' docblock still describes an environment-list-only hook ("Only fetch when this is the environment list"), and it now has a second caller. Per the claim, the hook is not edited.app-shell:cloud:onboarding-next,app-shell:cloud:ai-model-statusand their siblings) stay in the refused set. That family is objectui#10872's population.Generated by Claude Code