Repository navigation
feat(console): make the orphaned console pages targetable by navigation, retire the Developer Hub (objectui#10520) - #10576
Conversation
…and retire the Developer Hub Retiring the System Hub card wall left three console pages with no in-app link, and a fourth reachable only through one of them. Framework navigation reaches a console page only through a registry key, so: - AuditLogPage is kept and registered as `audit:log`: the sys_audit_log object view has no equivalent of its before/after JSON drawer. - AiPendingActionsPage is kept and registered as `ai:approvals`: nothing else lists the whole AI pending-action queue. - IntegrationsPage is kept and registered as `developer:integrations`: no Studio surface shows the base URL or the x-api-key cURL sample. - DeveloperHubPage is retired: all four of its cards now point at `developer:*` registry keys. Its route and page file go; the sub-page routes stay for bookmarks. The standalone routes of the kept pages stay; each key is additive. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BP8CMtACxTdLjqR6rhd33C
…oper Hub URL - `audit:log` and `ai:approvals` resolve through app-shell's real ComponentNavView to their pages, and the standalone system routes keep rendering them. - `developer:integrations` does the same, and all four of the retired hub's destinations are registered `developer:*` keys. - A `/apps/:app/developer` bookmark now falls through to the generic object route in place (never a blank screen); the sub-page routes still resolve. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BP8CMtACxTdLjqR6rhd33C
…r Hub retirement Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BP8CMtACxTdLjqR6rhd33C
Importing app-shell's ComponentNavView by source path put that file into the console's tsc program, under compiler options it is not written against, and type-check failed on it. The three keys are now driven through app-shell's exported DefaultAppContent, whose `component/:ns/:name/*` route is what serves ComponentNavView in the shipped console. The two per-module pin files merge into one. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BP8CMtACxTdLjqR6rhd33C
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: Rendered by an isolated review subagent spawned by the Read: the card body of objectui#10520, triage ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #10520
Clause-②: yes
What this does
Retiring the System Hub card wall (objectui#3743, PR objectui#10507) left three console pages with no in-app link. A fourth page,
IntegrationsPage, was linked only from one of them, the Developer Hub. Framework navigation reaches a console page only through atype: 'component'item whosecomponentRefis a component-registry key, never a console path. So each page is either kept and made targetable, or retired when navigation already covers everything it offers.AuditLogPageaudit:log(new)system/audit-logstaysAiPendingActionsPageai:approvals(new)system/ai-approvalsstaysIntegrationsPagedeveloper:integrations(new)developer/integrationsstaysDeveloperHubPagedeveloperremovedregisterSystemComponents.tsx(new, wired by one side-effect import inmain.tsx) registersaudit:logandai:approvalsin the shaperegisterApprovalsComponents.tsxuses: a lazy import, aSuspensefallback,ref,label,source: '@object-ui/console'.registerDeveloperComponents.tsxgainsdeveloper:integrationsbeside the other threedeveloper:*keys. Its docblock said the standalone routes "remain reachable from the Console-shell Developer Hub"; that sentence is replaced.AppContent.tsxloses thedeveloperroute and theDeveloperHubPagelazy import.DeveloperHubPage.tsxis deleted; it had no tests.AuditLogPageandAiPendingActionsPagedescribed the retired hub card and a Studio mount that does not exist in this repository; both now name the route and the key.packages/app-shell/**is untouched.registerAppComponentis used as it is.Measurements, per page (objectui
origin/mainat the branch base, objectstackorigin/main)A page counts as a DUPLICATE only when a navigation-reachable surface covers every capability it offers.
M1
AuditLogPagevs thesys_audit_logobject view: KEEP. The object view is Setup'snav_audit_logs, contributed byplugin-auditintogroup_diagnostics. Capability by capability:showFiltersdefaults on inObjectView), andrecentpages at 50. I did not measure each of the four filters one by one; the verdict does not rest on them.old_value,new_valueandmetadataareField.textareacolumns that the writer fills with compactJSON.stringifyoutput. On the record page they render through the textarea widget's readonly branch, which prints the raw string with whitespace kept. The page runsJSON.stringify(JSON.parse(...), null, 2)and shows Before and After side by side.So Setup would carry two audit entries, the object and the page. Which one Setup shows is for the objectstack card.
M2
AiPendingActionsPage: KEEP. The seat expected this. TheAiPendingActionsInboxdocblock says it also renders in Studio's assistant builder. In this repository its only mount is this page: agit grepofAiPendingActionsInboxoverpackagesandapps, tests excluded, finds the export and this page and nothing else. Studio's navigation has no pending-actions entry (its AI group is Agents, Tools and Skills). What else shows pending actions is scoped to one context:AiChatPageshows the approval card of its own conversation, andBuildDebugDrawershows the pending actions of one build. So this page is the only surface that lists the whole queue.M3
DeveloperHubPage: RETIRE. It was a wall of four cards: integrations, api-console, flow-runs and public-forms. After this PR all four are registereddeveloper:*keys, and the new pin asserts it. Studio'sgroup_developeralready namesdeveloper:api-console,developer:flow-runsanddeveloper:public-forms. No destination was left without a key, so theneeds_decisionfork did not arise.What a
/apps/APP/developerbookmark shows now::objectNameroute. It lands in place, with no redirect, inside the app's shell. The realObjectViewanswers a name that is not an object with itsobjectNotFoundempty state. Inenthat reads "Object Not Found" and 'The object "developer" does not exist in the current configuration.' I measured that branch once with a throwaway render of the realObjectView, which printed the threeconsole.objectView.objectNotFound*keys; the throwaway file is not in this PR. It is not a blank screen, so I added no redirect. There is also nothing to redirect to. No in-app link produces this URL, and the hub's four cards have no single canonical destination. objectui#3655 likewise declined to bind a bookmark to a surface nobody chose. (The retired static route used to shadow any object nameddeveloper; with it gone,/apps/APP/developermeans what/apps/APP/NAMEmeans everywhere else.)isSystemRoutekeys on asystemsegment, so/developernever reached the host fragment on that branch, and the hub route was unreachable there before this PR.AppContent.systemHubRoutes.test.tsx, against the realDefaultAppContentand the realsystemRoutes.M4
IntegrationsPage: KEEP. It shows the environment's base URL (copyable), the generated CRUD endpoints of each business object, an auth explainer, a cURL sample that sendsx-api-key, andAgentConnectSection.developer:api-console) discovers endpoints and sends session-authenticated requests. It shows only the relative request path: no base URL, no cURL, nox-api-key.mcp:connect-agentpage (Setup and Account) covers the MCP-connect half: the MCP URL, key minting and the SKILL.md download.Namespaces. Before this PR, no registry key or namespace named
AuditLogPageorAiPendingActionsPage. I grepped app-shellbuiltinComponents.tsx,plugin-chatbotand the console; the onlyregisterAppComponentkeys aremetadata:*,developer:*,automation:packaged,studio:builder,account:profile_cardandapprovals:inbox. Each new key is named for the capability that owns the data, the wayapprovals:inboxis, not for the console path:auditisplugin-audit, which ownssys_audit_logand registers theauditkernel service.aiis the kernel service (CoreServiceNameai) that serves/api/v1/ai/pending-actions.ai:approvalssits in the app-component registry, which is separate from the SDUI block registry that holds the protocol'sai:*block types (ai:chat_window,ai:suggestion). The two do not collide.Console docs.
content/docs/**,apps/console/README.mdandapps/console/docs/**name none of the four pages or routes.ROADMAP.mdnames/system/audit-logas a standalone page, which stays true.Tests and gates (all at
d8c18e1a3, the final commit)New pin file
orphanedPageComponentRefs-10520.test.tsx. For each of the three keys it asserts that:main.tsximports;component/NS/NAME, built from the ref throughcomponentRefToUrlSegments;DefaultAppContent, reaches the page through itscomponent/:ns/:name/*route, which servesComponentNavView;systemRoutesfragment, still renders the page.It also asserts that all four of the retired hub's destinations are registered keys. The pages are stubbed at the exact specifiers the register modules and
AppContentlazy-import.AppContent.systemHubRoutes.test.tsxgains athe retired Developer Hub URL (objectui#10520)describe. The with-app landing is the retirement pin. The zero-app measurement shows the branch is unchanged. A sub-page check showsdeveloper/integrationsstill resolves.Why not
ComponentNavViewdirectly: its first draft importedComponentNavViewby source path, and the consoletype-checkthen failed with TS6133 inside that app-shell file, whose unusedReactimport breaks the console'snoUnusedLocals. Routing through the exportedDefaultAppContentexercises the same lazyComponentNavViewand needs no app-shell edit.pnpm --filter @object-ui/console test: 117 files and 1288 tests passed, which equals the 117 tracked console test files.The two focused files together: 35 passed.
The other suites that name these pages:
AppContent.pseudoRouteSegments.test.tsx(app-shell) anddollar-dialect-alias-census.test.ts(scripts), 73 passed.pnpm --filter @object-ui/console type-check: exit 0, after building the dependency closure (turbo run build --filter=@object-ui/console^..., 34 tasks, exit 0).tsc --listFilesincludes both test files, so type-check covers them.pnpm --filter @object-ui/console build: exit 0. The post-build gates all passed:check-eager-closure-budget: "Console eager closure is 3046.6 KB gzipped across 329 of 2308 chunks (budget: 3104.5 KB, headroom: 57.8 KB)";check-eager-locale-catalogues,check:sdui-registration-pinsandcheck:docs-route-closure: exit 0.Changeset and source gates, all exit 0:
check-changeset-presence("9 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)");check-changeset-no-major,check-changeset-fixed,check-changeset-overwrite;check:changeset-claims("No pending changeset names a file this change touches");check:pending-changeset-literals;check:new-line-citations("0 new citation(s)");check:control-bytes,check-vi-mock-specifiers,check-vi-mock-inherit,check-test-path-roots,check-vi-mock-override-shape;check:unreferenced-sources,check:side-effects-array,check:phantom-deps,check:self-import,check:i18n-keys.check-governed-queue-guard --teston the 10 changed paths: NOT GOVERNED.Lint is a proven narrowing, not the repo-wide
pnpm lint:.ts/.tsxfiles, all insideeslint.config.js's scope;eslint --no-inline-config --format jsonreports 8 files, 0 errors and 18 warnings, the samereact-refreshandno-explicit-anykinds thatregisterApprovalsComponents.tsxalready carries;parserOptions.projectorprojectService), and no customobject-ui/*rule reads another file, so this diff cannot move a verdict on an untouched file.Repo-wide lint is left to CI.
Reverse proofs (from the committed state, through
ablation-replace.mjs: the anchor must hit, and the restore is proven against the HEAD blob)registerAppComponentcall foraudit:logturned into a bare objectaudit:logregistered, component URLaudit:log's standalone routeai:approvalsai:approvalsregistered, component URLdeveloper:integrationsdeveloperroute plusDeveloperHubPage.tsxput back from the base commitThe component-URL failures show app-shell's own "Component not registered" state in the DOM dump. After every leg, the tool restored
blob == HEADwith an emptygit diff HEAD, and the tree was clean.Acceptance notes
For the objectstack navigation card (the seat files it,
Blocked-by: objectstack-ai/objectui#10520):audit:log: Setupgroup_diagnostics, besidenav_audit_logs. The natural contributor isplugin-audit's ownnavigationContributions, the ADR-0029 K2 owner, which adds no gate: the entry lives and dies with the plugin, asnav_audit_logsdoes. If it lands inplatform-objects'setup-nav.contributions.tsinstead, gate itrequiresService: 'audit'. Setup then carries two audit entries.ai:approvals: Setupgroup_approvals, which already gates onmanage_platform_settings. Gate:requiresService: 'ai', the kernel service discovery reports asservices.ai. It is absent on a Community Edition boot, where objectstack's QA checklist row K2 records/api/v1/ai/pending-actionsanswering 501 behind the old card. That row names the now-deletedSystemHubPage. The gate closes its "ungated entry" half; its "error-blind inbox" half is inplugin-chatbotand is untouched here.developer:integrations: Studiogroup_developer, besidenav_api_console,nav_flow_runsandnav_public_forms. The neighbours carry no gate beyond Studio's app-levelstudio.access.Other notes:
.claude/skills/objectui-contributor/guides/console-development.mdstill listsDeveloperHubPagein itspages/developer/tree, beside theSystemHubPageline objectui#10507 already made stale. It is on the governed surface, so it is ⛔ not edited in this code PR. Carrier: the seat, as a separate Tier S doc PR.AiPendingActionsInbox's docblock still names asystem/ai/pending-actionsconsole path and a Studio mount. Both are stale comments inplugin-chatbot, outside this surface. Carrier: none.ToolPreview's "Open in API Console" link is/developer/api-console?path=..., with no/apps/APPprefix. The console's root routes declare no/developerpath, and their*catch-all redirects to/.ApiConsolePagealso reads no search params, so thepathpreset would be dropped even at the right URL. I measured this by reading both route tables and the page, not with a running probe.Generated by Claude Code