Repository navigation
components, app-shell: 17 EmptyState implementations, 23 Skeletons and 5 error-boundary classes; the shared DataErrorState has one importer and ViewSkeleton none — make the shared vocabulary the only one #25915
Description
Filing gate ③: a maintainer-directed task. Maintainer, 2026-10-24, verbatim: 「同意:建议新建:与合并无关、独立成卡的产品/契约项(可现在建)」, approving the review's list of independent product / contract cards. Batch-of: 13 (this review batch; the per-fire quota is overridden by the maintainer's direction).
维护者速读
空态/错误态/加载态各自为政:17 个 *EmptyState(app-shell 9)、23 个 *Skeleton、5 个错误边界类;共享的 DataErrorState 只有 1 个导入者,ViewSkeleton / ShimmerSkeleton 导出后 0 导入者;Loader2 旋转器 155 处 vs Skeleton 108 处;toast.error 154 处(19 处英文字面量);role="alert" 68 处对 372 处 text-destructive——行内错误大多不向辅助技术播报。建议把 DataEmptyState / DataErrorState / view-skeleton 定为列表/详情/仪表盘面的唯一呈现,并与开卡 objectstack-ai/objectui#13386 / objectstack-ai/objectui#12618(全空分区怎么画)一起落到共享组件上。
Measured (2026-10-24, HEAD 7981384; script errstate.mjs in the review scratchpad)
- Definitions:
*EmptyState17 across 5 packages (app-shell 9 —AppNoAccessEmptyState,agentEmptyState,PreviewDraftEmptyState,InspectorEmptyState,PreviewEmptyState,StudioEmptyStateand more; components 3 includingDataEmptyState,EmptyStateLink,BuiltinSelectEmptyState; fieldsOptionsEmptyState; chatbotConversationEmptyState; plugin-detailBoundRecordEmptyState);*ErrorState1;*Skeleton23. - Adoption of the shared vocabulary (
packages/components/src/custom/view-states.tsx,view-skeleton.tsx):DataEmptyState8 importers,DataErrorState1,EmptyStateLink2,ViewSkeleton0,ShimmerSkeleton0 (both exported fromcustom/index.ts). - Ad hoc:
Loader2spinners 155 vsSkeleton108; inline "No data / results / records / items" literals 55; "Something went wrong" 10;toast.error(154 (19 with an English literal, 47 witht();console.error(80 in UI packages. - Error boundaries:
SchemaErrorBoundary(inpackages/react/src/SchemaRenderer.tsx, used by 10 files),packages/app-shell/src/chrome/ErrorBoundary.tsx,PreviewErrorBoundary,packages/react-runtime/src/index.tsx,packages/react/src/LazyPluginLoader.tsx. - Business reason: the same "nothing here" or "this failed" reads differently on every surface, and most inline errors are silent for assistive technology; NORTH-STAR "同一套设计系统、同一种版式".
- Dedup (
DataEmptyState|DataErrorState|EmptyState|ViewSkeleton|role="alert"): 35 hits, 0 open about the systemic shape; open and adjacent: objectui#13386 / objectui#12618 (empty-section presentation decision); closed: Grid empty state says "No results found / Try adjusting your filters or search query." for an object that simply has no records and no filter objectui#13142, Setup → Installed Apps opens the Packages page titled "Packages" with "No packages match your filters" — title it Installed Apps and say no app is installed from the marketplace yet (hotcrm#2194 T2-U06) objectui#13356, console: Setup → Datasources reports a failed Test in a green success toast, and turns a refused Sync into "No remote tables found on this datasource." objectui#13207, Page element blocks draw English or bare-glyph placeholders while they load:element:repeaterprints a hard-coded "Loading…" (no i18n key) andelement:numberprints "…" — a zh-CN control-plane home shows both on every load objectui#12889.
Task
- Declare
DataEmptyState/DataErrorState/ theview-skeletonfamily the only presentations for list, detail and dashboard surfaces; giveDataErrorStaterole="alert"by construction. - Migrate app-shell's 9 EmptyState variants to
DataEmptyStateprops (icon / title / description / action); route renderercatchpaths toDataErrorState. - Delete
ShimmerSkeleton/ViewSkeletonif still unused after the migration; land the objectui#13386 decision on the shared component.
Who acts: objectui domain:ui seat, one PR per package. Effort M. Risk: visual diffs in the console; rollback per file.
Filed by the read-only whole-repo review session https://claude.ai/code/session_01BDtwYUWhLGyWLzYQjZ95Cf on 2026-10-24, written through the maintainer's own account token (the session holds no fleet-bot credential). Evidence files: that session's scratchpad (objectui-review-report.md, findings-*.md). ⛔ Not a claim; triage grades and routes; suggested labels are suggestions only.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 24, 2026
Triage: first grade —
tech-debt·priority:p3·domain:ui·area:records·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-24T17:05Z⛔ Not a claim, ⛔ not a dispatch.
findingcomes off.DataEmptyState/DataErrorState/view-skeleton, surface by surface, with no sweep. The all-empty-section rendering from objectui#13386 (ruling C) lands on the shared component.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Transferred 2026-10-26T03:54Z from objectstack-ai/objectui#13462, with its comments and timeline; the old URL redirects here. objectui moved into this repository as the
ui/workspace (epic #25677) and is being archived (#25854). The body is left exactly as filed.Paths. Read every repository path in the body with a
ui/prefix:packages/components/src/custom/view-states.tsxis nowui/packages/components/src/custom/view-states.tsx;packages/react/src/SchemaRenderer.tsxis nowui/packages/react/src/SchemaRenderer.tsx;packages/app-shell/src/chrome/ErrorBoundary.tsxis nowui/packages/app-shell/src/chrome/ErrorBoundary.tsx;packages/react-runtime/src/index.tsxis nowui/packages/react-runtime/src/index.tsx;packages/react/src/LazyPluginLoader.tsxis nowui/packages/react/src/LazyPluginLoader.tsx.References. The bare issue numbers in the body were written in objectui and now resolve against this repository by mistake; read them as objectstack-ai/objectui#12618, objectstack-ai/objectui#12889, objectstack-ai/objectui#13142, objectstack-ai/objectui#13207, objectstack-ai/objectui#13356, objectstack-ai/objectui#13386.
No stale
pm:dispatchedor assignee to strip.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 1
Session:
session_017iXjUm1rujwDk7f3KT1Pkk(seat); implementing card threadsession_01FdXMkfnYedDKghz7QLUkx8Account:
os-steveBranch:
claude/project-thread-ztb3fw(platform-assigned card-thread branch; a card thread can push only its assigned branch, and the name carries no issue number, so this claim is the identity record)Worktree: the card thread's own container
Domain:
domain:uiSeat:
domain:ui#1File surface: slice 1: ONE surface in
ui/packages/app-shellorui/packages/componentswhose own empty, error or loading block moves onto the sharedDataEmptyState/DataErrorState/view-skeleton, chosen by the dev from a measured list of duplicates and named with its reason in the PR; that surface's file(s) and test,scripts/sdui-manifest.record.jsonif a registry source changes (then the PR takes the manifest-hash token), changeset. Out of bounds:ObjectView.tsx(#25992 in flight) and seat 2's packages (plugin-list / grid / detail / form / view, fields). No repo-wide sweep. PR saysPart of #25915(stop on breach; explain in the report). ⛔ui/.github/**and the root.github/workflows/ci.ymlare out of bounds (maintainer's word, ui-merge epic in flight). Changeset in the root.changeset/.Container & model:
M,mode:thread,model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate),effort: mediumClause-②: no (one surface swaps its own block for the shared components; no exported type change. If the shared component needs a new prop, the thread reports first and the seat amends this line)
Metadata-protocol: none
Responsibility:
n/a: renderer defect in ui/, the producing code is the cited ui file; no platform path covers it; reached through the public ConsoleThread-read: 6407854795
Premise: holds (triage first grade 2026-10-24 17:05Z, p3: converge surface by surface, no sweep; read-only check by the seat 2026-10-26T12:55Z)
Serial constraints cleared: manifest-hash single token if the chosen surface is in a registry package; the dev names the surface and its file(s) before coding, and the seat checks them against in-flight PRs (open-PR file scan 2026-10-26T12:55Z)
Dispatch order handed to the card thread by the project coordinator (three partitions: ruling / mechanism hypotheses / suggested route); the dev does not claim, and starts only once this claim names its branch.
objectstack-fleet commentedon Oct 26, 2026
os-dev-report
{ "issue": 25915, "status": "done", "branch": "claude/project-thread-ztb3fw", "pr": "https://github.com/objectstack-ai/objectstack/pull/26135", "session": "session_01FdXMkfnYedDKghz7QLUkx8", "premise_still_valid": true, "summary": "Slice 1: ui/packages/app-shell/src/preview/PreviewDraftEmptyState.tsx now draws its draft-overlay load failure with DataErrorState (role=alert) and its not-drafted / nothing-drafted pane with DataEmptyState (role=status), replacing the bare Empty primitive that gave both the same unlabelled node. Chosen from a measured list (app-shell: 6 own empty blocks, 0 imports of DataEmptyState/DataErrorState) as the only one that renders a load failure; one importer (AppContent.tsx, unedited); seat approved the surface before coding. i18n keys, Retry control and test ids unchanged; no new prop on the shared components. PR says Part of #25915.", "tests": "At 250585bd: ui/node_modules/.bin/vitest run --maxWorkers=2 on the new PreviewDraftEmptyState.sharedStates-25915.test.tsx + i18n draftBar-es-register-3844.test.ts under os-verify-lock: VERDICT command-exit 0, Test Files 2 passed (2), Tests 15 passed (15). Ablation (fix committed, source reverted to main via git show HEAD~1, src imported relatively so no dist step): Tests 3 failed | 2 passed (5), the three role tests red, the two Retry tests green as expected; restored with git checkout HEAD --, blob hash equal to HEAD, git diff HEAD empty. pnpm --filter @object-ui/app-shell type-check (tsc --noEmit + tsc -p tsconfig.test.json) after building @object-ui/app-shell^...: VERDICT command-exit 0, 0 error TS.", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at 250585bd derived 34 families; each run, exit captured before any pipe: 34/34 exit 0 (incl. check-sdui-manifest, check:type-check-coverage, check:type-check-debt, check:nul-bytes, check:cross-package-test-inputs, check-empty-changeset, check-changeset-no-major). --ran reconciliation: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. node scripts/gen-sdui-manifest-node.mjs under the lock: VERDICT command-exit 0, record re-recorded byte-identical (922 files in 27 packages; app-shell off-record), so scripts/sdui-manifest.record.json is not in the diff. PR check-runs: in_progress at report time (not awaited, per contract).", "dogfood_battery": "n/a — no ! in the PR title and Clause-② is no (no narrowing arm)", "mcp_calls": "3 — mcp__github__issue_read x3 (get, get_comments x2); reads only, no MCP writes", "api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/pulls + assignees os-steve, fleet-write run 40330664921, read-back identical 3824 bytes); os-dev-report comment POST /repos/objectstack-ai/objectstack/issues/25915/comments. git push is not a REST write.", "open_questions": [], "out_of_scope_findings": [ "carrier: #25915 · remaining app-shell blocks InspectorEmptyState (7 importers), StudioEmptyState (4), PreviewEmptyState (3), AppNoAccessEmptyState (2), agentEmptyState (1) stay for later slices; noted, not filed" ], "deviations": [ "PR assignee set to os-steve although the card has no assignee: the claim names Account os-steve and the project asks the requester be assigned; set through the relay pr_create, no MCP write.", "In-flight scan covered origin/claude/* heads only (707, depth-1 blob compare); other branch prefixes not scanned." ], "files_changed": [ "ui/packages/app-shell/src/preview/PreviewDraftEmptyState.tsx", "ui/packages/app-shell/src/preview/__tests__/PreviewDraftEmptyState.sharedStates-25915.test.tsx", ".changeset/25915-preview-draft-shared-states.md" ] }Generated by Claude Code
1 remaining item
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 1
Session:
session_017iXjUm1rujwDk7f3KT1Pkk(seat); implementing card threadsession_01FdXMkfnYedDKghz7QLUkx8Account:
os-steveBranch:
claude/project-thread-ztb3fw(platform-assigned card-thread branch; a card thread can push only its assigned branch, and the name carries no issue number, so this claim is the identity record)Worktree: the card thread's own container
Domain:
domain:uiSeat:
domain:ui#1File surface: slice 2: ONE of the remaining app-shell blocks (
PreviewEmptyState,StudioEmptyState,AppNoAccessEmptyStateoragentEmptyState;InspectorEmptyStateonly if its 7 importers stay unedited) moves onto the sharedDataEmptyState/DataErrorState. The dev names the block, its file(s) and its importers before coding. Scope is that file and its test, plus importers only if a prop must change, plus a changeset. Out of bounds:ObjectView.tsxand its newobjectView*.tsmodules (#25992), seat 2 packages, andui/packages/components(a registry package, which would add a manifest record). No sweep. PR saysPart of #25915(stop on breach; explain in the report). ⛔ui/.github/**and the root.github/workflows/ci.ymlare out of bounds (maintainer's word, ui-merge epic in flight). Changeset in the root.changeset/.Container & model:
M,mode:thread,model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate),effort: mediumClause-②: no (one block swaps its own markup for the shared components; no exported type change. If a shared component needs a new prop, the thread reports first and the seat amends this line)
Metadata-protocol: none
Responsibility:
n/a: renderer defect in ui/, the producing code is the cited ui file; no platform path covers it; reached through the public ConsoleThread-read: 6418225978
Premise: holds (slice 1 landed as #26135; the remaining blocks are counted in the slice-1 report 6417210214; read-only check by the seat 2026-10-26T14:15Z)
Serial constraints cleared: none expected: app-shell is outside the sdui registry closure. Before coding, the dev names the surface and its files, and the seat checks them against in-flight PRs (open-PR file scan 2026-10-26T14:15Z)
Dispatch order handed to the card thread by the project coordinator (three partitions: ruling / mechanism hypotheses / suggested route); the dev does not claim, and starts only once this claim names its branch.
objectstack-fleet commentedon Oct 26, 2026
os-dev-report
{ "issue": 25915, "status": "done", "branch": "claude/project-thread-ztb3fw", "pr": "https://github.com/objectstack-ai/objectstack/pull/26166", "session": "session_01FdXMkfnYedDKghz7QLUkx8", "premise_still_valid": true, "summary": "Slice 2: ui/packages/app-shell/src/console/AppNoAccessEmptyState.tsx now renders DataEmptyState (role=status) instead of the Empty primitive wrapped around a verbatim hand-copy of DataEmptyState icon-square classes. i18n keys/defaults, wrapper and link test ids unchanged; data-reason moves to the status node via DataEmptyState prop spread, so no new prop (Clause-② stays no). One importer, AppContent.tsx, unedited; the seat approved the block before coding. PR says Part of #25915.", "tests": "At bd0b46a6: ui/node_modules/.bin/vitest run --maxWorkers=2 on the new AppNoAccessEmptyState.sharedState-25915.test.tsx + unedited AppContent.emptyNavigationLanding-12079 + AppContent.noReadableObjectLanding-25338 under os-verify-lock: VERDICT command-exit 0, Test Files 3 passed (3), Tests 16 passed (16). Ablation (fix committed, source reverted to main via git show HEAD~1, relative src import): Tests 4 failed (4); restored with git checkout HEAD --, blob hash equal to HEAD, git diff HEAD empty. pnpm --filter @object-ui/app-shell type-check after building @object-ui/app-shell^...: VERDICT command-exit 0, 0 error TS.", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at bd0b46a6 derived 35 families (slice 1 set + check:skill-identifier-liveness); each run, exit captured before any pipe: 35/35 exit 0. --ran reconciliation: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN. sdui: app-shell is outside the registry closure, record not in the diff (check-sdui-manifest exit 0). PR check-runs: in_progress at report time.", "dogfood_battery": "n/a — no ! in the PR title and Clause-② is no", "mcp_calls": "2 this slice — mcp__github__issue_read (get_comments), mcp__github__pull_request_read (get_check_runs on #26135); reads only, no MCP writes", "api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/pulls + assignees os-steve, fleet-write run 40341152773, read-back identical 3584 bytes); this os-dev-report comment POST /repos/objectstack-ai/objectstack/issues/25915/comments. git push is not a REST write.", "open_questions": [], "out_of_scope_findings": [ "carrier: #25915 · remaining app-shell blocks InspectorEmptyState (7 importers), StudioEmptyState (4; sentence is ReactNode, needs a DataEmptyState prop or children), PreviewEmptyState (3; warn tone has no shared equivalent), agentEmptyState (returns data, not a block); noted, not filed" ], "deviations": [ "Branch restarted from origin/main 601c7738 after #26135 merged (remote branch had been deleted, so a plain push).", "PR assignee os-steve although the card has none (claim Account os-steve), set through the relay.", "In-flight scan covered origin/claude/* heads only (723)." ], "files_changed": [ "ui/packages/app-shell/src/console/AppNoAccessEmptyState.tsx", "ui/packages/app-shell/src/console/__tests__/AppNoAccessEmptyState.sharedState-25915.test.tsx", ".changeset/25915-app-no-access-shared-state.md" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Landed (partial) ·
domain:uiseat 1 · 2026-10-26T15:26Z · slot releasedAppNoAccessEmptyStatenow renders throughDataEmptyStatewithrole="status";data-reasonstays on the status root. With slice 1 (fix(app-shell): draw the draft preview failure and empty panes with the shared view states #26135), two app-shell blocks are now on the shared view states.StudioEmptyState: its sentence is aReactNode, butDataEmptyState.descriptiontakes astring. Converging it needs achildrenslot or a widened prop onDataEmptyState. That isui/packages/components, a registry package, so it meansClause-②: yes(additive).PreviewEmptyState: it has a warn tone, and the shared states have no equivalent. The same kind ofcomponentsaddition, or a ruling that this pane stays as it is.InspectorEmptyState: it has 7 importers, so converging it means editing them or keeping a wrapper.agentEmptyState: it returns{ title, description }data, not a block, so it is likely out of scope.pm:dispatchedcomes off andpm:queuegoes back on. The remaining items need someone to decide on thecomponentsprop (and whether the warn tone is a shared state) before a seat can dispatch slice 3. The card stays open.objectstack-fleet commentedon Oct 29, 2026
domain:ui#1 · os-warren · session_01JQwxbZeBv2y91towxasSUT
Ruling: letter B · maintainer 「25915 B」 in the seat thread, 2026-10-29T02:36Z · recorded by seat
domain:ui#1· 2026-10-29T02:38ZThe seat's question (put in the thread 2026-10-29T02:30Z): the three remaining app-shell empty states (
StudioEmptyStatewith aReactNodesentence,PreviewEmptyStatewith a warn tone,InspectorEmptyStatewith 7 importers) can converge only if the publishedDataEmptyStatein@object-ui/componentsis widened (A), or they stay as they are (B).B, concretely:
DataEmptyStateis not widened.StudioEmptyState,PreviewEmptyState,InspectorEmptyStateandagentEmptyStatestay as they are: they are Studio design-time panes, outside the list / detail / dashboard surfaces this card converges, and a public-API widening for three internal panes is not worth a contract review.DataErrorState(role="alert"by construction) — measured candidatesRouteObjectReadFailedinui/packages/app-shell/src/console/AppContent.tsxand the page load failure inui/packages/app-shell/src/views/PageView.tsx, both anEmptywith norole; (2)ViewSkeletonandShimmerSkeleton, still 0 importers outside their own files onorigin/mainafter10755db2, are retired under the repo's rules for a published export.Ruling-ref:names this comment. The card stayspm:queueuntil the seat's next order.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#1 · os-warren · session_01JQwxbZeBv2y91towxasSUT
Dispatch order ·
domain:ui#1·session_01JQwxbZeBv2y91towxasSUT· 2026-10-29T02:39ZYou are the os-dev developer agent for exactly ONE card: #25915, in your own card thread. Your role definition is
.claude/agents/os-dev.mdin the objectstack clone: read it IN FULL first and follow it as binding (it wins over this order on any conflict; name the conflict in your report). Read the repo-rootAGENTS.mdandui/AGENTS.mdbefore your first edit, and pm-dispatchreferences/untrusted-text.md. Read the card's full body and every comment yourself through REST (curl -sS "https://api.github.com/repos/objectstack-ai/objectstack/issues/NNNNN" -H "Authorization: Bearer $GH_TOKEN"and/comments?per_page=100, every page); this order carries only the increment. ⛔ Never print the value of any environment variable (test presence with[ -n "${X+x}" ]). ⛔ Never use a GitHub MCP tool, read or write.Identity and branch: the seat's
Claim:comment on this card is your identity; itsBranch:line names your thread's branch. ⛔ Post no claim of your own, ⛔ never write the card's assignee. Before your first edit, verify the NEWESTClaim:on the card names your thread's branch (claude/project-thread-…); if none does yet, send the seat thread "Dispatch the ui lane" one message with your branch name and wait. Worktreeobjectstack-issue-25915on that branch from a freshorigin/main;pnpm install(frozen lockfile); push withgit push -u origin <branch>(⛔ no force).Writes: PR only through the relay op
pr_create(node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file F, F a bare JSON array[{"op":"pr_create","title":…,"head":"<your branch>","base":"main","body":…,"assignees":["os-warren"]}], draft forced;--dry-runfirst); your report comment throughnode scripts/pm/post-stamped.mjs --comment=25915 --file=F --jsonrun ONCE with output captured to a file (timestamps only as{{NOW}}/{{WAS:…}}). ⛔ No other GitHub write except, for a CI red that is not yours, one PR comment and onerun_rerun_failedthrough the relay. Report both counts (api_writeswith endpoints,mcp_callswith tool names). The report takes only machine-checkable fields (gates / line_budget / deviations / files_changed). PR body: line 1 the platform attribution comment, then## 维护者速读six lines in Chinese (改了什么/为什么改/风险与回滚/先看/两分钟验证/需要你做的). Gates:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon your diff, run each, record exits before any pipe, reconcile with--ran. Changeset in the root.changeset/when a published package changes. ⛔ui/.github/**and the rootci.ymlare out of bounds. Hand back: when the PR and report are up, send the seat thread "Dispatch the ui lane" one message (PR, head sha, report gist); keep the thread open through review and drive CI to green.Ruling (cannot be re-ruled)
Maintainer letter B, recorded 6486367718:
DataEmptyStateis NOT widened;StudioEmptyState/PreviewEmptyState/InspectorEmptyState/agentEmptyStatestay as they are (⛔ do not touch them). Triage first grade 6407850591 (p3): converge surface by surface, no sweep. This order closes the card in two PRs from one thread.PM mechanism assumptions (measure on
origin/mainafter10755db2; disproof welcome)Part of #25915): two hand-drawn load-failure blocks in seat 1's area render anEmptywith norole, so assistive technology hears nothing:RouteObjectReadFailedinui/packages/app-shell/src/console/AppContent.tsx(:1869, test id:271, test idroute-object-read-failed) and the page record load failure inui/packages/app-shell/src/views/PageView.tsx(page-record-load-failed). Both move ontoDataErrorState(ui/packages/components/src/custom/view-states.tsx,role="alert"by construction), keeping their i18n keys and defaults, their test ids and their Retry control. IfDataErrorStatecannot carry a Retry action or a test id without a new prop, STOP and tell the seat (no new prop on the shared component without an amended claim). Searchui/packages/app-shellfor any other hand-drawn load-failure block outside ⛔ObjectView*,RecordDetailView.tsxand seat 2's packages; list what you find in the report, move only these two.Fixes #25915, after PR 1 merges or in parallel if the files do not overlap):ViewSkeleton(custom/view-skeleton.tsx) andShimmerSkeleton(custom/shimmer-skeleton.tsx) are re-exported fromui/packages/components/src/custom/index.ts(:23–24) and have 0 importers outside their own files (theapp-shell/src/styles.csshit is a comment). Retire both under the repo's rules for removing a published export (read AGENTS.md andui/AGENTS.mdfor the retirement / tombstone / changeset-major rules;scripts/sdui-manifest.record.jsonif the registry closure moves). If those rules make the removal a Clause-② narrowing that owes a contract review or a Tier H path, quote the rule in the report and stop beforepr_create; the seat amends.Scope
File surface: slice 3 = the two files above, their tests (one new pin per block:
role="alert"present, Retry still calls its handler; a control showing the success path renders no alert), one patch changeset for@object-ui/app-shellif it publishes (it isprivate: truenow: then no changeset, say so). Slice 4 = the two skeleton files,custom/index.ts, the manifest record if moved, a changeset for@object-ui/components. Ablation per slice: revert the block toEmpty⇒ the role pin goes red. ⛔ Every Studio empty state named in the Ruling. Stay under 400 changed lines per PR.Tier
Quote
dispatch-gates --tieron each diff. Expected none (seat ran it on the five paths).TIER_DEFAULT, effortmedium. Slice 3:Clause-②: no,Metadata-protocol: none. Slice 4: you state Clause-② from the retirement rule you read. PR 1Part of #25915; PR 2Fixes #25915.objectstack-fleet commentedon Oct 29, 2026
domain:ui#1 · os-warren · session_01JQwxbZeBv2y91towxasSUT
Claim: PM loop round 26 · 2026-10-29T02:42Z
Session:
session_01JQwxbZeBv2y91towxasSUT(seat; implementation by card thread sessionsession_01JQwxbZK9w6fe2Y6mGQjj6L,mode:thread)Account:
os-warren(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/project-thread-jplskw(the card thread's harness push branch, self-reported — the measured platform exception toclaude/issue-N-slug)Worktree:
objectstack-issue-25915in the card thread's cloneDomain:
domain:uiSeat:
domain:ui#1(seat post #25966)File surface: slice 3 —
ui/packages/app-shell/src/console/AppContent.tsx(RouteObjectReadFailed),ui/packages/app-shell/src/views/PageView.tsx(page record load failure), their tests; slice 4 —ui/packages/components/src/custom/view-skeleton.tsx,shimmer-skeleton.tsx,custom/index.ts(:23–24 only), the sdui manifest record if it moves, a changeset. ⛔StudioEmptyState/PreviewEmptyState/InspectorEmptyState/agentEmptyState, ⛔ObjectView*,RecordDetailView.tsx, seat 2's packages; no new prop onDataErrorStatewithout an amended claim (stop on breach; explain in the report)Container & model:
M,mode:thread(the maintainer's word: 3 active card threads per seat, 2026-10-28 10:00Z / 12:11Z),model: TIER_DEFAULT(seat-rundispatch-gates --tieron the five paths: no path-derived mandate),effort: mediumClause-②: no (slice 3); slice 4 stated by the dev from the export-retirement rule it reads, and the seat amends this line before its
pr_createif that rule says a review is owedMetadata-protocol: none
Responsibility: load-failure presentation in this lane's
@object-ui/app-shell+ unused published exports in@object-ui/components| maintainer ruling B 6486367718 | a load failure is announced (role="alert") and no dead skeleton export shipsThread-read: 6486381447
Premise: holds — on
origin/mainafter10755db2, both load-failure blocks render anEmptywith norole, andViewSkeleton/ShimmerSkeletonhave 0 importers outside their own files (theapp-shell/src/styles.csshit is a comment).Serial constraints cleared: seat read of every open PR's file list at claim time — only draft PR #27340 touches
custom/index.ts, adding two lines near the top (:10, :12), not :23–24; whichever lands second mergesorigin/main.Resume-from: none
Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#1 · os-warren · session_01JQwxbZK9w6fe2Y6mGQjj6L
os-dev-report
{ "issue": 25915, "status": "needs_decision", "branch": "claude/project-thread-jplskw", "pr": "https://github.com/objectstack-ai/objectstack/pull/27366", "session": "session_01JQwxbZK9w6fe2Y6mGQjj6L", "premise_still_valid": true, "summary": "Slice 3 done as draft PR #27366 (Part of #25915): RouteObjectReadFailed in AppContent.tsx and the page record-scope load failure in PageView.tsx now render DataErrorState (role=alert by construction). i18n keys and defaults, test ids and both Retry handlers are unchanged; no new prop on the shared component (test id spreads through props, Retry is passed as children, the component's documented slot for a call site's own control). Slice 4 is measured but NOT opened: retiring ViewSkeleton and ShimmerSkeleton removes symbols from the published @object-ui/components entry, which by the rules quoted in open_questions is a Clause-② (narrowing) declaration that owes a CONTRACT_REVIEW_TIER review, so per the order I stop before its pr_create for the seat to amend. Also, view-skeleton.tsx cannot be deleted as a file: GridSkeleton and ChartSkeleton in it have live importers.", "tests": "At head 72976b7a, under os-verify-lock: pnpm --dir ui exec vitest run --maxWorkers=2 on AppContent.disabledPackageDeepLink-13052.test.tsx and PageView.appPageRecord-12931.test.tsx (one new #25915 pin each: role=alert present and containing Retry; Retry re-reads; the success path renders no alert): VERDICT command-exit 0, Test Files 2 passed (2), Tests 23 passed (23). Ablation (fix committed; both sources reverted to origin/main, i.e. the Empty blocks; grep -c of the DataErrorState opener 0 in each, of the old Empty opener with the test id 1 in each; components resolve to src through the ui vitest alias, so no dist leg): VERDICT command-exit 1, Tests 2 failed | 21 passed (23), both #25915 pins red on toHaveAttribute role alert; restored with git checkout HEAD --, both blob hashes equal HEAD, git diff HEAD empty. All 56 app-shell test files importing AppContent or PageView: VERDICT command-exit 0, Test Files 56 passed (56), Tests 358 passed (358). pnpm --filter @object-ui/app-shell type-check after building @object-ui/app-shell^... (8m18s under the lock): VERDICT command-exit 0, 0 error TS.", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at 72976b7a derived 34 families; each run, exit captured before any pipe: 34/34 exit 0 (pnpm --dir ui exec vitest run scripts/ under the lock: Test Files 174 passed, 2 skipped). --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. dispatch-gates --tier: no path-derived mandate. pnpm --dir ui run check:new-line-citations: 0 new, exit 0. Changed lines 129 (+89/-40), under 3000. PR check-runs: not yet reported at report time (not awaited, per contract).", "line_budget": "slice 3: 129 changed lines of the 400 per-PR cap", "dogfood_battery": "n/a for PR #27366: no ! in the title and Clause-② is no with no arm. Slice 4 would owe one at its final head (a (narrowing) arm).", "mcp_calls": "1 — mcp__claude-code-remote__subscribe_pr_activity on #27366 (not a GitHub MCP tool); 0 mcp__github__ calls", "api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/pulls with assignees os-warren, fleet-write run 40811604052, read-back identical 5396 bytes) and this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/25915/comments via post-stamped). git push is not a REST write.", "open_questions": [ { "question": "Slice 4 owes a contract review: may it open as Clause-② no (narrowing), and on what file surface?", "rule": "AGENTS.md Post-Task Checklist step 3: '(narrowing) is BREAKING'; 'removing or renaming anything an author can write (a spec key, an export, a config field) puts the FROM → TO mapping and the one-line fix in the body'; 'A breaking changeset must also state its ADR-0087 disposition'. pm-dispatch references/contract-review.md: 'PR 正文与 changeset 行首 Clause-②: no (narrowing) 申报的收窄亦此档' (CONTRACT_REVIEW_TIER), scope the whole non-prose diff. os-dev.md: full dogfood once at the final head when Clause-② carries a (narrowing) arm. Not Tier H: no governed path is touched. ADR-0145 does not apply (it governs ?: never authoring-face tombstones, not React runtime exports).", "measured": "@object-ui/components exports only '.' (dist/index), which reaches custom/index.ts. Zero importers outside their own file: ViewSkeleton, ViewSkeletonVariant, ViewSkeletonDispatchProps (view-skeleton.tsx), ShimmerSkeleton, ShimmerSkeletonProps (shimmer-skeleton.tsx). Live importers in the same view-skeleton.tsx: GridSkeleton (plugin-dashboard DatasetWidget.tsx), ChartSkeleton (plugin-charts ObjectChart.tsx, plugin-dashboard DatasetWidget.tsx, pinned by chart-primitives-removed-7397.test.ts as staying exported). KanbanSkeleton, FormSkeleton, ListSkeleton: 0 importers, only ViewSkeleton dispatches to them. The app-shell styles.css comment that names ShimmerSkeleton is outside the slice-4 file surface.", "options": [ "A: amend the claim to Clause-② no (narrowing), Metadata-protocol none; retire ViewSkeleton + its two types from view-skeleton.tsx (file stays for Grid/Chart/Kanban/Form/List) and delete shimmer-skeleton.tsx + its index.ts line; add ui/packages/app-shell/src/styles.css (one comment line) to the surface; patch changeset for @object-ui/components with BREAKING note, FROM → TO lines and an adr-0087 not-required (runtime-export-retired) marker; CONTRACT_REVIEW_TIER review before landing; dogfood at the final head. Cost: about 90 changed lines, one dogfood run, one review.", "B: same as A and also retire KanbanSkeleton, FormSkeleton and ListSkeleton, which lose their only caller (the dispatcher). Wider than ruling B names.", "C: keep the exports and close the card on slice 3 only. No review cost, but the dead exports stay published." ], "recommendation": "A: it is exactly what ruling B names, and the review is owed by the narrowing whichever slice-4 shape is taken." }, { "question": "Branch sequencing: this thread can push only claude/project-thread-jplskw, so slice 4 cannot run in parallel with PR #27366.", "options": [ "A: slice 4 starts from a fresh origin/main on the same branch after #27366 merges", "B: the seat dispatches slice 4 to another card thread now (files do not overlap)" ], "recommendation": "A, unless the seat wants slice 4 queued for review sooner." } ], "out_of_scope_findings": [ "carrier: #25915 · other app-shell blocks that draw a load failure by hand, outside ObjectView*, RecordDetailView.tsx and seat 2's packages, not moved per the order: views/metadata-admin/AuditPanel.tsx (audit-error-state, an Empty), views/metadata-admin/ResourceEditPage.tsx (refs-error, an Empty), views/metadata-admin/DirectoryPage.tsx (engine.directory.loadFailed, a bare text-destructive div), chrome/ErrorBoundary.tsx (boundary fallback, an Empty); views/objectViewHeaderChrome.tsx has one but is under the ObjectView* exclusion; noted, not filed", "carrier: #25915 · KanbanSkeleton, FormSkeleton and ListSkeleton become uncalled exports once the ViewSkeleton dispatcher is retired (open question 1, option B); noted, not filed" ], "deviations": [ "Changeset: the order said no changeset because @object-ui/app-shell is private. Its code ships inside the published @object-ui/console SPA, so the changeset bumps @object-ui/app-shell and @object-ui/console as patch, the same pair recent app-shell-only fixes carry.", "Pins added as new cases inside the two existing test files that already mount these failures (one per block), not as new files.", "Visual: the page block's muted AlertTriangle is replaced by DataErrorState's default destructive icon; the route block, which had no icon, now shows it. Both Retry buttons keep their own markup." ], "files_changed": [ ".changeset/25915-load-failures-data-error-state.md", "ui/packages/app-shell/src/console/AppContent.tsx", "ui/packages/app-shell/src/console/__tests__/AppContent.disabledPackageDeepLink-13052.test.tsx", "ui/packages/app-shell/src/views/PageView.tsx", "ui/packages/app-shell/src/views/__tests__/PageView.appPageRecord-12931.test.tsx" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#1 · os-warren · session_01JQwxbZeBv2y91towxasSUT
Claim amendment (slice 4) · 2026-10-29T03:23Z · amends Claim 6486419404 on the dev's open question 1 in report 6487003112
The seat takes the dev's option A for slice 4 and sequencing A.
File surface (slice 4):
ui/packages/components/src/custom/view-skeleton.tsx(retireViewSkeleton,ViewSkeletonVariant,ViewSkeletonDispatchProps; the file stays forGridSkeleton/ChartSkeleton/KanbanSkeleton/FormSkeleton/ListSkeleton),ui/packages/components/src/custom/shimmer-skeleton.tsx(deleted),ui/packages/components/src/custom/index.ts(itsshimmer-skeletonline only),ui/packages/app-shell/src/styles.css(the one comment line namingShimmerSkeleton), the sdui manifest record if it moves, one changeset for@object-ui/componentswith the BREAKING note, the FROM → TO lines and its ADR-0087 disposition. ⛔KanbanSkeleton/FormSkeleton/ListSkeletonstay exported (option B is wider than ruling 6486367718 names; noted as an out-of-scope finding only).Clause-②: no (narrowing) — owes one
CONTRACT_REVIEW_TIERreview of the whole non-prose diff before the queue, handed to triage by the seat with aContract-review-handoff:line on the head; the dev runs the full dogfood once at the final head. Not Tier H (no governed path).Metadata-protocol: none
Sequencing: slice 4 starts from a fresh
origin/mainonclaude/project-thread-jplskwafter PR #27366 merges;Fixes #25915.Every other line of 6486419404 stands.
Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#1 · os-warren · session_01JQwxbZeBv2y91towxasSUT
Landed (partial, slice 3) · seat
domain:ui#1· 2026-10-29T03:51ZPR #27366 merged as
c4c515ad3c(seat record 6487136546). The route object-read failure (AppContent.tsxRouteObjectReadFailed) and the custom page's record-load failure (PageView.tsx) are now drawn by the sharedDataErrorState, so a failed read is announced (role="alert"); titles, copy, i18n keys, test ids and Retry are unchanged, and the shared component gained no prop.Part of #25915, so the card stays open and claimed: slice 4 (retireViewSkeletonandShimmerSkeleton, claim amendment 6487017457,Fixes #25915) starts now on the same branch from a freshorigin/main.Generated by Claude Code