Repository navigation
Studio: the landing card of each installed base says "n customized, m switched off", read from the per-package customization read #28566
Description
epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
Filing gate: ④ child of epic #28236 (P2, cut 2026-11-01 on the maintainer's 「四项提速措施全部按推荐执行」, schedule reviewed) · plan of record: ADR-0146 Rollout P2 "N-24 landing counts"; Q8 makes it one of the two conditions for retiring Setup › Packaged automation (N-28). ⛔ Not a claim.
Blocked-by: #28411
Part of #28236 (P2, child N-24) · governs: ADR-0146 D3 ("Upgrade and audit": one line per package, "n customized, m switched off"), D4 / Q8 (Packaged automation retires after the ledger switch and this card) · lane: domain:ui · size: S.
Why it waits. The numbers are the server's: N-15 (#28411) serves, per base, the overrides, extensions and switched-off items with their counts. This card draws them and computes none.
Business reason. Before an upgrade, or just to check, an administrator wants to see at a glance which installed apps they have changed and what they have switched off. Studio's landing should say it on each app's card ("HotCRM — 4 customized, 2 switched off"). Today the only deployment-wide view is Setup's Packaged automation page, which covers flows and actions only.
Measured (35b178bd0a).
- Studio's landing is
ui/packages/app-shell/src/views/studio-design/BuilderLanding.tsx. Read-only packages get one card each (:369–402): name, id, a "read-only" chip, and a link out to Setup (studio-landing-overlay, :389). No card carries a count. - Package rows come from
fetchPackages()(packages-io.ts), withwritableas the server's verdict. packages/spec/src/kernel/customization-package.ts:isCustomizationPackage(:119) andcustomizationBasePackageId(:111) recognize a pair id.- N-15's route and response field names: not landed, not verified.
Wanted.
- Each read-only base card carries one line: "n customized, m switched off" (zh 「已定制 n 项,已停用 m 项」). The numbers are N-15's served counts for that base, as served. n covers overrides and extensions, as N-15 counts them.
- Zero is shown as zero only when N-15 answered. While loading, the line shows no number. On a failed read it shows a muted "couldn't read customizations", with the server's sentence in the title, never "0".
- When the base has a pair, the line links to it in Studio (the pair's Studio URL,
/studio/hotcrm.custom/datafor HotCRM; N-19 later folds this into the base). The switched-off part links to the base's Automations pillar, where N-04's switch lives. - No line on a writable package or on a pair (
isCustomizationPackage). A pair id is never sent to N-15, which refuses it. - One read per read-only base, capped by the cards drawn, or one batched read if N-15 offers one. The landing renders before the counts arrive.
Tests. Stubbed N-15 answers:
- a base with 3 overrides, 1 extension and 2 switched off reads "4 customized, 2 switched off" and links to
<base>.custom; - a base with no pair reads "0 customized, 0 switched off" with no pair link;
- a failed read shows no number;
- a writable package and a pair draw no line and send no request;
- the landing paints before the counts resolve.
Release safety. Safe on its own: read-only drawing of a served read (Blocked-by: #28411). Packaged automation is not touched here. Its retirement is N-28.
Not here. The read itself (N-15). Rail marks and the "only customizations" filter (N-19). Retiring Packaged automation and the landing's Setup link (N-28). Directory marks (N-26b).
Dedupe. Searched studio landing customized switched off, landing counts customization: only #28411 (the read) and closed history.
Links back: #28236, #28411, ADR-0146 D3, D4, Q8.
Generated by Claude Code
Activity
objectstack-fleet commentedon Nov 1, 2026
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y
Correction to the unblock note 6555740870 · 2026-11-01T09:04Z
The read this card draws is ledgered but not mounted on
main: #28622 (p0 main red,domain:server) measuresGET /api/v1/packages/:id/customizationsanswering nothing at runtime since519089e1. The card stays claimed and the work goes on against the response contract (GetPackageCustomizationsResponseSchema) with stubbed answers, as its Tests section already asks. The seat does not land the PR until #28622 is fixed, so a live probe can show the line drawing real counts and not the "couldn't read customizations" state.Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:ui#4 · zhuangjianguo · session_01QsR2jRL4qcnwHDD4M1e4wx
os-dev-report
Delivered 2026-11-01T09:44Z: draft PR #28634 on
claude/project-thread-9g21fi, head3d90f3d4.{ "issue": 28566, "status": "done", "branch": "claude/project-thread-9g21fi", "pr": "https://github.com/objectstack-ai/objectstack/pull/28634", "session": "session_01QsR2jRL4qcnwHDD4M1e4wx", "premise_still_valid": true, "summary": "Each read-only base card on the Studio landing now reads GET /api/v1/packages/:id/customizations once and draws 'n customized, m switched off' (zh 已定制 n 项,已停用 m 项): n = served counts.overrides + counts.extensions, m = served counts.switchedOff, judged by GetPackageCustomizationsResponseSchema. Pending shows no number; a failed or off-contract read shows a muted 'Couldn't read customizations' with the server sentence as title. The pair link (/studio/BASE.custom/data) appears only when the package list holds the pair; the switched-off part links /studio/BASE/automations. Writable packages and pairs draw no line and send no request. Live runtime shows the failed state until #28622 mounts the route.", "tests": "head 7b294065 (code), final head 3d90f3d4 = 7b294065 + merge of main d77dc07a (ui/e2e/live only, no overlap). New BuilderLanding.customizationCounts-28566.test.tsx: 1 file, 6/6. Ablations via ablation-replace.mjs, tree restored each time (git diff HEAD empty): (1) drop the extensions term → 1 failed (the 4-customized case); (2) drop only the landing pair filter → 6/6 green, the reader's own pair guard holds; (3) drop reader guard + landing filter together → 1 failed (no-line/no-request case). Every ui test naming BuilderLanding or the reader (git grep, 22 files): 120/120; before stubbing four older landing tests they were 4 failed with the network-escape guard. Route-level landing consumers: 4 files 16/16. app-shell type-check exit 0. packages/i18n/ 94 files, 1438 passed, 13 skipped. Narrowing declared: full app-shell suite left to CI Test UI.", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at 7b294065: 34 derived, all exit 0 (incl. check-sdui-manifest.mjs and --self-test, check:type-check-debt under lock, vitest run scripts/ 169 files / 4900 tests). --ran reconciliation: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. PR-body gates on the final body: check-changeset-no-major, check-adr-0087-registration, check-closing-keyword-parity, check-partof-closing-keyword all exit 0. check:new-line-citations: 0 new. CI check-runs: in_progress at report time.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "line_budget": "S card: +450 / -3 over 10 files (code 188 = BuilderLanding.tsx 117 + reader 71; tests 231; strings 16; changeset 18)", "files_changed": [ ".changeset/28566-studio-landing-customization-counts.md", "ui/packages/app-shell/src/views/studio-design/packageCustomizations-io.ts", "ui/packages/app-shell/src/views/studio-design/BuilderLanding.tsx", "ui/packages/app-shell/src/views/studio-design/BuilderLanding.customizationCounts-28566.test.tsx", "ui/packages/app-shell/src/views/studio-design/BuilderLanding.accessibleNames-12510.test.tsx", "ui/packages/app-shell/src/views/studio-design/BuilderLanding.orgScope-11553.test.tsx", "ui/packages/app-shell/src/views/studio-design/BuilderLanding.recentPackages-11863.test.tsx", "ui/packages/app-shell/src/views/studio-design/BuilderLanding.setupExit-12513.test.tsx", "ui/packages/i18n/src/namespaces/studio/en.ts", "ui/packages/i18n/src/namespaces/studio/zh.ts" ], "deviations": [ "Declaration line written in the closed-arm form 'Clause-②: yes (widening)' (precedent #28552 / PR #28606) in PR body and changeset; the claim's parenthetical reason is not copied into it.", "Four existing landing tests outside the new file got a 5-line vi.mock of the new reader (pending promise): the new read otherwise hit a real socket and the ui network-escape guard failed them. Within the claim's 'their tests' surface.", "Reader uses fetch like its packages-io neighbours rather than @objectstack/client packages.customizations (ui/AGENTS.md section 7 prefers the client); noted in Acceptance notes.", "Harness asked for PR assignee/reviewer via GitHub MCP; skipped per lane rule 3/10. The relay pr_create read back as UNCONFIRMED (exit 6, no run seen within 90 s); a PR list read then showed #28634 created by objectstack-fleet[bot], body read back intact, so no second dispatch was sent.", "The 'other locale packs' are not separate files: the studio namespace ships en and zh only, other languages fall back to en by design." ], "mcp_calls": "6 — reads only: issue_read x4 (get, get_comments x3), list_pull_requests x1, pull_request_read get x1, plus non-GitHub get_session/subscribe_pr_activity; 0 MCP writes", "api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/pulls via fleet-write dispatch), this os-dev-report comment via post-stamped (POST /repos/objectstack-ai/objectstack/issues/28566/comments)", "open_questions": [], "out_of_scope_findings": [ "carrier: #28622 · the customizations route is in the route ledger but not mounted on main; this PR shows the failed state live until it lands · noted, not filed" ] }objectstack-fleet commentedon Nov 1, 2026
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y
Unblocked · 2026-11-01T08:50Z ·
pm:blocked→pm:queueBlocked-by: #28411is met: runtime: per-package customization read — for an installed base, its.customoverrides, its object extensions and its switched-off items, served rows only, with counts #28411 closed completed, landed by PR feat(runtime,spec): GET /packages/:id/customizations answers a base's overrides, extensions and switched-off items in one read #28595 as519089e1(git merge-base --is-ancestor 519089e1 origin/main: yes).GET /packages/:id/customizationsis served and@objectstack/clientcarriesclient.packages.customizations(id)atorigin/main(packages/client/src/index.ts:3120);@object-ui/app-shelltakes@objectstack/specasworkspace:^, so no pin bump stands between the read and this card.GetPackageCustomizationsResponseSchemain@objectstack/spec/api.studio-design/BuilderLanding.tsx(the landing's button grid, not the read-only cards) and the studio i18n packs. The claim names it as a serial constraint.Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y
Claim: PM loop round 3 · 2026-11-01T08:55Z
Session:
session_01QsR2jRL4qcnwHDD4M1e4wx(card thread, os-dev); dispatched by seat sessionsession_01QsR2jR7ZFkpqmm4U8S1f9yAccount:
zhuangjianguoBranch:
claude/project-thread-9g21fi(platform-assigned to the card thread; it can push only this branch)Worktree:
objectstack-issue-28566Domain:
domain:uiSeat:
domain:ui#4File surface:
ui/packages/app-shell/src/views/studio-design/BuilderLanding.tsx,packages-io.tsor a small read helper beside it, their tests, and the locale packs for the new landing strings (stop on breach; explain in the report)Container & model: S,
mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate),effort: highClause-②: yes (widening: new
@object-ui/i18nlanding keys; owes this seat'sTIER_DEFAULTreview)Metadata-protocol: none
Responsibility:
Studio's landing draws read-only base cards with no customization count | GET /packages/:id/customizations (PR #28595, 519089e1) serves overrides, extensions, switched-off items and counts per base | an administrator checking what they changed before an upgradeThread-read: 6555740870
Premise: holds (BuilderLanding's read-only cards carry no count at
origin/main;client.packages.customizationsis served)Serial constraints cleared: #28561 (claimed by this seat, branch
claude/project-thread-ddww6q, PR not yet open) also editsBuilderLanding.tsx(button grid) and the studio i18n packs; whichever lands second mergesmainand resolvesGenerated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y
ACCEPT → PR #28634 comment 6557136505 · 2026-11-01T11:10Z · contract review
TIER_DEFAULTPASS 6557127021 · live probe drew the counts after #28622 landed (28ef35e0) · landing through the merge queue.Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y
Landed · 2026-11-01T11:27Z · PR #28634 merged through the merge queue as
5e879eab. Records: contract reviewTIER_DEFAULTPASS 6557127021 and ACCEPT 6557136505, both at1c9ccf15.5e879eab~1)origin/main)fetchPackageCustomizationCountsinstudio-design/BuilderLanding.tsxGetPackageCustomizationsResponseSchemainstudio-design/packageCustomizations-io.tslanding.customizationskeys ini18n/src/namespaces/studio/zh.tsThe live probe at
1c9ccf15(after #28622 landed) drew "0 customized, 1 switched off" from one 200 read. The card closed throughFixes.pm:dispatchedcomes off in the same stroke as this note.Generated by Claude Code