feat(spec,objectql,platform-objects): ADR-0029 D7 setup app navigation contributions - #1436

Merged
os-zhuang merged 3 commits into
mainfrom
claude/adr-0029-d7-setup-nav-contributions
Jun 1, 2026
Merged

feat(spec,objectql,platform-objects): ADR-0029 D7 setup app navigation contributions#1436
os-zhuang merged 3 commits into
mainfrom
claude/adr-0029-d7-setup-nav-contributions

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Implements ADR-0029 D7 — the navigation-contribution mechanism that lets the setup admin app become a thin shell while each capability plugin ships the menu for the objects it owns. This is the gap the K2.a webhooks pilot deferred, and it unblocks moving every remaining K2 domain's menu out of the platform-objects monolith with its objects.

The mechanism (UI-layer analog of object own/extend)

  • @objectstack/specNavigationContributionSchema ({ app, group?, priority, items }, using the real NavigationItemSchema) + an optional navigationContributions field on the manifest. Chosen over reviving the vestigial, never-consumed contributes.menus.
  • @objectstack/objectqlSchemaRegistry.registerAppNavContribution() stores contributions; getApp / getAllApps lazily merge them into the target app's navigation tree by group id + priority (lower first, mirroring object extenders). The stored app is cloned, so reads are idempotent and the base artifact is never mutated. The engine wires manifest.navigationContributions during app registration.
  • @objectstack/platform-objects — the Setup app (setup.app.ts) is now a shell of empty group anchors; its entries for platform-objects-owned objects moved to SETUP_NAV_CONTRIBUTIONS.
  • @objectstack/plugin-auth — registers SETUP_NAV_CONTRIBUTIONS alongside the Setup app it already owns.
  • @objectstack/plugin-webhooks — contributes its Webhooks / Webhook Deliveries entries into the Setup group_integrations slot (it owns sys_webhook / sys_webhook_delivery from K2.a) — proving end-to-end cross-plugin contribution.

Behavior

The rendered Setup nav is identical to the former static artifact — just assembled from its owners. An absent/disabled capability contributes nothing and its slot stays empty (in addition to the existing requiresObject / requiredPermissions frontend gating). Registration order is irrelevant (merge is lazy on read).

Verification

  • turbo build (spec + objectql + platform-objects + plugin-auth + plugin-webhooks + dep graph, incl. DTS type-check): green
  • Tests: spec 128, objectql 61 (incl. 7 new D7 tests), platform-objects 78 (incl. shell + contributions tests), plugin-webhooks 45, plugin-auth 85 — all green
  • Repo-wide check: no other package asserts on the static Setup nav items.

Follow-ups (per ADR-0029)

  • As each remaining K2 domain (jobs/email/audit/...) moves to its plugin, its Setup entries move out of SETUP_NAV_CONTRIBUTIONS into that plugin the same way.
  • Generalizing app-extension to arbitrary (non-setup) apps remains a tracked follow-up.

https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4


Generated by Claude Code

…n contributions
Add the UI-layer analog of object own/extend so a shared admin app can be a
thin shell while each capability plugin ships the menu for objects it owns.
- spec: NavigationContributionSchema { app, group?, priority, items } + optional
manifest.navigationContributions.
- objectql: SchemaRegistry.registerAppNavContribution() + lazy merge in
getApp/getAllApps (by group id + priority, clones the stored app so reads are
idempotent); engine wires manifest.navigationContributions on app registration.
- platform-objects: Setup app reduced to a shell of empty group anchors; its
platform-objects-owned entries moved to SETUP_NAV_CONTRIBUTIONS.
- plugin-auth: registers SETUP_NAV_CONTRIBUTIONS alongside the Setup app.
- plugin-webhooks: contributes Webhooks/Webhook Deliveries into the Setup
group_integrations slot (owns those objects per K2.a) — end-to-end cross-plugin
contribution.
Rendered Setup nav is unchanged; absent capabilities leave their slot empty.
Tests: spec 128, objectql 61 (incl 7 new D7), platform-objects 78 (incl shell +
contributions), plugin-webhooks 45, plugin-auth 85 — all green; turbo build
(10 packages incl DTS type-check) green.
https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJun 1, 2026 4:38am

Request Review

The previous Test Core run failed only in @objectstack/driver-mongodb, where
mongodb-memory-server could not download the MongoDB binary from
fastdl.mongodb.org (network/infra flake) — unrelated to this PR's changes
(spec/objectql/platform-objects/plugin-auth/plugin-webhooks). Local full
`turbo run test` is green except for that same environmental download error.
https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
…tup-nav-contributions
# Conflicts:
#	packages/platform-objects/src/apps/setup.app.ts
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(spec,objectql,platform-objects): ADR-0029 D7 setup app navigation contributions - #1436

Merged
os-zhuang merged 3 commits into
mainfrom
claude/adr-0029-d7-setup-nav-contributions
Jun 1, 2026
Merged

feat(spec,objectql,platform-objects): ADR-0029 D7 setup app navigation contributions#1436
os-zhuang merged 3 commits into
mainfrom
claude/adr-0029-d7-setup-nav-contributions

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Implements ADR-0029 D7 — the navigation-contribution mechanism that lets the setup admin app become a thin shell while each capability plugin ships the menu for the objects it owns. This is the gap the K2.a webhooks pilot deferred, and it unblocks moving every remaining K2 domain's menu out of the platform-objects monolith with its objects.

The mechanism (UI-layer analog of object own/extend)

  • @objectstack/specNavigationContributionSchema ({ app, group?, priority, items }, using the real NavigationItemSchema) + an optional navigationContributions field on the manifest. Chosen over reviving the vestigial, never-consumed contributes.menus.
  • @objectstack/objectqlSchemaRegistry.registerAppNavContribution() stores contributions; getApp / getAllApps lazily merge them into the target app's navigation tree by group id + priority (lower first, mirroring object extenders). The stored app is cloned, so reads are idempotent and the base artifact is never mutated. The engine wires manifest.navigationContributions during app registration.
  • @objectstack/platform-objects — the Setup app (setup.app.ts) is now a shell of empty group anchors; its entries for platform-objects-owned objects moved to SETUP_NAV_CONTRIBUTIONS.
  • @objectstack/plugin-auth — registers SETUP_NAV_CONTRIBUTIONS alongside the Setup app it already owns.
  • @objectstack/plugin-webhooks — contributes its Webhooks / Webhook Deliveries entries into the Setup group_integrations slot (it owns sys_webhook / sys_webhook_delivery from K2.a) — proving end-to-end cross-plugin contribution.

Behavior

The rendered Setup nav is identical to the former static artifact — just assembled from its owners. An absent/disabled capability contributes nothing and its slot stays empty (in addition to the existing requiresObject / requiredPermissions frontend gating). Registration order is irrelevant (merge is lazy on read).

Verification

  • turbo build (spec + objectql + platform-objects + plugin-auth + plugin-webhooks + dep graph, incl. DTS type-check): green
  • Tests: spec 128, objectql 61 (incl. 7 new D7 tests), platform-objects 78 (incl. shell + contributions tests), plugin-webhooks 45, plugin-auth 85 — all green
  • Repo-wide check: no other package asserts on the static Setup nav items.

Follow-ups (per ADR-0029)

  • As each remaining K2 domain (jobs/email/audit/...) moves to its plugin, its Setup entries move out of SETUP_NAV_CONTRIBUTIONS into that plugin the same way.
  • Generalizing app-extension to arbitrary (non-setup) apps remains a tracked follow-up.

https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4


Generated by Claude Code

…n contributions
Add the UI-layer analog of object own/extend so a shared admin app can be a
thin shell while each capability plugin ships the menu for objects it owns.
- spec: NavigationContributionSchema { app, group?, priority, items } + optional
manifest.navigationContributions.
- objectql: SchemaRegistry.registerAppNavContribution() + lazy merge in
getApp/getAllApps (by group id + priority, clones the stored app so reads are
idempotent); engine wires manifest.navigationContributions on app registration.
- platform-objects: Setup app reduced to a shell of empty group anchors; its
platform-objects-owned entries moved to SETUP_NAV_CONTRIBUTIONS.
- plugin-auth: registers SETUP_NAV_CONTRIBUTIONS alongside the Setup app.
- plugin-webhooks: contributes Webhooks/Webhook Deliveries into the Setup
group_integrations slot (owns those objects per K2.a) — end-to-end cross-plugin
contribution.
Rendered Setup nav is unchanged; absent capabilities leave their slot empty.
Tests: spec 128, objectql 61 (incl 7 new D7), platform-objects 78 (incl shell +
contributions), plugin-webhooks 45, plugin-auth 85 — all green; turbo build
(10 packages incl DTS type-check) green.
https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJun 1, 2026 4:38am

Request Review

The previous Test Core run failed only in @objectstack/driver-mongodb, where
mongodb-memory-server could not download the MongoDB binary from
fastdl.mongodb.org (network/infra flake) — unrelated to this PR's changes
(spec/objectql/platform-objects/plugin-auth/plugin-webhooks). Local full
`turbo run test` is green except for that same environmental download error.
https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
…tup-nav-contributions
# Conflicts:
#	packages/platform-objects/src/apps/setup.app.ts
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(spec,objectql,platform-objects): ADR-0029 D7 setup app navigation contributions - #1436

Merged
os-zhuang merged 3 commits into
mainfrom
claude/adr-0029-d7-setup-nav-contributions
Jun 1, 2026
Merged

feat(spec,objectql,platform-objects): ADR-0029 D7 setup app navigation contributions#1436
os-zhuang merged 3 commits into
mainfrom
claude/adr-0029-d7-setup-nav-contributions

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Implements ADR-0029 D7 — the navigation-contribution mechanism that lets the setup admin app become a thin shell while each capability plugin ships the menu for the objects it owns. This is the gap the K2.a webhooks pilot deferred, and it unblocks moving every remaining K2 domain's menu out of the platform-objects monolith with its objects.

The mechanism (UI-layer analog of object own/extend)

  • @objectstack/specNavigationContributionSchema ({ app, group?, priority, items }, using the real NavigationItemSchema) + an optional navigationContributions field on the manifest. Chosen over reviving the vestigial, never-consumed contributes.menus.
  • @objectstack/objectqlSchemaRegistry.registerAppNavContribution() stores contributions; getApp / getAllApps lazily merge them into the target app's navigation tree by group id + priority (lower first, mirroring object extenders). The stored app is cloned, so reads are idempotent and the base artifact is never mutated. The engine wires manifest.navigationContributions during app registration.
  • @objectstack/platform-objects — the Setup app (setup.app.ts) is now a shell of empty group anchors; its entries for platform-objects-owned objects moved to SETUP_NAV_CONTRIBUTIONS.
  • @objectstack/plugin-auth — registers SETUP_NAV_CONTRIBUTIONS alongside the Setup app it already owns.
  • @objectstack/plugin-webhooks — contributes its Webhooks / Webhook Deliveries entries into the Setup group_integrations slot (it owns sys_webhook / sys_webhook_delivery from K2.a) — proving end-to-end cross-plugin contribution.

Behavior

The rendered Setup nav is identical to the former static artifact — just assembled from its owners. An absent/disabled capability contributes nothing and its slot stays empty (in addition to the existing requiresObject / requiredPermissions frontend gating). Registration order is irrelevant (merge is lazy on read).

Verification

  • turbo build (spec + objectql + platform-objects + plugin-auth + plugin-webhooks + dep graph, incl. DTS type-check): green
  • Tests: spec 128, objectql 61 (incl. 7 new D7 tests), platform-objects 78 (incl. shell + contributions tests), plugin-webhooks 45, plugin-auth 85 — all green
  • Repo-wide check: no other package asserts on the static Setup nav items.

Follow-ups (per ADR-0029)

  • As each remaining K2 domain (jobs/email/audit/...) moves to its plugin, its Setup entries move out of SETUP_NAV_CONTRIBUTIONS into that plugin the same way.
  • Generalizing app-extension to arbitrary (non-setup) apps remains a tracked follow-up.

https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4


Generated by Claude Code

…n contributions
Add the UI-layer analog of object own/extend so a shared admin app can be a
thin shell while each capability plugin ships the menu for objects it owns.
- spec: NavigationContributionSchema { app, group?, priority, items } + optional
manifest.navigationContributions.
- objectql: SchemaRegistry.registerAppNavContribution() + lazy merge in
getApp/getAllApps (by group id + priority, clones the stored app so reads are
idempotent); engine wires manifest.navigationContributions on app registration.
- platform-objects: Setup app reduced to a shell of empty group anchors; its
platform-objects-owned entries moved to SETUP_NAV_CONTRIBUTIONS.
- plugin-auth: registers SETUP_NAV_CONTRIBUTIONS alongside the Setup app.
- plugin-webhooks: contributes Webhooks/Webhook Deliveries into the Setup
group_integrations slot (owns those objects per K2.a) — end-to-end cross-plugin
contribution.
Rendered Setup nav is unchanged; absent capabilities leave their slot empty.
Tests: spec 128, objectql 61 (incl 7 new D7), platform-objects 78 (incl shell +
contributions), plugin-webhooks 45, plugin-auth 85 — all green; turbo build
(10 packages incl DTS type-check) green.
https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJun 1, 2026 4:38am

Request Review

The previous Test Core run failed only in @objectstack/driver-mongodb, where
mongodb-memory-server could not download the MongoDB binary from
fastdl.mongodb.org (network/infra flake) — unrelated to this PR's changes
(spec/objectql/platform-objects/plugin-auth/plugin-webhooks). Local full
`turbo run test` is green except for that same environmental download error.
https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
…tup-nav-contributions
# Conflicts:
#	packages/platform-objects/src/apps/setup.app.ts
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(spec,objectql,platform-objects): ADR-0029 D7 setup app navigation contributions - #1436

Merged
os-zhuang merged 3 commits into
mainfrom
claude/adr-0029-d7-setup-nav-contributions
Jun 1, 2026
Merged

feat(spec,objectql,platform-objects): ADR-0029 D7 setup app navigation contributions#1436
os-zhuang merged 3 commits into
mainfrom
claude/adr-0029-d7-setup-nav-contributions

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Implements ADR-0029 D7 — the navigation-contribution mechanism that lets the setup admin app become a thin shell while each capability plugin ships the menu for the objects it owns. This is the gap the K2.a webhooks pilot deferred, and it unblocks moving every remaining K2 domain's menu out of the platform-objects monolith with its objects.

The mechanism (UI-layer analog of object own/extend)

  • @objectstack/specNavigationContributionSchema ({ app, group?, priority, items }, using the real NavigationItemSchema) + an optional navigationContributions field on the manifest. Chosen over reviving the vestigial, never-consumed contributes.menus.
  • @objectstack/objectqlSchemaRegistry.registerAppNavContribution() stores contributions; getApp / getAllApps lazily merge them into the target app's navigation tree by group id + priority (lower first, mirroring object extenders). The stored app is cloned, so reads are idempotent and the base artifact is never mutated. The engine wires manifest.navigationContributions during app registration.
  • @objectstack/platform-objects — the Setup app (setup.app.ts) is now a shell of empty group anchors; its entries for platform-objects-owned objects moved to SETUP_NAV_CONTRIBUTIONS.
  • @objectstack/plugin-auth — registers SETUP_NAV_CONTRIBUTIONS alongside the Setup app it already owns.
  • @objectstack/plugin-webhooks — contributes its Webhooks / Webhook Deliveries entries into the Setup group_integrations slot (it owns sys_webhook / sys_webhook_delivery from K2.a) — proving end-to-end cross-plugin contribution.

Behavior

The rendered Setup nav is identical to the former static artifact — just assembled from its owners. An absent/disabled capability contributes nothing and its slot stays empty (in addition to the existing requiresObject / requiredPermissions frontend gating). Registration order is irrelevant (merge is lazy on read).

Verification

  • turbo build (spec + objectql + platform-objects + plugin-auth + plugin-webhooks + dep graph, incl. DTS type-check): green
  • Tests: spec 128, objectql 61 (incl. 7 new D7 tests), platform-objects 78 (incl. shell + contributions tests), plugin-webhooks 45, plugin-auth 85 — all green
  • Repo-wide check: no other package asserts on the static Setup nav items.

Follow-ups (per ADR-0029)

  • As each remaining K2 domain (jobs/email/audit/...) moves to its plugin, its Setup entries move out of SETUP_NAV_CONTRIBUTIONS into that plugin the same way.
  • Generalizing app-extension to arbitrary (non-setup) apps remains a tracked follow-up.

https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4


Generated by Claude Code

…n contributions
Add the UI-layer analog of object own/extend so a shared admin app can be a
thin shell while each capability plugin ships the menu for objects it owns.
- spec: NavigationContributionSchema { app, group?, priority, items } + optional
manifest.navigationContributions.
- objectql: SchemaRegistry.registerAppNavContribution() + lazy merge in
getApp/getAllApps (by group id + priority, clones the stored app so reads are
idempotent); engine wires manifest.navigationContributions on app registration.
- platform-objects: Setup app reduced to a shell of empty group anchors; its
platform-objects-owned entries moved to SETUP_NAV_CONTRIBUTIONS.
- plugin-auth: registers SETUP_NAV_CONTRIBUTIONS alongside the Setup app.
- plugin-webhooks: contributes Webhooks/Webhook Deliveries into the Setup
group_integrations slot (owns those objects per K2.a) — end-to-end cross-plugin
contribution.
Rendered Setup nav is unchanged; absent capabilities leave their slot empty.
Tests: spec 128, objectql 61 (incl 7 new D7), platform-objects 78 (incl shell +
contributions), plugin-webhooks 45, plugin-auth 85 — all green; turbo build
(10 packages incl DTS type-check) green.
https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJun 1, 2026 4:38am

Request Review

The previous Test Core run failed only in @objectstack/driver-mongodb, where
mongodb-memory-server could not download the MongoDB binary from
fastdl.mongodb.org (network/infra flake) — unrelated to this PR's changes
(spec/objectql/platform-objects/plugin-auth/plugin-webhooks). Local full
`turbo run test` is green except for that same environmental download error.
https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
…tup-nav-contributions
# Conflicts:
#	packages/platform-objects/src/apps/setup.app.ts
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(spec,objectql,platform-objects): ADR-0029 D7 setup app navigation contributions - #1436

Merged
os-zhuang merged 3 commits into
mainfrom
claude/adr-0029-d7-setup-nav-contributions
Jun 1, 2026
Merged

feat(spec,objectql,platform-objects): ADR-0029 D7 setup app navigation contributions#1436
os-zhuang merged 3 commits into
mainfrom
claude/adr-0029-d7-setup-nav-contributions

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Implements ADR-0029 D7 — the navigation-contribution mechanism that lets the setup admin app become a thin shell while each capability plugin ships the menu for the objects it owns. This is the gap the K2.a webhooks pilot deferred, and it unblocks moving every remaining K2 domain's menu out of the platform-objects monolith with its objects.

The mechanism (UI-layer analog of object own/extend)

  • @objectstack/specNavigationContributionSchema ({ app, group?, priority, items }, using the real NavigationItemSchema) + an optional navigationContributions field on the manifest. Chosen over reviving the vestigial, never-consumed contributes.menus.
  • @objectstack/objectqlSchemaRegistry.registerAppNavContribution() stores contributions; getApp / getAllApps lazily merge them into the target app's navigation tree by group id + priority (lower first, mirroring object extenders). The stored app is cloned, so reads are idempotent and the base artifact is never mutated. The engine wires manifest.navigationContributions during app registration.
  • @objectstack/platform-objects — the Setup app (setup.app.ts) is now a shell of empty group anchors; its entries for platform-objects-owned objects moved to SETUP_NAV_CONTRIBUTIONS.
  • @objectstack/plugin-auth — registers SETUP_NAV_CONTRIBUTIONS alongside the Setup app it already owns.
  • @objectstack/plugin-webhooks — contributes its Webhooks / Webhook Deliveries entries into the Setup group_integrations slot (it owns sys_webhook / sys_webhook_delivery from K2.a) — proving end-to-end cross-plugin contribution.

Behavior

The rendered Setup nav is identical to the former static artifact — just assembled from its owners. An absent/disabled capability contributes nothing and its slot stays empty (in addition to the existing requiresObject / requiredPermissions frontend gating). Registration order is irrelevant (merge is lazy on read).

Verification

  • turbo build (spec + objectql + platform-objects + plugin-auth + plugin-webhooks + dep graph, incl. DTS type-check): green
  • Tests: spec 128, objectql 61 (incl. 7 new D7 tests), platform-objects 78 (incl. shell + contributions tests), plugin-webhooks 45, plugin-auth 85 — all green
  • Repo-wide check: no other package asserts on the static Setup nav items.

Follow-ups (per ADR-0029)

  • As each remaining K2 domain (jobs/email/audit/...) moves to its plugin, its Setup entries move out of SETUP_NAV_CONTRIBUTIONS into that plugin the same way.
  • Generalizing app-extension to arbitrary (non-setup) apps remains a tracked follow-up.

https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4


Generated by Claude Code

…n contributions
Add the UI-layer analog of object own/extend so a shared admin app can be a
thin shell while each capability plugin ships the menu for objects it owns.
- spec: NavigationContributionSchema { app, group?, priority, items } + optional
manifest.navigationContributions.
- objectql: SchemaRegistry.registerAppNavContribution() + lazy merge in
getApp/getAllApps (by group id + priority, clones the stored app so reads are
idempotent); engine wires manifest.navigationContributions on app registration.
- platform-objects: Setup app reduced to a shell of empty group anchors; its
platform-objects-owned entries moved to SETUP_NAV_CONTRIBUTIONS.
- plugin-auth: registers SETUP_NAV_CONTRIBUTIONS alongside the Setup app.
- plugin-webhooks: contributes Webhooks/Webhook Deliveries into the Setup
group_integrations slot (owns those objects per K2.a) — end-to-end cross-plugin
contribution.
Rendered Setup nav is unchanged; absent capabilities leave their slot empty.
Tests: spec 128, objectql 61 (incl 7 new D7), platform-objects 78 (incl shell +
contributions), plugin-webhooks 45, plugin-auth 85 — all green; turbo build
(10 packages incl DTS type-check) green.
https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJun 1, 2026 4:38am

Request Review

The previous Test Core run failed only in @objectstack/driver-mongodb, where
mongodb-memory-server could not download the MongoDB binary from
fastdl.mongodb.org (network/infra flake) — unrelated to this PR's changes
(spec/objectql/platform-objects/plugin-auth/plugin-webhooks). Local full
`turbo run test` is green except for that same environmental download error.
https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
…tup-nav-contributions
# Conflicts:
#	packages/platform-objects/src/apps/setup.app.ts
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(spec,objectql,platform-objects): ADR-0029 D7 setup app navigation contributions - #1436

Merged
os-zhuang merged 3 commits into
mainfrom
claude/adr-0029-d7-setup-nav-contributions
Jun 1, 2026
Merged

feat(spec,objectql,platform-objects): ADR-0029 D7 setup app navigation contributions#1436
os-zhuang merged 3 commits into
mainfrom
claude/adr-0029-d7-setup-nav-contributions

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Implements ADR-0029 D7 — the navigation-contribution mechanism that lets the setup admin app become a thin shell while each capability plugin ships the menu for the objects it owns. This is the gap the K2.a webhooks pilot deferred, and it unblocks moving every remaining K2 domain's menu out of the platform-objects monolith with its objects.

The mechanism (UI-layer analog of object own/extend)

  • @objectstack/specNavigationContributionSchema ({ app, group?, priority, items }, using the real NavigationItemSchema) + an optional navigationContributions field on the manifest. Chosen over reviving the vestigial, never-consumed contributes.menus.
  • @objectstack/objectqlSchemaRegistry.registerAppNavContribution() stores contributions; getApp / getAllApps lazily merge them into the target app's navigation tree by group id + priority (lower first, mirroring object extenders). The stored app is cloned, so reads are idempotent and the base artifact is never mutated. The engine wires manifest.navigationContributions during app registration.
  • @objectstack/platform-objects — the Setup app (setup.app.ts) is now a shell of empty group anchors; its entries for platform-objects-owned objects moved to SETUP_NAV_CONTRIBUTIONS.
  • @objectstack/plugin-auth — registers SETUP_NAV_CONTRIBUTIONS alongside the Setup app it already owns.
  • @objectstack/plugin-webhooks — contributes its Webhooks / Webhook Deliveries entries into the Setup group_integrations slot (it owns sys_webhook / sys_webhook_delivery from K2.a) — proving end-to-end cross-plugin contribution.

Behavior

The rendered Setup nav is identical to the former static artifact — just assembled from its owners. An absent/disabled capability contributes nothing and its slot stays empty (in addition to the existing requiresObject / requiredPermissions frontend gating). Registration order is irrelevant (merge is lazy on read).

Verification

  • turbo build (spec + objectql + platform-objects + plugin-auth + plugin-webhooks + dep graph, incl. DTS type-check): green
  • Tests: spec 128, objectql 61 (incl. 7 new D7 tests), platform-objects 78 (incl. shell + contributions tests), plugin-webhooks 45, plugin-auth 85 — all green
  • Repo-wide check: no other package asserts on the static Setup nav items.

Follow-ups (per ADR-0029)

  • As each remaining K2 domain (jobs/email/audit/...) moves to its plugin, its Setup entries move out of SETUP_NAV_CONTRIBUTIONS into that plugin the same way.
  • Generalizing app-extension to arbitrary (non-setup) apps remains a tracked follow-up.

https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4


Generated by Claude Code

…n contributions
Add the UI-layer analog of object own/extend so a shared admin app can be a
thin shell while each capability plugin ships the menu for objects it owns.
- spec: NavigationContributionSchema { app, group?, priority, items } + optional
manifest.navigationContributions.
- objectql: SchemaRegistry.registerAppNavContribution() + lazy merge in
getApp/getAllApps (by group id + priority, clones the stored app so reads are
idempotent); engine wires manifest.navigationContributions on app registration.
- platform-objects: Setup app reduced to a shell of empty group anchors; its
platform-objects-owned entries moved to SETUP_NAV_CONTRIBUTIONS.
- plugin-auth: registers SETUP_NAV_CONTRIBUTIONS alongside the Setup app.
- plugin-webhooks: contributes Webhooks/Webhook Deliveries into the Setup
group_integrations slot (owns those objects per K2.a) — end-to-end cross-plugin
contribution.
Rendered Setup nav is unchanged; absent capabilities leave their slot empty.
Tests: spec 128, objectql 61 (incl 7 new D7), platform-objects 78 (incl shell +
contributions), plugin-webhooks 45, plugin-auth 85 — all green; turbo build
(10 packages incl DTS type-check) green.
https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJun 1, 2026 4:38am

Request Review

The previous Test Core run failed only in @objectstack/driver-mongodb, where
mongodb-memory-server could not download the MongoDB binary from
fastdl.mongodb.org (network/infra flake) — unrelated to this PR's changes
(spec/objectql/platform-objects/plugin-auth/plugin-webhooks). Local full
`turbo run test` is green except for that same environmental download error.
https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
…tup-nav-contributions
# Conflicts:
#	packages/platform-objects/src/apps/setup.app.ts
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(spec,objectql,platform-objects): ADR-0029 D7 setup app navigation contributions - #1436

Merged
os-zhuang merged 3 commits into
mainfrom
claude/adr-0029-d7-setup-nav-contributions
Jun 1, 2026
Merged

feat(spec,objectql,platform-objects): ADR-0029 D7 setup app navigation contributions#1436
os-zhuang merged 3 commits into
mainfrom
claude/adr-0029-d7-setup-nav-contributions

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Implements ADR-0029 D7 — the navigation-contribution mechanism that lets the setup admin app become a thin shell while each capability plugin ships the menu for the objects it owns. This is the gap the K2.a webhooks pilot deferred, and it unblocks moving every remaining K2 domain's menu out of the platform-objects monolith with its objects.

The mechanism (UI-layer analog of object own/extend)

  • @objectstack/specNavigationContributionSchema ({ app, group?, priority, items }, using the real NavigationItemSchema) + an optional navigationContributions field on the manifest. Chosen over reviving the vestigial, never-consumed contributes.menus.
  • @objectstack/objectqlSchemaRegistry.registerAppNavContribution() stores contributions; getApp / getAllApps lazily merge them into the target app's navigation tree by group id + priority (lower first, mirroring object extenders). The stored app is cloned, so reads are idempotent and the base artifact is never mutated. The engine wires manifest.navigationContributions during app registration.
  • @objectstack/platform-objects — the Setup app (setup.app.ts) is now a shell of empty group anchors; its entries for platform-objects-owned objects moved to SETUP_NAV_CONTRIBUTIONS.
  • @objectstack/plugin-auth — registers SETUP_NAV_CONTRIBUTIONS alongside the Setup app it already owns.
  • @objectstack/plugin-webhooks — contributes its Webhooks / Webhook Deliveries entries into the Setup group_integrations slot (it owns sys_webhook / sys_webhook_delivery from K2.a) — proving end-to-end cross-plugin contribution.

Behavior

The rendered Setup nav is identical to the former static artifact — just assembled from its owners. An absent/disabled capability contributes nothing and its slot stays empty (in addition to the existing requiresObject / requiredPermissions frontend gating). Registration order is irrelevant (merge is lazy on read).

Verification

  • turbo build (spec + objectql + platform-objects + plugin-auth + plugin-webhooks + dep graph, incl. DTS type-check): green
  • Tests: spec 128, objectql 61 (incl. 7 new D7 tests), platform-objects 78 (incl. shell + contributions tests), plugin-webhooks 45, plugin-auth 85 — all green
  • Repo-wide check: no other package asserts on the static Setup nav items.

Follow-ups (per ADR-0029)

  • As each remaining K2 domain (jobs/email/audit/...) moves to its plugin, its Setup entries move out of SETUP_NAV_CONTRIBUTIONS into that plugin the same way.
  • Generalizing app-extension to arbitrary (non-setup) apps remains a tracked follow-up.

https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4


Generated by Claude Code

…n contributions
Add the UI-layer analog of object own/extend so a shared admin app can be a
thin shell while each capability plugin ships the menu for objects it owns.
- spec: NavigationContributionSchema { app, group?, priority, items } + optional
manifest.navigationContributions.
- objectql: SchemaRegistry.registerAppNavContribution() + lazy merge in
getApp/getAllApps (by group id + priority, clones the stored app so reads are
idempotent); engine wires manifest.navigationContributions on app registration.
- platform-objects: Setup app reduced to a shell of empty group anchors; its
platform-objects-owned entries moved to SETUP_NAV_CONTRIBUTIONS.
- plugin-auth: registers SETUP_NAV_CONTRIBUTIONS alongside the Setup app.
- plugin-webhooks: contributes Webhooks/Webhook Deliveries into the Setup
group_integrations slot (owns those objects per K2.a) — end-to-end cross-plugin
contribution.
Rendered Setup nav is unchanged; absent capabilities leave their slot empty.
Tests: spec 128, objectql 61 (incl 7 new D7), platform-objects 78 (incl shell +
contributions), plugin-webhooks 45, plugin-auth 85 — all green; turbo build
(10 packages incl DTS type-check) green.
https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJun 1, 2026 4:38am

Request Review

The previous Test Core run failed only in @objectstack/driver-mongodb, where
mongodb-memory-server could not download the MongoDB binary from
fastdl.mongodb.org (network/infra flake) — unrelated to this PR's changes
(spec/objectql/platform-objects/plugin-auth/plugin-webhooks). Local full
`turbo run test` is green except for that same environmental download error.
https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
…tup-nav-contributions
# Conflicts:
#	packages/platform-objects/src/apps/setup.app.ts
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(spec,objectql,platform-objects): ADR-0029 D7 setup app navigation contributions - #1436

Merged
os-zhuang merged 3 commits into
mainfrom
claude/adr-0029-d7-setup-nav-contributions
Jun 1, 2026
Merged

feat(spec,objectql,platform-objects): ADR-0029 D7 setup app navigation contributions#1436
os-zhuang merged 3 commits into
mainfrom
claude/adr-0029-d7-setup-nav-contributions

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Implements ADR-0029 D7 — the navigation-contribution mechanism that lets the setup admin app become a thin shell while each capability plugin ships the menu for the objects it owns. This is the gap the K2.a webhooks pilot deferred, and it unblocks moving every remaining K2 domain's menu out of the platform-objects monolith with its objects.

The mechanism (UI-layer analog of object own/extend)

  • @objectstack/specNavigationContributionSchema ({ app, group?, priority, items }, using the real NavigationItemSchema) + an optional navigationContributions field on the manifest. Chosen over reviving the vestigial, never-consumed contributes.menus.
  • @objectstack/objectqlSchemaRegistry.registerAppNavContribution() stores contributions; getApp / getAllApps lazily merge them into the target app's navigation tree by group id + priority (lower first, mirroring object extenders). The stored app is cloned, so reads are idempotent and the base artifact is never mutated. The engine wires manifest.navigationContributions during app registration.
  • @objectstack/platform-objects — the Setup app (setup.app.ts) is now a shell of empty group anchors; its entries for platform-objects-owned objects moved to SETUP_NAV_CONTRIBUTIONS.
  • @objectstack/plugin-auth — registers SETUP_NAV_CONTRIBUTIONS alongside the Setup app it already owns.
  • @objectstack/plugin-webhooks — contributes its Webhooks / Webhook Deliveries entries into the Setup group_integrations slot (it owns sys_webhook / sys_webhook_delivery from K2.a) — proving end-to-end cross-plugin contribution.

Behavior

The rendered Setup nav is identical to the former static artifact — just assembled from its owners. An absent/disabled capability contributes nothing and its slot stays empty (in addition to the existing requiresObject / requiredPermissions frontend gating). Registration order is irrelevant (merge is lazy on read).

Verification

  • turbo build (spec + objectql + platform-objects + plugin-auth + plugin-webhooks + dep graph, incl. DTS type-check): green
  • Tests: spec 128, objectql 61 (incl. 7 new D7 tests), platform-objects 78 (incl. shell + contributions tests), plugin-webhooks 45, plugin-auth 85 — all green
  • Repo-wide check: no other package asserts on the static Setup nav items.

Follow-ups (per ADR-0029)

  • As each remaining K2 domain (jobs/email/audit/...) moves to its plugin, its Setup entries move out of SETUP_NAV_CONTRIBUTIONS into that plugin the same way.
  • Generalizing app-extension to arbitrary (non-setup) apps remains a tracked follow-up.

https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4


Generated by Claude Code

…n contributions
Add the UI-layer analog of object own/extend so a shared admin app can be a
thin shell while each capability plugin ships the menu for objects it owns.
- spec: NavigationContributionSchema { app, group?, priority, items } + optional
manifest.navigationContributions.
- objectql: SchemaRegistry.registerAppNavContribution() + lazy merge in
getApp/getAllApps (by group id + priority, clones the stored app so reads are
idempotent); engine wires manifest.navigationContributions on app registration.
- platform-objects: Setup app reduced to a shell of empty group anchors; its
platform-objects-owned entries moved to SETUP_NAV_CONTRIBUTIONS.
- plugin-auth: registers SETUP_NAV_CONTRIBUTIONS alongside the Setup app.
- plugin-webhooks: contributes Webhooks/Webhook Deliveries into the Setup
group_integrations slot (owns those objects per K2.a) — end-to-end cross-plugin
contribution.
Rendered Setup nav is unchanged; absent capabilities leave their slot empty.
Tests: spec 128, objectql 61 (incl 7 new D7), platform-objects 78 (incl shell +
contributions), plugin-webhooks 45, plugin-auth 85 — all green; turbo build
(10 packages incl DTS type-check) green.
https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJun 1, 2026 4:38am

Request Review

The previous Test Core run failed only in @objectstack/driver-mongodb, where
mongodb-memory-server could not download the MongoDB binary from
fastdl.mongodb.org (network/infra flake) — unrelated to this PR's changes
(spec/objectql/platform-objects/plugin-auth/plugin-webhooks). Local full
`turbo run test` is green except for that same environmental download error.
https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
…tup-nav-contributions
# Conflicts:
#	packages/platform-objects/src/apps/setup.app.ts
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude