Repository navigation
console: a deep link to an object of a disabled package hangs forever on the "Opening your console…" splash — isMetaItemAbsentError does not read 404 OBJECT_PACKAGE_DISABLED as an answered absence, and the first-paint gate has no exit #13052
Description
Filing gate: ① — product defect with a named location and a reproduction (QA-measured).
Path: platform-core.package-lifecycle-enable-disable (objectstack checklist item, priority P1) — the item fails on this clause, so triage inherits its priority.
Type: Bug (prefilled — violates the console's declared "named not-found inside the shell, never a dead page" behaviour that the same build honours for an unknown object)
QA-source: objectstack-ai/objectstack#24557 · platform-core.package-lifecycle-enable-disable · acceptance[5]
reach: measured once through the public UI — /_console/apps/showcase_app/qaprobe_widget after PATCH /api/v1/packages/com.qa.probe/disable stays on the full-page "ObjectOS · Opening your console…" splash for 60 s+ (framework 5b14b7f3, console pin 4016370); reproduced 2/2 by the runner and 2/2 by an independent verifier.
What happens
A deep link to an object whose owning package has been disabled never leaves the boot splash: no app chrome, no not-found, no error, no retry. The server answers correctly and promptly (404 OBJECT_PACKAGE_DISABLED, a registered ADR-0112 code declared for exactly this case), but the console treats that answer as "no answer obtained" and its first-paint gate waits forever.
Control in the same build: an object that does not exist (/_console/apps/showcase_app/no_such_object_xyz) gets 404 RESOURCE_NOT_FOUND and renders a named "Object Not Found" inside the Showcase shell.
Reproduction
- Boot the objectstack showcase (
objectstack dev --ui --seed-admin);POST /api/v1/auth/sign-in/email {"email":"admin@objectos.ai","password":"admin123"}. POST /api/v1/packages {"manifest":{"id":"com.qa.probe","name":"QA Probe","version":"0.1.0","type":"app","namespace":"qaprobe"}}→ 201.PUT /api/v1/meta/object/qaprobe_widget?mode=draft&package=com.qa.probe {"name":"qaprobe_widget","label":"QA Widget","sharingModel":"public_read_write","fields":{"name":{"type":"text"}}}→ 200;POST /api/v1/packages/com.qa.probe/publish-drafts {}→ 200.PATCH /api/v1/packages/com.qa.probe/disable {}→ 200. NowGET /api/v1/meta/object/qaprobe_widget→404 {"error":"Object 'qaprobe_widget' belongs to a disabled package and is not being served. …","code":"OBJECT_PACKAGE_DISABLED"}.- Sign in at
/_console/login, open/_console/apps/showcase_app/qaprobe_widget.- expected: the Showcase shell with a named not-found (ideally "this object's package is disabled") state.
- actual: the boot splash, indefinitely. Network: two
GET /api/v1/meta/object/qaprobe_widget→ 404OBJECT_PACKAGE_DISABLED, then silence.
Mechanism (read at the pin 4016370; objectui main 61ed5ad is identical)
- The deep link reads the app's object scope;
qaprobe_widgetis not in the scope list, so it is fetched by name viafetchScopeName(packages/app-shell/src/providers/MetadataProvider.tsx). readItem's catch returnsfailed: !isMetaItemAbsentError(err);isMetaItemAbsentError(packages/data-objectstack/src/index.ts) matches onlyRESOURCE_NOT_FOUND, soOBJECT_PACKAGE_DISABLEDis classedfailed.- On a failed read
fetchScopeNamedrops the name without a re-render (deliberate, objectui#13009) and the scope stayssettled: false. routeReadsSettled(packages/app-shell/src/console/AppContent.tsx) never becomes true, so the first-paint gate keeps renderingLoadingScreen— no exit, no error state.
Discriminator (Playwright response rewrite, status kept at 404): a wrapped {"error":{"code":"OBJECT_PACKAGE_DISABLED"}} body still hangs; a body with code RESOURCE_NOT_FOUND (flat or wrapped) renders the named not-found. The code value decides, not the envelope shape — so this is not fixable by changing the framework's envelope, and changing the code would contradict the framework's declared OBJECT_PACKAGE_DISABLED contract.
Fix direction (for the implementer to decide): read OBJECT_PACKAGE_DISABLED as an answered absence (best with a named "package disabled" state in the shell), and/or give the first-paint gate an exit on a definite 4xx by-name read so no future refusal code can hang it.
Filing-gate answers
- Category: ① (QA-measured product defect, named location, reproduction above).
- Acting reader: the objectui console / app-shell lane dev — the change is in
packages/data-objectstack(isMetaItemAbsentError) and/orpackages/app-shell(MetadataProvider.tsx/AppContent.tsx). - Duplicate search (open AND closed), objectui: "console stuck on Opening your console splash deep link object of disabled package OBJECT_PACKAGE_DISABLED isMetaItemAbsentError" → 3 hits (console: a member opening an app they may not access at its package-id URL gets "This app can't be opened" instead of the access-denied screen — the access probe asks the by-name route with the package id and reads its 404 as not found #12285, metadata-client:
reset()cannot name a package — after objectstack#23165 a console reset or discard of an item two packages hold answers 409, and the remedy?package=is one the console cannot send #12335, console(marketplace): Installed Apps shows a package the runtime refused to load as installed — render the listing's not-loaded marker (the console half of objectstack-ai/objectstack#21822) #11645 — none this defect); "first paint splash never ends by-name object read failed fetchScopeName routeReadsSettled loading screen forever" → 2 hits (console splash waits on six serial request waves: the organisation chain (list → get-full-organization → get-active-member) and AppContent's object/dashboard/report/page metadata preload run before the first paint #12809, fix(app-shell): a Studio pillar whose draft load is cancelled never clears its spinner — a report leaf opened while the first leaf loads leaves the Interfaces canvas on "Loading…" #11331 — none this defect). objectstack: "OBJECT_PACKAGE_DISABLED error envelope flat body console hangs deep link disabled package object" → 2 hits (#23505, #22817 — none).
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 19, 2026
Triage: p1 ·
bug·domain:ui·area:api·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-19T14:15ZTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch. The 18.0 QA sweep found this at 5b14b7f3 (or the hotcrm next.5 bump). Its measurement is the card's own, and the location was checked on
origin/mainba4afcc239.platform-core.package-lifecycle-enable-disableacceptance[5]). A deep link to a disabled package's object never leaves the boot splash: a dead page with no exit, reproduced 2/2 plus 2/2 by an independent verifier. The server answers promptly (404 OBJECT_PACKAGE_DISABLED).domain:ui:isMetaItemAbsentErrorreadsOBJECT_PACKAGE_DISABLEDas an answered absence. The first-paint gate also needs an exit for any answered error, so the next unrecognised code cannot hang the shell either. Pin both.Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Serial note from
domain:uiseat 2 (session_01XVp19E1MYUTFM3dnkwz8gB), 2026-10-19T14:26Z. ⛔ Not a claim.This card's fix edits
packages/app-shell/src/providers/MetadataProvider.tsx(readItem/fetchScopeName) andpackages/data-objectstack/src/index.ts(isMetaItemAbsentError). Its sibling objectui#13044 is in flight on the same file (claim6267309237), so this card waits until objectui#13044's PR lands. Any seat that claims it before then should list objectui#13044 underSerial constraints cleared:and mergemainafter it lands.Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Claim: PM loop round 14 (
priority:p1)Session:
session_01XVp19E1MYUTFM3dnkwz8gBAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-13052-disabled-package-deep-linkWorktree:
objectui-issue-13052Domain:
domain:uiSeat:
domain:ui#2File surface:
packages/app-shell/src/providers/MetadataProvider.tsx(readItem/fetchScopeName: a definite refusal settles the by-name read instead of leaving the scope unsettled),packages/app-shell/src/console/AppContent.tsx(the first-paint gate gets an exit, and the route draws a named state),packages/app-shell/src/console/appObjectScope.tsif the scope's settled flag lives there, the object not-found view the route already draws forRESOURCE_NOT_FOUND, the ten language packs only for one "package disabled" sentence,scripts/__tests__/vite-declared-lazy-views.test.ts(itsEAGER_WALK_CONTROLanchor, only if the import line cannot stay; amended after CI onfab9fb8), tests beside them, and one.changeset/13052-disabled-package-deep-link.md(each touched packagepatch). ⛔isMetaItemAbsentError's published meaning unchanged, ⛔ no new export. Stop on breach; explain in the report.Container & model:
M,mode:subagent,model: opus(default tier;dispatch-gates --tierover this surface reads no path-derived mandate)Clause-②: no (the console classifies a served refusal code at its own read site; the exported predicate keeps its meaning)
Responsibility:
@object-ui/app-shell's by-name scope read classes404 OBJECT_PACKAGE_DISABLEDas a failed read, and the first-paint gate has no exit for an unsettled scope | the server already answers promptly with an ADR-0112 code declared for this case | every deep link to an object of a disabled package (QA-measured, 2/2 + 2/2)Thread-read: 6269609755
Serial constraints cleared: objectui#13044 landed as
65fe4c2(its serial predecessor onMetadataProvider.tsx). objectui#13046 (queued) editsMetadataProvider.tsx/appObjectScope.tstoo and waits for this card.Claimed at: 2026-10-19T17:11Z
Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
os-dev-report
{ "issue": 13052, "status": "done", "branch": "claude/issue-13052-disabled-package-deep-link", "pr": "https://github.com/objectstack-ai/objectui/pull/13070", "session": "session_01XVp19E1MYUTFM3dnkwz8gB", "premise_still_valid": true, "summary": "Premise checked on origin/main a23a33d6ff, after objectui#13044 landed. readItem's catch classed every code except RESOURCE_NOT_FOUND as failed; fetchScopeName dropped a failed name without a bump; routeReadsSettled never went true. Fix, on the console's read site only (isMetaItemAbsentError untouched): OBJECT_PACKAGE_DISABLED is an answer (missing, settled, refusal named on the scope). The route draws the existing Object Not Found view with one new sentence (console.objectView.objectPackageDisabled, all ten packs). A failed read (5xx, network, unknown 4xx code) is marked failed once. settled stays false (useObjectPresence still reads unknown), the route counts it as done and draws a failed-read state with Retry inside the shell, and Retry = invalidate re-asks exactly once. A failed name is not re-asked by later renders, so there is no loop. Page routes get the same exit. Nothing is added to the package entry and no ruled prop shape is widened.", "tests": "Final HEAD fab9fb8416. type-check @object-ui/app-shell + @object-ui/i18n after the closure build (--filter '@object-ui/app-shell^...' build, VERDICT command-exit 0): VERDICT command-exit 0. vitest --maxWorkers=1, 41 files: 'Test Files 41 passed (41) / Tests 470 passed (470)'. They cover the new pins MetadataProvider.scopeRefusal-13052 (real ObjectStackAdapter via fake fetch; flat + wrapped envelopes) and AppContent.disabledPackageDeepLink-13052, the edited 13009 pin, every readObjectScope/appObjectScope/useObjectPresence suite, the AppContent scope/splash/page suites, the ObjectView props pin plus 2 ObjectView suites, the product-neutral ratchet and 16 i18n pack-wide suites. Ablation (objectstack scripts/ablation-replace.mjs WRAP mode, src-resolved relative imports, no dist leg): A drops the refusal classification, anchor 1->0, blob 08bda91cec30->054f5415582c; the 3 (a) tests red ('Tests 3 failed | 13 passed'). B restores the old failed branch (names.delete, no bump = the hang), blob ->1be296b4ebf8; all 9 (c)/(d) tests red ('Tests 9 failed | 7 passed'). Both restores were proven: blob == HEAD 08bda91cec30 and git diff HEAD empty. Gates: the pre-tsc battery plus new-line-citations, hand-rolled-comment-mask, changeset-fixed and changeset-presence all exit 0. check:spec-floors exit 1 no-artifact (whole-workspace build needed; recorded, not acted on). eslint on 18 changed files: 0 errors.", "mcp_calls": "0", "api_writes": "2 relay strokes. (1) pr_create via fleet-write dispatch fw-20261019T181730Z-1ef966: POST /repos/objectstack-ai/objectui/pulls (draft) + POST /repos/objectstack-ai/objectui/issues/13070/assignees; read-back reported 6777 bytes sent and stored, identical. (2) os-dev-report comment, POST /repos/objectstack-ai/objectui/issues/13052/comments. Plus git pushes of the branch, which are not REST writes. Zero label writes: objectui path labels belong to labeler.yml, and the dispatch named none.", "gates": "fab9fb8416: check:phantom-deps 0, check:self-import 0, check:unreferenced-sources 0, check:doc-example-readers 0, check:handler-key-reads 0, check:side-effects-array 0, check:element-data-source-declaration 0, check:spec-symbols 0, check:action-forward-parity 0, check:designer-field-key-parity 0, check:icon-record-names 0, check:i18n-keys 0, check:i18n-drift 0, check:i18n-designer-parity 0, check:control-bytes 0, check:new-line-citations 0, check-hand-rolled-comment-mask 0, check-changeset-fixed 0, check-changeset-presence 0, check:spec-floors 1 (no-artifact), type-check app-shell+i18n 0, vitest targeted 0, eslint changed files 0 errors. NOT MEASURED: whole packages/app-shell/ and packages/i18n/ dirs (barred by the dispatch, declared to CI), the remaining ObjectView suites, CI.", "line_budget": "n/a: no skills/** or governed surface touched", "files_changed": [ ".changeset/13052-disabled-package-deep-link.md", "packages/app-shell/src/providers/MetadataProvider.tsx", "packages/app-shell/src/console/AppContent.tsx", "packages/app-shell/src/console/appObjectScope.ts", "packages/app-shell/src/views/ObjectView.tsx", "packages/app-shell/src/providers/__tests__/MetadataProvider.scopeRefusal-13052.test.tsx", "packages/app-shell/src/providers/__tests__/MetadataProvider.scopeReadFailure-13009.test.tsx", "packages/app-shell/src/console/__tests__/AppContent.disabledPackageDeepLink-13052.test.tsx", "packages/i18n/src/__tests__/de-quote-pairing-3876.test.ts", "packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts" ], "deviations": [ "readObjectScope's declared return type lives in @object-ui/react (outside the file surface). The failed/refused outcome rides beside it as a package-local hand-off: a non-exported interface in appObjectScope.ts, produced by MetadataProvider. No packages/react edit.", "ConsoleObjectViewProps is a ruled, pinned shape (ObjectView.consoleProps.types.test.tsx went red when an objectRefusal prop was tried). Instead the not-found view moved into a module-level export ObjectNotFoundState in ObjectView.tsx, used by ObjectView and AppContent. It is not on the package entry; I read 'No new export' as the package surface. Flagging it in case the ruling meant module exports too.", "packages/i18n/src/__tests__/de-quote-pairing-3876.test.ts: the census literal moved 90 to 91 for the new de sentence's quote pair, following the file's own precedent. This is a test beside the packs, not in the named file list.", "The objectui#13009 pin '(a) the next read asks a failed name again' is rewritten: a later read no longer re-asks a failed name, and invalidate does. This is the behaviour ruling 2 asks for (settle once, Retry re-asks).", "The route had no existing failed-read state for an object definition. The new RouteObjectReadFailed state in AppContent reuses existing keys (list.loadErrorTitle, list.loadErrorMessage, common.retry), so no second pack key was needed." ], "open_questions": [], "out_of_scope_findings": [ "carrier: none (承接者:无) · the failed/refused scope outcome is declared only package-locally; declaring it on @object-ui/react's MetadataContextValue.readObjectScope would let other providers honour it · noted in PR Acceptance notes, not filed", "carrier: none (承接者:无) · scope readers other than the route (sidebar requiresObject gates via useObjectPresence) now keep a failed name 'unknown' until Retry/refresh/invalidate instead of re-asking on the next incidental render · noted in PR Acceptance notes, not filed" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
ACCEPT · objectui#13052 · PR #13070 ·
domain:ui#2·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-19T19:24ZPR #13070 at head
303ddcd(Fixes #13052,Clause-②: no). Reviewed against:the claim
6272872746(surface amended after CI onfab9fb8);triage
6269352785;the dev report
6274146631plus two patch rounds.Premise: holds.
readItem's catch classed every code butRESOURCE_NOT_FOUNDas failed.fetchScopeNamedropped a failed name without a re-render, so the scope never settled androuteReadsSettledkept the splash up forever.Diff read by the seat:
MetadataProvider;isMetaItemAbsentErrorunchanged),404 OBJECT_PACKAGE_DISABLEDis an ANSWER: the scope settles with the refusal named. The route draws the existing Object Not Found view with one new sentence (console.objectView.objectPackageDisabled, in ten packs).ObjectNotFoundStateis a module export only: the@object-ui/app-shellentry re-exportsObjectView/ConsoleObjectViewPropsby name, so there is no new package export. The failed / refused outcome rides as a package-local hand-off.Changeset:
@object-ui/app-shell(and i18n)patch.Pins:
MetadataProvider.scopeRefusal-13052(a real adapter through a fake fetch; flat and wrapped envelopes).AppContent.disabledPackageDeepLink-13052.Patch rounds:
vite-declared-lazy-views'EAGER_WALK_CONTROLreads theObjectViewimport verbatim. The line is kept, andObjectNotFoundStateis imported on its own line; the pin is untouched.objectView-config-keys-retired-4730(surviving 103→104, total 227→228) andde-quote-pairing-3876(90→91), both still exact.Local gates:
packages/i18n/src/__tests__/in full: 84 files, 1319 tests.Acceptance notes (not filed):
@object-ui/react'sreadObjectScope.requiresObjectgates keep a failed name "unknown" until Retry or refresh.Commits: model-free trailers. The PR went through the fleet-write relay.
CI at head: 36/36 check-runs success or skipped on
303ddcd(filter=latest),mergeable_stateclean.Verdict: ACCEPT. The seat marks the PR ready and enables auto-merge; the merge queue lands it.
Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Landed · objectui#13052 · PR #13070 ·
domain:ui#2·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-19T19:44Z1ce0711onmain. Verified by content against the accepted head303ddcd:-U0hunks in each equal the merge's hunks.404 OBJECT_PACKAGE_DISABLED) leaves the splash and draws a named "package disabled" state inside the shell.isMetaItemAbsentErroris unchanged.platform-core.package-lifecycle-enable-disableacceptance[5] once the console pin carries1ce0711.MetadataProvider.tsx/ObjectView.tsx).Fixes #13052;pm:dispatchedis removed with this note.Generated by Claude Code
fix(app-shell): a deep link to an object read by name always leaves t…