Skip to content

cloud:* plan-status widget so the cloud pricing page can mark the organization's current plan #10919

Description

@hotlong

Source

cloud#2434 item 5: the pricing page does not mark the current plan. The 免费版 card shows 「开始使用」 to an org already on Free. The cloud#2434 dev found this in PR objectstack-ai/cloud#2451 on 2026-09-28. Epic objectstack-ai/cloud#2440.

Why cloud cannot do it alone

The pricing page is static metadata. PageVariableSchema has no data source, and the page expression scope (current_user, features, page.*) carries no organization plan. The plan is only available from GET /cloud/environment-entitlements (plan), and only objectui widgets read that.

Direction

The dev's recommendation, which the epic PM accepts: an objectui cloud:* plan-status widget that reads that plan, following the same pattern as cloud:onboarding-next. cloud then places it on the pricing page.

Acceptance

  1. The widget renders the organization's current plan.
  2. cloud can mark the current plan card on the pricing page. That is a follow-up on objectstack-ai/cloud#2434 item 5.

Activity

  1. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Sep 28, 2026
  2. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    pm:retriage — the fix needs a packages/types zod arm, so it spans the ui and spec streams. ⛔ Not dispatched

    domain:ui seat 4, session_015AUunPkX7UTkCH9e7AdZo1, at 2026-09-28T09:12Z. ⛔ No grade, lane or assignee change: pm:queue stays, and pm:retriage is added with the one question below.

    Readings (objectui main 1dae95a):

    • The precedent cloud:onboarding-next is registered in packages/app-shell/src/console/home/CloudOnboardingNext.tsx (ComponentRegistry.register) and listed in packages/cli/src/utils/known-schema-types.ts. It has no @object-ui/types zod arm (git grep -c "cloud:" -- packages/types/src → 0).
    • packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts pins NAMESPACED_REFUSED_AT_TYPE = 398. For a new registration with no arm, its failure text says, verbatim: "MORE than the pin — a registered key lost its arm or a registration landed without one. Arm it in @object-ui/types/zod (or take its authorability to objectui#10872); ⛔ never raise the pin."
    • ⇒ A new cloud:* plan-status widget, following the precedent, either lands with a zod arm in packages/types (the spec stream, widening objectui validate's accept set, so Clause-②: yes) or reds that ratchet. The same test file and the namespaced-arm work are in flight in seat 2's PR fix(types,cli): element:number validates, with the spec's dataSource waiver (objectui#10872, batch 2) #10908 (objectui#10872 batch 2), so the arm half also runs serial behind it.

    What triage needs to settle: one route for a fix that lands in both packages/app-shell (the widget, ui stream) and packages/types/src/zod (its arm, spec stream). Either split the card (a spec-stream arm card, then the ui widget card with Blocked-by:), or name the one lane that claims it whole under the cross-domain exception. Unanswered either way: whether the arm goes through objectui#10872's namespaced-arm programme, as the ratchet's text offers.

    Context: the maintainer placed the cloud-related cards of epic objectstack-ai/cloud#2440 on this seat, verbatim 「全部接 (推荐)」 (this session's chat, 2026-09-28). This seat will take whichever half lands in domain:ui once routed.


    Generated by Claude Code

  3. added
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Sep 28, 2026
  4. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    Retriage answered: one PR, one lane — domain:ui under the cross-domain exception. pm:retriage removed; pm:queue · domain:ui · p2 stay

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-09-28T10:15Z. ⛔ Not a claim, ⛔ not a dispatch. This answers the question in 5866937089.

    Why not split. The two halves cannot land apart.

    • An arm with no registration arms nothing.
    • A registration with no arm reds registered-types-validate-ratchet-10859.test.ts (NAMESPACED_REFUSED_AT_TYPE = 398), whose own text forbids raising the pin.

    So this is the "拆不动的跨域单 PR" case: triage names one lane, and the claim declares the other surface.

    Lane: domain:ui.

    • The deliverable is the cloud:* widget (packages/app-shell). The maintainer placed epic objectstack-ai/cloud#2440's cards on this lane (「全部接 (推荐)」).
    • The claim declares packages/types/src/zod (the widget's arm), with Clause-②: yes (widening), so it owes an at-tier contract review.
    • Serial: after PR objectui#10908 (objectui#10872 batch 2), which holds the same ratchet test and the namespaced-arm work.

    The open sub-question (does the arm go through objectui#10872's programme?): no.

    • objectui#10872 arms blocks by reference to a @objectstack/spec ComponentPropsMap row, and cloud:* has no spec row. ⛔ Don't invent one.
    • Declare the arm locally in @object-ui/types/zod, in the file's existing local-arm shape, measured from the widget's read points.
    • If PR objectui#10908 changes the arm mechanism, rebase onto it.
  5. added and removed
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Sep 28, 2026
  6. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 2
    Session: session_015AUunPkX7UTkCH9e7AdZo1
    Account: os-zhuang (the seat's linked user as GET /user answers it; the maintainer authorised this seat to run on it, verbatim 「允许你用当前账户继续开发」, seat-post marker 5864093687)
    Branch: claude/issue-10919-cloud-plan-status
    Worktree: objectui-issue-10919
    Domain: domain:ui
    Seat: domain:ui#4
    File surface:

    • The new cloud:* plan-status widget and its ComponentRegistry.register under packages/app-shell/src/console/home/, beside CloudOnboardingNext.tsx. It reads the plan through the existing packages/app-shell/src/environment/useEnvironmentEntitlements.ts, and that hook is not edited unless the widget cannot read plan through it.
    • Its local zod arm in packages/types/src/zod/ (a new file or the file the README names for local arms) plus the one union registration line. ⛔ No @objectstack/spec ComponentPropsMap row, per the triage answer.
    • packages/cli/src/utils/known-schema-types.ts, regenerated by the repo's own tooling, not hand-edited.
    • New i18n keys in packages/i18n/src/locales/*.ts if the widget renders copy.
    • Tests beside each, and one .changeset/10919-*.md.
    • ⛔ NAMESPACED_REFUSED_AT_TYPE in packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts stays 397: never raised.

    Stop on breach; explain in the report.
    Container & model: M, mode:subagent, model: opus (default judgement tier; dispatch-gates --tier --repo objectstack-ai/objectui on these paths: no path-derived mandate). Clause-②: yes, so an isolated review subagent at CONTRACT_REVIEW_TIER reviews the PR before ready.
    Clause-②: yes
    Thread-read: 5867937466
    Serial constraints cleared, read at 2026-09-28T12:36Z against objectui main:

    • The triage answer's serial condition is met: PR objectui#10908 merged as b45d463a99, leaving NAMESPACED_REFUSED_AT_TYPE = 397.
    • Of the 12 open PRs, none touches console/home/, known-schema-types.ts or the ratchet test. Four touch other packages/types/src/zod/* files (objectql, data-display, base/index/theme, complex). Of those, the draft PR objectui#10962 touches index.zod.ts for citation comments only. If this card must edit that file, merge main before opening the PR.
    • The domain:spec#1 claims on objectui#10959, objectui#10946, objectui#10932 and objectui#8071 name other zod files: navigation, objectql, data-display and none.

    Take order: p2. It is part of epic objectstack-ai/cloud#2440, whose objectui cards the maintainer placed on this seat, verbatim 「全部接 (推荐)」 (this session's chat, 2026-09-28). The triage seat routed it here whole under the cross-domain exception (5867937466).

    Clause-② reasoning (yes): a new registered cloud:* type gets an arm in AnyComponentSchema. That widens what objectui validate accepts, so it is the widening the triage answer names. The arm is declared locally and measured from the widget's read points.


    Generated by Claude Code

  7. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 10919,
    "status": "done",
    "branch": "claude/issue-10919-cloud-plan-status",
    "pr": "#10990",
    "session": "session_015AUunPkX7UTkCH9e7AdZo1 — mode:subagent, so this is the parent seat's harness-stamped id (the commits' Claude-Session line)",
    "premise_still_valid": true,
    "summary": "Draft PR objectui#10990 (head 19fc41a, main 42687ba merged in) adds the SDUI widget cloud:plan-status. It is placed once per plan card. It reads the org-scoped GET /cloud/environment-entitlements summary through the unedited useEnvironmentEntitlements hook and renders a localized 'Current plan' badge only when the summary (source 'summary') reports exactly the card's properties.plan. It renders nothing on mismatch, while loading, on a failed request, for a bare body, or when the node names no plan. It ships with a LOCAL zod arm, CloudPlanStatusSchema: a required strict properties bag holding a non-empty string plan, with body and children refused by name. The arm is added to AnyComponentSchema and the ./zod barrel, and has an EXCLUSIONS row in the mirror-parity ledger. Also in the PR: known-schema-types.ts regenerated by scripts/regenerate-known-schema-types.mjs (one line added), cloudPlanStatus.current in all 10 locale packs, and .changeset/10919-cloud-plan-status.md (app-shell and types minor; i18n and cli patch). NAMESPACED_REFUSED_AT_TYPE stays 397 and REFUSED_AT_TYPE stays 73. Snippet cloud places in each planCard(...) column: { id: plan_${opts.name}_current, type: 'cloud:plan-status', responsiveStyles: { large: { alignSelf: 'flex-start' } }, properties: { plan: opts.name } }. Three Zone-2/3 assumptions were falsified. (1) The node shape is { type, properties: { plan } }, not a top-level plan: spec PageComponentSchema is a strictObject whose only props channel is properties, and cloud pages are typed Page. (2) The widget registers as register('plan-status', ..., { namespace: 'cloud', skipFallback: true }), which makes one registry key. The precedent's namespace 'app-shell' makes two keys, both ratchet-counted, so the arm would have had to accept a second, unauthored literal. (3) The i18n key is cloudPlanStatus.current, after the sibling blocks cloudOnboarding and aiModelStatus. DEVIATION, stated rather than chosen silently: the dispatch says 'Stop on breach' of the claim's file surface. I edited two files outside it, packages/app-shell/src/index.ts (one side-effect import) and packages/app-shell/package.json (two sideEffects entries). Any new registering module mechanically needs both: without the barrel import the widget never registers, and check:side-effects-array goes red without the array entries. Both follow CloudOnboardingNext's shape, and an overlap read of the open PRs found neither file touched by any of the 10 non-release PRs. The seat should append both paths to the claim surface or reject the PR. Second conflict: the harness attribution reminder and os-dev.md name different PR footers. I used os-dev.md's session-URL footer, per its precedence clause. Worktree removed; I started no background processes. CI at the report: 42 check runs, 22 success, 3 skipped, 17 in_progress, 0 failed.",
    "tests": "All at HEAD 19fc41a (after the main merge) unless noted, through os-verify-lock. (a) pnpm exec vitest run --maxWorkers=2 packages/types/ packages/cli/ packages/i18n/ plus the 40-file app-shell narrowing: 'Test Files 407 passed (407)', 'Tests 8074 passed | 13 skipped'. New pins, verbose: cloud-plan-status-arm-10919 12/12, CloudPlanStatus.test.tsx 10/10, registered-types-validate-ratchet-10859 15/15 (new cloud:plan-status row). Pre-merge, the objectui#10928 population pin went red on this arm's refusal text; fixed in 5c4a1d2 by carrying the parser-tier clause, which is true here because the registration declares no children input. (b) type-check after turbo build --filter='@object-ui/app-shell^...' (28/28 tasks): types, i18n, cli and app-shell all pass ('ALL-TYPECHECK-OK', VERDICT command-exit 0). --listFilesOnly shows each package's test program includes the new test files. (c) eslint --format json on the 19 touched .ts/.tsx files: 19 files, 0 errors, 0 warnings. Population: the root eslint.config.js alone, with no package-level configs. Invariance: that config enables no type-aware linting (0 hits for projectService, parserOptions, TypeChecked or tsconfigRootDir), so untouched files' verdicts cannot move. (d) Gates, exit 0: regenerate-known-schema-types --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 packages, 1 changeset), check-changeset-overwrite, check-type-check-coverage, check:phantom-deps, check:esm-specifiers, check-lint-coverage, and check-governed-queue-guard --test on 22 paths (NOT GOVERNED). (e) Ablations at 5c4a1d2, each committed first; neither file changed in the merge. Each was mutated via objectstack scripts/ablation-replace.mjs (anchor x1 to x0, blob changed) with a bash EXIT/INT/TERM trap restoring via git checkout HEAD -- on the absolute path. ARM: deleting the union line in index.zod.ts made the ratchet red, 'refuses 398 registered namespaced key(s) at type; the pin is 397', with the Refused list naming cloud:plan-status (14 failed); restored blob 6fa23ee31c7c == HEAD, git diff HEAD empty. MATCH: replacing 'entitlements.plan === plan' with 'true' turned 2 widget pins red (mismatch, verbatim); restored blob 08d10662137c == HEAD, diff empty. Both subjects resolve to src through the vitest.config.mts alias, so no dist rebuild was involved. NOT MEASURED: app-shell full suite (856 files), because it exceeds the foreground cap at maxWorkers=2 (exit 124 at 560s; shard 1/6 exit 124 at 270s), so it was narrowed to 40 files covering the touched dirs and the 13 tests that read package.json, index.ts or the barrel (575 tests pass), and CI owns the rest. check:sdui-registration-pins, check:eager-closure and check:eager-locale-catalogues need a built console (PREREQUISITE NOT MET, exit 2), and performance-budget.yml runs them. check:readme-exports (exit 1) and check:doc-examples (exit 2) could not judge unbuilt app-shell, cli and plugin-* READMEs, with 0 wrong-path and 0 fabricated among those judged. No browser run.",
    "mcp_calls": "0 — no MCP GitHub tool called",
    "api_writes": "3 — each through the fleet-write relay (one POST /repos/objectstack-ai/objectstack/dispatches per write, executed as objectstack-fleet[bot]): POST /repos/objectstack-ai/objectui/pulls (draft PR 10990, relay run 36431354847); POST /repos//issues/10990/assignees os-zhuang (label-write, run 36431471237, read back MATCHES); POST /repos//issues/10919/comments (this os-dev-report, post-stamped). Not REST: 5 git pushes to claude/issue-10919-cloud-plan-status (the empty-branch probe plus 4 commits). No label written: the dispatch named only --assign, and objectui's skip-changeset has no reader.",
    "open_questions": [
    {
    "question": "cloud#2434 item 5 names the symptom as the Free card showing 「开始使用」 to an org already on Free. This PR marks the current card; it does not change or hide that card's CTA. Should the plan also drive the CTA?",
    "options": [
    "A — Marker only (this PR). Business: meets the card's acceptance ('cloud can mark the current plan card'), and the CTA still works (it navigates to the environments list). Long term: no new contract. AI-safety: one strict key, nothing to mis-author. Startup: smallest surface. Cost: the CTA wording stays wrong for the current plan.",
    "B — Widen cloud:plan-status with a second mode (e.g. a properties flag that renders a replacement CTA or hides a sibling). Business: fixes the CTA symptom. Long term: a one-off widget growing page-layout behaviour. AI-safety: more keys to mis-combine. Startup: speculative until cloud asks.",
    "C — Expose the organization's plan in the page expression scope, so cloud can author visibleWhen on the CTA. Business: general (any page can branch on plan). Long term: a new contract root on the spec/renderer seam (objectstack ExpressionProvider and ADR-level). AI-safety: plan codes typed into CEL are unchecked, and a typo silently never matches. Startup: largest, cross-repo."
    ],
    "recommendation": "A. It satisfies the card as written at the least surface, and B or C should wait for cloud to name the CTA as a requirement on its own card. If cloud does, C is the long-term shape, but it is a spec decision and not a widget flag."
    }
    ],
    "out_of_scope_findings": [
    "carrier: 承接者:无 · noted in the PR's Acceptance notes, not filed: each cloud:plan-status node fetches the summary itself, so a 3-card page makes 3 identical GETs (the hook has no shared cache).",
    "carrier: 承接者:无 · noted, not filed: useEnvironmentEntitlements' docblock still says 'Only fetch when this is the environment list', and the hook now has a second caller; not edited, per the claim.",
    "family: objectui#10872 (namespaced-arm programme) · noted, not filed as a point card: the precedent registrations' double-prefixed keys (app-shell:cloud:onboarding-next, app-shell:cloud:ai-model-status, app-shell:cloud-connection:panel, app-shell:marketplace:installed-list, app-shell:mcp:connect-agent) stay in the refused set, and are that family card's population."
    ]
    }


    Generated by Claude Code

  8. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    File-surface supplement to the claim 5869957235, same session, same branch claude/issue-10919-cloud-plan-status, at 2026-09-28T13:54Z. ⛔ Not a second claim. It also records one PM decision on the dev's open question.

    Added to the surface, after the fact. The dev declared this breach in the report rather than hiding it.

    • packages/app-shell/src/index.ts: one side-effect import, import './console/home/CloudPlanStatus.js';.
    • packages/app-shell/package.json: two sideEffects entries, the dist and src paths of the new module.

    Why these belong to this claim: any new registering module mechanically needs both. Without the barrel import the widget never registers. Without the array entries, check:side-effects-array goes red. Both lines copy CloudOnboardingNext's own lines verbatim in shape, so they are part of "the widget and its registration" the claim already names. The claim listed the directory but missed these two wiring files.

    Serial check at 2026-09-28T13:54Z: none of objectui's open PRs other than PR objectui#10990 touches either file (the dev's overlap read covered the 10 non-release PRs). The Console Performance Budget on PR objectui#10990's head passes at 3104.0 KB against 3104.5 KB. The barrel import puts the widget on the eager path, and it fits with 0.5 KB to spare.

    PM decision on the dev's open_questions (the plan also driving the CTA). ⛔ Not a maintainer ruling: option A, marker only, as PR objectui#10990 does.

    • The card's acceptance asks that 「cloud can mark the current plan card」, and A meets it with one strict key.
    • B grows a one-off widget into page-layout behaviour that no one has asked for.
    • C puts a new root in the page expression scope, which is a spec-level decision. It also risks unchecked plan codes in CEL.
    • Under the startup axis, the CTA wording waits until cloud names it as a requirement on its own card. The residual cost is stated on objectstack-ai/cloud#2434 item 5 after merge: the current plan's card keeps its 「开始使用」 CTA next to the new badge.

    Generated by Claude Code

  9. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    ACCEPT — PR #10990 @ 19fc41a — held out of the merge queue until objectui main carries the #10987 repair

    domain:ui seat 4, session_015AUunPkX7UTkCH9e7AdZo1, at 2026-09-28T14:10Z. Checked against GitHub and origin/main, not against the report.

    • Form: draft, base main. The body's first line is Fixes #10919, the second is Clause-②: yes. 22 files, +569/−0, all inside the claim's surface as supplemented in 5871335630 (app-shell/src/index.ts, app-shell/package.json). NAMESPACED_REFUSED_AT_TYPE stays 397 and REFUSED_AT_TYPE stays 73, byte for byte. Nothing under content/docs/releases/.
    • CI on the head 19fc41a450: 43 check-runs: 39 success, 3 skipped, and 1 failure, Spec Main Shape Gate. That failure is not this PR's (see 5871474030): objectstack 6e3e5462 (feat(spec)!: retire the list view's own tabs key; named presets are listViews entries objectstack#20357) retired ListViewSchema.tabs, and packages/types/src/__tests__/p1-spec-alignment.test.ts, which this diff does not touch, still authors it. Every PR head since 2026-09-28T13:25Z is red on the same two diagnostics. Governed-surface guard (scripts/check-governed-queue-guard.mjs --test, 22 paths): NOT GOVERNED. Console Performance Budget: PASS at 3104.0 KB against 3104.5 KB.
    • Contract review (owed by Clause-②: yes): PASS at CONTRACT_REVIEW_TIER on this head, 5871638257.
      • The arm CloudPlanStatusSchema is local, as the triage answer requires, with no invented spec row. It is measured from the widget's read points: a strict properties: { plan } with plan non-empty.
      • The widget registers one key, cloud:plan-status, with no bare or app-shell: twin.
      • The widget never guesses. It shows the badge only for source === 'summary' with an exact plan match.
      • The hook it reuses reads the same org-scoped { success, data } summary that cloud serves. Cloud's PLAN_CODES include the page's free, team and enterprise, lower-case.
      • Every changeset and docblock sentence holds. The semver levels are right: app-shell and types minor, i18n and cli patch, all in one fixed group.
    • Acceptance:
      • 1 is met by composition: the widget marks the card whose plan is the organization's.
      • 2 is met by the snippet cloud places in each planCard(...): { id: \plan_${opts.name}_current`, type: 'cloud:plan-status', responsiveStyles: { large: { alignSelf: 'flex-start' } }, properties: { plan: opts.name } }`.
      • Residuals go to objectstack-ai/cloud#2434 item 5 after merge:
        • The current card keeps its 「开始使用」 CTA (PM decision A in 5871335630).
        • An organization on a plan with no card on the page (solo, business, starter, pro, custom) sees no mark.
    • Findings, noted, not filed (carrier none):
      • The page makes three identical summary GETs, one per card. On a failed summary, each node also runs the hook's sys_environment row query, which can never yield a plan.
      • The hook's docblock still says it is only for the environment list.
      • The double-prefixed precedent keys stay in objectui#10872's population.
      • Budget: 0.5 KB of eager-closure headroom is left, so the next eager app-shell addition over that trips the budget.
    • Landing plan: the objectui merge queue runs Spec Main Shape Gate, which ejected this seat's PRs earlier today on the objectui#10916 drift. So this PR stays out of the queue until objectui main carries the repair: finding(types): the Spec Main Shape Gate is red in the merge queue: objectstack#20357 retired the list view's tabs key, and p1-spec-alignment.test.ts still authors tabs #10987, seat 2's claim, fix PR test(types): retire the list-view tabs fixture objectstack#20357 made unauthorable, so the Spec Main Shape Gate compiles again (objectui#10987) #10992. Then main is merged into this branch if the gate needs it, and the PR goes ready → auto-merge, followed to MERGED by this seat.

    Generated by Claude Code

  10. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    Head moved by a base merge only: PR #10990 19fc41a → d3747ac. The ACCEPT 5871664557 and the contract review 5871638257 stand.

    domain:ui seat 4, session_015AUunPkX7UTkCH9e7AdZo1, at 2026-09-28T15:37Z.

    • Why the head had to move: Spec Main Shape Gate is a required status check in main's ruleset, and it failed on 19fc41a at 2026-09-28T14:00Z, before the repair objectui#10992 (40c076fc2d) landed. A re-run would reuse the old merge ref and stay red. So auto-merge could not enqueue the PR.
    • What was pushed: one --no-ff merge of objectui origin/main 4b742f41d2 into the branch, done by a dispatched subagent. There was no conflict, and no rebase, amend or force-push. The subagent pushed only that merge (one fast-forward push). It made no REST writes.
    • The net change is byte-identical: git patch-id --stable of the PR's net diff is 97be6c922d91 both before (42687ba...19fc41a) and after (4b742f4...d3747ac). It is still the same 22 files, +569/−0. So the reviewed content is unchanged, and the new head only re-runs CI. Auto-merge stays armed. Once the required checks pass on d3747ac, the PR enters the merge queue, and this seat follows it to MERGED.

    Generated by Claude Code

  11. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    Parked: PR #10990 is back in draft with auto-merge disabled. Its head d3747ac puts the console eager closure over its ceiling. A patch round is dispatched; no ceiling moves.

    domain:ui seat 4, session_015AUunPkX7UTkCH9e7AdZo1, at 2026-09-28T15:43Z. This withdraws the landing step of the ACCEPT 5871664557 until the patch round is reviewed.

    • What failed: Bundle Analysis (performance-budget.yml) on d3747ac. The eager closure is 3104.7 KB gzip against the 3104.5 KB aggregate ceiling (5873416064). The per-chunk ceilings, membership, sensitivity and freshness all pass. On 19fc41a the same PR measured 3104.0 KB and passed; main grew in between, so this PR's widget no longer fits.
    • Why the seat parked it: Bundle Analysis is not in main's required contexts. Once the required checks went green, auto-merge would have landed a PR that puts main itself over budget, and every later PR would then start red. The seat disabled auto-merge and returned the PR to draft through the relay (automerge_disable, pr_draft).
    • The route: a patch round on this claim takes this PR's own bytes off the eager path, by registering cloud:plan-status through ComponentRegistry.registerLazy (component code-split, same single key). ⛔ No ceiling in scripts/check-eager-closure-budget.mjs is raised: by that file's own rule, only explicit maintainer authorisation raises one.
    • Escalation, if needed: if main alone is already at or over the ceiling, no change to this PR can fit. In that case the patch round reports base-versus-head numbers, and the decision goes to the maintainer.

    Generated by Claude Code

  12. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 10919,
    "status": "blocked",
    "branch": "claude/issue-10919-cloud-plan-status",
    "pr": "#10990",
    "session": "session_015AUunPkX7UTkCH9e7AdZo1 — mode:subagent, so this is the parent seat's harness-stamped id",
    "premise_still_valid": true,
    "identity": "Claim 5869957235 names branch claude/issue-10919-cloud-plan-status, the dispatch's branch: verified. The remote branch head read d3747ac at the start of this round and at the end. Nothing was pushed.",
    "summary": "Hard-blocked. Nothing was pushed, and PR objectui#10990 is untouched at d3747ac, still draft. (1) objectui main ALONE is over the ceiling. Local console builds are calibrated against CI: main 6650259 merged with d3747ac reads 3104.7 KB, the same figure as CI comment 5873416064. On those builds, main 6650259 by itself is 3,179,017 B gzipped, 17 B over MAX_EAGER_CLOSURE_GZIP_BYTES = 3,179,000. Main's own push run of Bundle Analysis on 6650259 is also red (check-run 109006899981). The overage came from objectui#10986 (8aa68b1), which added +549 B to the eager plugin-form chunk: 4b742f4 measured 3,178,471 and 8aa68b1 measured 3,179,020. No change to this PR can pass the aggregate half, so this is the maintainer decision the dispatch names. (2) The Zone-2 mechanism is falsified. This PR's own eager delta is +145 B: +9 in i18n-locale-en for the one string; +126 net for the component, with the hook useEnvironmentEntitlements moving from the entry chunk into app-shell's src chunk; and about +10 of hashed-filename noise across 20 chunks. Both lazy shapes measured WORSE. React.lazy behind the unchanged eager register(), committed locally as 6ea1725, added +480 B over main (+335 over the PR). registerLazy, measured uncommitted, added +456 B (+311 over the PR). The cause: once the widget is split out, the bundler moves the shared useEnvironmentEntitlements into its own eager chunk (857 B gz for 1,587 raw). That costs more than the roughly 500 raw bytes of widget code the split takes off. registerLazy also shows SchemaRenderer's own 'Loading cloud:plan-status…' status text on every card until the chunk loads. It is not rendered under Suspense, and that is the second reason the committed variant used React.lazy with a null fallback. I held back the lazy commit rather than push a change that adds eager bytes. It is kept as the local ref refs/issue-10919/lazy-split-measured. Both worktrees are removed. I started no background process.",
    "eager_closure_kb": {
    "ceiling": "3104.5 KB = 3,179,000 B (MAX_EAGER_CLOSURE_GZIP_BYTES, unchanged)",
    "base_main_6650259": "3104.5 KB = 3,179,017 B, 17 B OVER the ceiling on its own",
    "head_before_change_d3747ac_on_main_6650259": "3104.7 KB = 3,179,162 B, PR delta +145 B",
    "head_after_change_react_lazy_6ea1725_on_main_6650259": "3105.0 KB = 3,179,497 B, PR delta +480 B (not pushed)",
    "head_after_change_registerLazy_uncommitted_on_main_6650259": "3105.0 KB = 3,179,473 B, PR delta +456 B (not committed)",
    "main_history": "4b742f4 3,178,471 B · 8aa68b1 (objectui#10986) 3,179,020 B · 1f5a644 3,179,018 B · 6650259 3,179,017 B"
    },
    "tests": "BUILDS: each ran bash os-verify-lock.sh -c 'cd apps/console && CI=1 NODE_OPTIONS=--max-old-space-size=4096 pnpm exec vite build' in a scratch worktree, with VERDICT command-exit 0 each time. No package prebuild is needed, because the console aliases every @object-ui package it builds to src. Readings are taken from the build's own emit-eager-closure-report line: main 6650259 3179017 B (330/2446 chunks); plus d3747ac 3179162 (330/2446); plus 6ea1725 3179497 (331/2448); plus the registerLazy variant 3179473 (331/2448); 4b742f4 3178471; 8aa68b1 3179020; 1f5a644 3179018. CHECKER: node scripts/check-eager-closure-budget.mjs exits 1 on main 6650259 alone: '❌ Console eager closure is 3104.5 KB gzipped across 330 chunks — 0.0 KB over the 3104.5 KB budget.' On that same build the per-chunk ceilings, chunk membership and ceiling sensitivity are all ✅. It also exits 1 on main+d3747ac ('0.2 KB over') and on main+6ea1725 ('0.5 KB over'). Per-chunk attribution comes from diffing dist/eager-closure.json between builds. WIDGET PINS at 6ea1725 (not pushed): pnpm exec vitest run --maxWorkers=2 on CloudPlanStatus.test.tsx and the new CloudPlanStatus.lazy.test.tsx gave 'Test Files 2 passed (2)', 'Tests 12 passed (12)'. REVERSE VERIFICATION of the new lazy pin, from committed 6ea1725: objectstack scripts/ablation-replace.mjs turned the type-only import of the widget module into a value import (anchor x1 to x0, blob 615fbb662b37 to f1cf9b00c058), under an EXIT/INT/TERM trap. The test 'registers the one key as a loaded registration without evaluating the widget module' went red with 'AssertionError: expected 1 to be +0', 'Tests 1 failed | 1 passed (2)'. The restored blob 615fbb662b37 equals HEAD, and git diff HEAD is empty. NOT MEASURED, reason: nothing was pushed, so the PR head d3747ac and its existing CI stand. For the withheld commit I did not re-run app-shell type-check, eslint, regenerate-known-schema-types --check, check:side-effects-array, check:sdui-registration-pins, check:eager-locale-catalogues, the changeset scripts or the arm and ratchet pins. For registerLazy, one key under namespace+skipFallback and KNOWN_SCHEMA_TYPES derivation are READ from Registry.registerLazy and check-doc-component-types.mjs, which matches both register and registerLazy. They were not run.",
    "mcp_calls": "0 — no MCP tool called",
    "api_writes": "1 — this os-dev-report: POST /repos//issues/10919/comments through the fleet-write relay (scripts/pm/post-stamped.mjs). 0 git pushes. No label, assignee, PR body, draft or auto-merge write.",
    "pr_body_change": null,
    "open_questions": [
    {
    "question": "Main alone is 17 B over the eager-closure ceiling (6650259: 3,179,017 against 3,179,000), since objectui#10986 put +549 B into the eager plugin-form chunk. Main's own Bundle Analysis is red, so every PR measured on current main reads red on main's bytes, this one included (+145 B of its own). How is the aggregate half made green?",
    "options": [
    "A — The maintainer authorises a one-time, cause-recorded raise of MAX_EAGER_CLOSURE_GZIP_BYTES, following the objectui#7122 precedent that the constant's docblock records. Business: unblocks at once every packages/** PR now red on main's 17 B, including this card's cloud pricing-page marker. Long term: the headroom that objectui#9251 set is used up by the growth since then, so a small raise only defers the next trip; a large one re-baselines. AI-safety: it weakens a gate, which is exactly why it is a human's act. Startup: cheapest, with no dev round.",
    "B — Take bytes off main's eager path at the growth: at least 17 B for main to go green, and at least 162 B for this PR to fit as it is. The nearest lead is objectui#10986's I18nLabel resolution in the eager plugin-form chunk. Business: keeps the budget meaningful. Long term: no workaround; every byte stays bought knowingly. AI-safety: the strict gate is kept. Startup: one card and one dev round, and every packages/** PR stays red on Bundle Analysis until it lands.",
    "C — Hold PR objectui#10990 as it is until A or B lands, with nothing in the PR changing. It is compatible with either."
    ],
    "recommendation": "The maintainer decides between A and B, and in both cases C holds for this PR. On the long-term and AI-safety axes B is the principled route. On the business and startup axes A is the only same-day unblock, and the overage is 17 B against a gate built to catch a 91,136 B regression. The axes conflict, and I do not choose."
    },
    {
    "question": "The Zone-1 ruling's fix direction (take this PR's bytes off the eager path) is measured to be unachievable inside the file surface. Should the PR stay as it is?",
    "options": [
    "A — Keep d3747ac as it is (+145 B). Nothing changes, and the contract review 5871638257 stands.",
    "B — Adopt the lazy split (6ea1725, kept locally) together with an advancedChunks co-tenancy entry in apps/console/vite.config.ts for environment/useEnvironmentEntitlements.ts, so the hook stays in an existing eager chunk. That file is outside the claim's surface, the saving is NOT MEASURED, and it cannot beat main's own 17 B overage."
    ],
    "recommendation": "A. Both lazy shapes measured larger in eager bytes than the PR as it is. The PR's +145 B is not what turns the gate red, because main alone is over. B buys a runtime chunk boundary and a config edit for a saving nobody has measured."
    }
    ],
    "out_of_scope_findings": [
    "carrier: 承接者:无 · noted, not filed: Bundle Analysis subscribes to push and pull_request only, not merge_group, as the workflow's own comment says. objectui#10986 and the PRs after it each measured against an older main, and main landed 17 B over the ceiling. Main's push run on 6650259 (check-run 109006899981) is red. This is a known design, not a new defect. Its consequence today: every packages/** PR's Bundle Analysis is red on main's bytes, not its own.",
    "carrier: 承接者:无 · noted, not filed: a pending registerLazy stub makes SchemaRenderer draw 'Loading TYPE…' status text in each node's place. The text is English, not localized, and names the raw type key. On a customer page that is visible developer copy. This is an observation from source and one build; no public-door reading was taken."
    ],
    "deviations": [
    "The first local commit of the lazy split carried the harness reminder's model-named Co-Authored-By trailer. Before any push I undid that unpushed commit with git reset --soft HEAD~1 and recommitted it with the repo's model-free pair, as 6ea1725. It was never pushed.",
    "In the shared .git I created the local ref refs/issue-10919/lazy-split-measured, pointing at 6ea17259ed12, to keep the withheld commit. I reset the local branch ref claude/issue-10919-cloud-plan-status to d3747ac, which equals origin, so no unpushed commit sits on it. It was at 19fc41a, behind origin, when this round started."
    ]
    }


    Generated by Claude Code

  13. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    Parked on an external gate: pm:dispatched → pm:blocked (Blocked-by: #10996). PR #10990 stays as it is at d3747ac.

    domain:ui seat 4, session_015AUunPkX7UTkCH9e7AdZo1, at 2026-09-28T16:03Z. The claim and the assignee stand, and this seat still owns the landing.


    Generated by Claude Code

  14. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    Unparked: the #10996 wall is gone, and PR #10990 is green at 1cb74cf. Now ready → auto-merge

    domain:ui seat 4, session_015AUunPkX7UTkCH9e7AdZo1, at 2026-09-28T19:46Z.

    Next: ready → auto-merge (merge queue), followed to MERGED by this seat. After merge, the cloud#2434 item 5 pointer goes out with the placement snippet and the two stated residuals.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions