From e03d2fc794019bb73ea8a534ef61439da9d8ae94 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Jul 2026 02:43:39 +0000 Subject: [PATCH 1/6] refactor(showcase): reorganize src/ by the six protocol domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restructure examples/app-showcase/src into the same domain taxonomy the metadata registry uses (DEFAULT_METADATA_TYPE_REGISTRY): data/, ui/, automation/, system/, security/. Pure mechanical move (git mv) plus import-specifier updates in objectstack.config.ts, the test files, and the descriptive paths in the coverage manifest. Two files are pinned in place by contract and stay at src/: - src/coverage.ts — package export "./coverage" - src/docs/ — the CLI doc collector requires a flat src/docs (ADR-0046) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01G6pPpRszk9cD3SxcKNFMWs --- examples/app-showcase/objectstack.config.ts | 44 +++++++++---------- .../src/{ => automation}/flows/index.ts | 0 .../src/{ => automation}/jobs/index.ts | 0 .../src/{ => automation}/webhooks/index.ts | 0 examples/app-showcase/src/coverage.ts | 16 +++---- .../src/{ => data}/hooks/index.ts | 0 .../src/{ => data}/objects/account.object.ts | 0 .../{ => data}/objects/announcement.object.ts | 0 .../objects/business-unit.object.ts | 0 .../src/{ => data}/objects/category.object.ts | 0 .../src/{ => data}/objects/contact.object.ts | 0 .../objects/external/customer.object.ts | 0 .../src/{ => data}/objects/external/index.ts | 0 .../objects/external/order.object.ts | 0 .../{ => data}/objects/field-zoo.object.ts | 0 .../src/{ => data}/objects/index.ts | 0 .../src/{ => data}/objects/inquiry.object.ts | 0 .../src/{ => data}/objects/invoice.object.ts | 0 .../{ => data}/objects/preference.object.ts | 0 .../{ => data}/objects/private-note.object.ts | 0 .../src/{ => data}/objects/project.object.ts | 0 .../{ => data}/objects/semantic-zoo.object.ts | 0 .../src/{ => data}/objects/task.object.ts | 0 .../src/{ => data}/objects/team.object.ts | 0 .../app-showcase/src/data/{ => seed}/index.ts | 0 .../src/{ => system}/books/index.ts | 0 .../datasources/external-fixture.ts | 0 .../src/{ => system}/datasources/index.ts | 0 .../showcase-external.datasource.ts | 0 .../src/{ => system}/emails/index.ts | 0 .../{ => system}/server/recalc-endpoint.ts | 0 .../src/{ => system}/translations/index.ts | 0 .../src/{ => ui}/actions/index.ts | 0 .../app-showcase/src/{ => ui}/apps/index.ts | 0 .../dashboards/chart-gallery.dashboard.ts | 0 .../src/{ => ui}/dashboards/index.ts | 0 .../dashboards/ops-dashboard.dashboard.ts | 0 .../datasets/chart-gallery.dataset.ts | 0 .../src/{ => ui}/datasets/index.ts | 0 .../{ => ui}/pages/account-cockpit.page.ts | 0 .../{ => ui}/pages/active-projects.page.ts | 0 .../{ => ui}/pages/command-center-jsx.page.ts | 0 .../src/{ => ui}/pages/command-center.page.ts | 0 .../src/{ => ui}/pages/contact-form.page.ts | 0 .../src/{ => ui}/pages/crm-workbench.page.ts | 0 .../app-showcase/src/{ => ui}/pages/index.ts | 0 .../src/{ => ui}/pages/my-work.page.ts | 0 .../{ => ui}/pages/new-project-wizard.page.ts | 0 .../src/{ => ui}/pages/page-variables.page.ts | 0 .../src/{ => ui}/pages/project-detail.page.ts | 0 .../{ => ui}/pages/project-workspace.page.ts | 0 .../{ => ui}/pages/renewals-pipeline.page.ts | 0 .../src/{ => ui}/pages/review-queue.page.ts | 0 .../src/{ => ui}/pages/settings.page.ts | 0 .../src/{ => ui}/pages/start-here.page.ts | 0 .../{ => ui}/pages/styling-gallery.page.ts | 0 .../src/{ => ui}/pages/task-desk.page.ts | 0 .../src/{ => ui}/pages/task-detail.page.ts | 0 .../src/{ => ui}/pages/task-triage.page.ts | 0 .../pages/task-visualizations.pages.ts | 0 .../src/{ => ui}/pages/task-workbench.page.ts | 0 .../src/{ => ui}/portals/index.ts | 0 .../src/{ => ui}/reports/index.ts | 2 +- .../app-showcase/src/{ => ui}/themes/index.ts | 0 .../src/{ => ui}/views/business-unit.view.ts | 0 .../src/{ => ui}/views/contact.view.ts | 0 .../app-showcase/src/{ => ui}/views/index.ts | 0 .../src/{ => ui}/views/inquiry.view.ts | 0 .../src/{ => ui}/views/project.view.ts | 0 .../src/{ => ui}/views/task.view.ts | 0 examples/app-showcase/test/actions.test.ts | 2 +- examples/app-showcase/test/coverage.test.ts | 12 ++--- .../test/external-datasource.test.ts | 4 +- .../app-showcase/test/page-variables.test.ts | 4 +- .../app-showcase/test/recalc-endpoint.test.ts | 2 +- examples/app-showcase/test/validation.test.ts | 2 +- 76 files changed, 44 insertions(+), 44 deletions(-) rename examples/app-showcase/src/{ => automation}/flows/index.ts (100%) rename examples/app-showcase/src/{ => automation}/jobs/index.ts (100%) rename examples/app-showcase/src/{ => automation}/webhooks/index.ts (100%) rename examples/app-showcase/src/{ => data}/hooks/index.ts (100%) rename examples/app-showcase/src/{ => data}/objects/account.object.ts (100%) rename examples/app-showcase/src/{ => data}/objects/announcement.object.ts (100%) rename examples/app-showcase/src/{ => data}/objects/business-unit.object.ts (100%) rename examples/app-showcase/src/{ => data}/objects/category.object.ts (100%) rename examples/app-showcase/src/{ => data}/objects/contact.object.ts (100%) rename examples/app-showcase/src/{ => data}/objects/external/customer.object.ts (100%) rename examples/app-showcase/src/{ => data}/objects/external/index.ts (100%) rename examples/app-showcase/src/{ => data}/objects/external/order.object.ts (100%) rename examples/app-showcase/src/{ => data}/objects/field-zoo.object.ts (100%) rename examples/app-showcase/src/{ => data}/objects/index.ts (100%) rename examples/app-showcase/src/{ => data}/objects/inquiry.object.ts (100%) rename examples/app-showcase/src/{ => data}/objects/invoice.object.ts (100%) rename examples/app-showcase/src/{ => data}/objects/preference.object.ts (100%) rename examples/app-showcase/src/{ => data}/objects/private-note.object.ts (100%) rename examples/app-showcase/src/{ => data}/objects/project.object.ts (100%) rename examples/app-showcase/src/{ => data}/objects/semantic-zoo.object.ts (100%) rename examples/app-showcase/src/{ => data}/objects/task.object.ts (100%) rename examples/app-showcase/src/{ => data}/objects/team.object.ts (100%) rename examples/app-showcase/src/data/{ => seed}/index.ts (100%) rename examples/app-showcase/src/{ => system}/books/index.ts (100%) rename examples/app-showcase/src/{ => system}/datasources/external-fixture.ts (100%) rename examples/app-showcase/src/{ => system}/datasources/index.ts (100%) rename examples/app-showcase/src/{ => system}/datasources/showcase-external.datasource.ts (100%) rename examples/app-showcase/src/{ => system}/emails/index.ts (100%) rename examples/app-showcase/src/{ => system}/server/recalc-endpoint.ts (100%) rename examples/app-showcase/src/{ => system}/translations/index.ts (100%) rename examples/app-showcase/src/{ => ui}/actions/index.ts (100%) rename examples/app-showcase/src/{ => ui}/apps/index.ts (100%) rename examples/app-showcase/src/{ => ui}/dashboards/chart-gallery.dashboard.ts (100%) rename examples/app-showcase/src/{ => ui}/dashboards/index.ts (100%) rename examples/app-showcase/src/{ => ui}/dashboards/ops-dashboard.dashboard.ts (100%) rename examples/app-showcase/src/{ => ui}/datasets/chart-gallery.dataset.ts (100%) rename examples/app-showcase/src/{ => ui}/datasets/index.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/account-cockpit.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/active-projects.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/command-center-jsx.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/command-center.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/contact-form.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/crm-workbench.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/index.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/my-work.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/new-project-wizard.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/page-variables.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/project-detail.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/project-workspace.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/renewals-pipeline.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/review-queue.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/settings.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/start-here.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/styling-gallery.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/task-desk.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/task-detail.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/task-triage.page.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/task-visualizations.pages.ts (100%) rename examples/app-showcase/src/{ => ui}/pages/task-workbench.page.ts (100%) rename examples/app-showcase/src/{ => ui}/portals/index.ts (100%) rename examples/app-showcase/src/{ => ui}/reports/index.ts (96%) rename examples/app-showcase/src/{ => ui}/themes/index.ts (100%) rename examples/app-showcase/src/{ => ui}/views/business-unit.view.ts (100%) rename examples/app-showcase/src/{ => ui}/views/contact.view.ts (100%) rename examples/app-showcase/src/{ => ui}/views/index.ts (100%) rename examples/app-showcase/src/{ => ui}/views/inquiry.view.ts (100%) rename examples/app-showcase/src/{ => ui}/views/project.view.ts (100%) rename examples/app-showcase/src/{ => ui}/views/task.view.ts (100%) diff --git a/examples/app-showcase/objectstack.config.ts b/examples/app-showcase/objectstack.config.ts index ea22d2528e..4d570fbdf7 100644 --- a/examples/app-showcase/objectstack.config.ts +++ b/examples/app-showcase/objectstack.config.ts @@ -11,33 +11,33 @@ import { resolveCloudUrl, } from '@objectstack/cloud-connection'; -import * as objects from './src/objects/index.js'; -import { ShowcaseExternalDatasource } from './src/datasources/showcase-external.datasource.js'; -import { ExternalCustomer, ExternalOrder } from './src/objects/external/index.js'; -import { setupShowcaseExternalDatasource } from './src/datasources/external-fixture.js'; -import { registerRecalcEndpoint } from './src/server/recalc-endpoint.js'; -import { TaskViews, ProjectViews, InquiryViews, BusinessUnitViews } from './src/views/index.js'; -import { ShowcaseApp } from './src/apps/index.js'; -import { ChartGalleryDashboard, OpsDashboard } from './src/dashboards/index.js'; -import { ShowcaseTaskDataset, ShowcaseProjectDataset } from './src/datasets/index.js'; -import { allReports } from './src/reports/index.js'; -import { allActions } from './src/actions/index.js'; -import { StartHerePage, ComponentGalleryPage, ProjectWorkspacePage, ProjectDetailPage, TaskWorkbenchPage, TaskTriagePage, TaskBoardPage, TaskCalendarPage, TaskGalleryPage, TaskSchedulePage, TaskTimelinePage, TaskMapPage, TaskAllViewsPage, ActiveProjectsPage, TaskDetailPage, ReviewQueuePage, NewProjectWizardPage, MyWorkPage, SettingsPage, StylingGalleryPage, CommandCenterPage, CommandCenterJsxPage, CrmWorkbenchPage, AccountCockpitPage, TaskDeskPage, PageVariablesPage, ContactFormPage, RenewalsPipelinePage } from './src/pages/index.js'; -import { allFlows } from './src/flows/index.js'; -import { allWebhooks } from './src/webhooks/index.js'; -import { allHooks } from './src/hooks/index.js'; -import { allJobs } from './src/jobs/index.js'; -import { allEmails } from './src/emails/index.js'; -import { allBooks } from './src/books/index.js'; +import * as objects from './src/data/objects/index.js'; +import { ShowcaseExternalDatasource } from './src/system/datasources/showcase-external.datasource.js'; +import { ExternalCustomer, ExternalOrder } from './src/data/objects/external/index.js'; +import { setupShowcaseExternalDatasource } from './src/system/datasources/external-fixture.js'; +import { registerRecalcEndpoint } from './src/system/server/recalc-endpoint.js'; +import { TaskViews, ProjectViews, InquiryViews, BusinessUnitViews } from './src/ui/views/index.js'; +import { ShowcaseApp } from './src/ui/apps/index.js'; +import { ChartGalleryDashboard, OpsDashboard } from './src/ui/dashboards/index.js'; +import { ShowcaseTaskDataset, ShowcaseProjectDataset } from './src/ui/datasets/index.js'; +import { allReports } from './src/ui/reports/index.js'; +import { allActions } from './src/ui/actions/index.js'; +import { StartHerePage, ComponentGalleryPage, ProjectWorkspacePage, ProjectDetailPage, TaskWorkbenchPage, TaskTriagePage, TaskBoardPage, TaskCalendarPage, TaskGalleryPage, TaskSchedulePage, TaskTimelinePage, TaskMapPage, TaskAllViewsPage, ActiveProjectsPage, TaskDetailPage, ReviewQueuePage, NewProjectWizardPage, MyWorkPage, SettingsPage, StylingGalleryPage, CommandCenterPage, CommandCenterJsxPage, CrmWorkbenchPage, AccountCockpitPage, TaskDeskPage, PageVariablesPage, ContactFormPage, RenewalsPipelinePage } from './src/ui/pages/index.js'; +import { allFlows } from './src/automation/flows/index.js'; +import { allWebhooks } from './src/automation/webhooks/index.js'; +import { allHooks } from './src/data/hooks/index.js'; +import { allJobs } from './src/automation/jobs/index.js'; +import { allEmails } from './src/system/emails/index.js'; +import { allBooks } from './src/system/books/index.js'; import { allRoles, allPermissionSets, allSharingRules, } from './src/security/index.js'; -import { allThemes } from './src/themes/index.js'; -import { ShowcaseTranslationBundle } from './src/translations/index.js'; -import { allPortals } from './src/portals/index.js'; -import { ShowcaseSeedData } from './src/data/index.js'; +import { allThemes } from './src/ui/themes/index.js'; +import { ShowcaseTranslationBundle } from './src/system/translations/index.js'; +import { allPortals } from './src/ui/portals/index.js'; +import { ShowcaseSeedData } from './src/data/seed/index.js'; // Ambient `process` for the env-var overrides below — the showcase tsconfig // doesn't pull in `@types/node`, but the CLI provides the real `process` at diff --git a/examples/app-showcase/src/flows/index.ts b/examples/app-showcase/src/automation/flows/index.ts similarity index 100% rename from examples/app-showcase/src/flows/index.ts rename to examples/app-showcase/src/automation/flows/index.ts diff --git a/examples/app-showcase/src/jobs/index.ts b/examples/app-showcase/src/automation/jobs/index.ts similarity index 100% rename from examples/app-showcase/src/jobs/index.ts rename to examples/app-showcase/src/automation/jobs/index.ts diff --git a/examples/app-showcase/src/webhooks/index.ts b/examples/app-showcase/src/automation/webhooks/index.ts similarity index 100% rename from examples/app-showcase/src/webhooks/index.ts rename to examples/app-showcase/src/automation/webhooks/index.ts diff --git a/examples/app-showcase/src/coverage.ts b/examples/app-showcase/src/coverage.ts index e849383297..4ae5e0b972 100644 --- a/examples/app-showcase/src/coverage.ts +++ b/examples/app-showcase/src/coverage.ts @@ -36,7 +36,7 @@ export const FORM_VIEW_TYPES = ['simple', 'tabbed', 'wizard', 'split', 'drawer'] export const COVERAGE = { fieldTypes: { source: 'FieldTypeSchema', - coveredBy: 'objects/field-zoo.object.ts (+ relationship/date/select fields on the backbone objects)', + coveredBy: 'data/objects/field-zoo.object.ts (+ relationship/date/select fields on the backbone objects)', }, relationships: { coveredBy: [ @@ -47,30 +47,30 @@ export const COVERAGE = { }, listViewTypes: { expected: LIST_VIEW_TYPES, - coveredBy: 'views/task.view.ts (all 8) + views/project.view.ts', + coveredBy: 'ui/views/task.view.ts (all 8) + ui/views/project.view.ts', }, formViewTypes: { expected: FORM_VIEW_TYPES, - coveredBy: 'views/task.view.ts formViews (simple/tabbed/wizard/split/drawer)', + coveredBy: 'ui/views/task.view.ts formViews (simple/tabbed/wizard/split/drawer)', }, chartTypes: { source: 'ChartTypeSchema', - coveredBy: 'dashboards/chart-gallery.dashboard.ts (one widget per chart family)', + coveredBy: 'ui/dashboards/chart-gallery.dashboard.ts (one widget per chart family)', }, reportTypes: { source: 'ReportType', - coveredBy: 'reports/index.ts (tabular/summary/matrix/joined)', + coveredBy: 'ui/reports/index.ts (tabular/summary/matrix/joined)', }, actionTypesAndLocations: { source: 'ActionType + ACTION_LOCATIONS', - coveredBy: 'actions/index.ts (script/url/flow/modal/api/form across all locations)', + coveredBy: 'ui/actions/index.ts (script/url/flow/modal/api/form across all locations)', }, capabilityChains: { security: 'security/index.ts — roles + permission set (CRUD + FLS + RLS) + sharing + policy', - automation: 'flows/index.ts (incl. approval nodes) + webhooks/index.ts + jobs/index.ts + emails/index.ts', + automation: 'automation/flows/index.ts (incl. approval nodes) + automation/webhooks/index.ts + automation/jobs/index.ts + system/emails/index.ts', }, i18nThemingPortals: { - coveredBy: 'translations/index.ts (en + zh-CN), themes/index.ts (light + dark), portals/index.ts', + coveredBy: 'system/translations/index.ts (en + zh-CN), ui/themes/index.ts (light + dark), ui/portals/index.ts', }, docs: { source: 'ADR-0046 (doc metadata)', diff --git a/examples/app-showcase/src/hooks/index.ts b/examples/app-showcase/src/data/hooks/index.ts similarity index 100% rename from examples/app-showcase/src/hooks/index.ts rename to examples/app-showcase/src/data/hooks/index.ts diff --git a/examples/app-showcase/src/objects/account.object.ts b/examples/app-showcase/src/data/objects/account.object.ts similarity index 100% rename from examples/app-showcase/src/objects/account.object.ts rename to examples/app-showcase/src/data/objects/account.object.ts diff --git a/examples/app-showcase/src/objects/announcement.object.ts b/examples/app-showcase/src/data/objects/announcement.object.ts similarity index 100% rename from examples/app-showcase/src/objects/announcement.object.ts rename to examples/app-showcase/src/data/objects/announcement.object.ts diff --git a/examples/app-showcase/src/objects/business-unit.object.ts b/examples/app-showcase/src/data/objects/business-unit.object.ts similarity index 100% rename from examples/app-showcase/src/objects/business-unit.object.ts rename to examples/app-showcase/src/data/objects/business-unit.object.ts diff --git a/examples/app-showcase/src/objects/category.object.ts b/examples/app-showcase/src/data/objects/category.object.ts similarity index 100% rename from examples/app-showcase/src/objects/category.object.ts rename to examples/app-showcase/src/data/objects/category.object.ts diff --git a/examples/app-showcase/src/objects/contact.object.ts b/examples/app-showcase/src/data/objects/contact.object.ts similarity index 100% rename from examples/app-showcase/src/objects/contact.object.ts rename to examples/app-showcase/src/data/objects/contact.object.ts diff --git a/examples/app-showcase/src/objects/external/customer.object.ts b/examples/app-showcase/src/data/objects/external/customer.object.ts similarity index 100% rename from examples/app-showcase/src/objects/external/customer.object.ts rename to examples/app-showcase/src/data/objects/external/customer.object.ts diff --git a/examples/app-showcase/src/objects/external/index.ts b/examples/app-showcase/src/data/objects/external/index.ts similarity index 100% rename from examples/app-showcase/src/objects/external/index.ts rename to examples/app-showcase/src/data/objects/external/index.ts diff --git a/examples/app-showcase/src/objects/external/order.object.ts b/examples/app-showcase/src/data/objects/external/order.object.ts similarity index 100% rename from examples/app-showcase/src/objects/external/order.object.ts rename to examples/app-showcase/src/data/objects/external/order.object.ts diff --git a/examples/app-showcase/src/objects/field-zoo.object.ts b/examples/app-showcase/src/data/objects/field-zoo.object.ts similarity index 100% rename from examples/app-showcase/src/objects/field-zoo.object.ts rename to examples/app-showcase/src/data/objects/field-zoo.object.ts diff --git a/examples/app-showcase/src/objects/index.ts b/examples/app-showcase/src/data/objects/index.ts similarity index 100% rename from examples/app-showcase/src/objects/index.ts rename to examples/app-showcase/src/data/objects/index.ts diff --git a/examples/app-showcase/src/objects/inquiry.object.ts b/examples/app-showcase/src/data/objects/inquiry.object.ts similarity index 100% rename from examples/app-showcase/src/objects/inquiry.object.ts rename to examples/app-showcase/src/data/objects/inquiry.object.ts diff --git a/examples/app-showcase/src/objects/invoice.object.ts b/examples/app-showcase/src/data/objects/invoice.object.ts similarity index 100% rename from examples/app-showcase/src/objects/invoice.object.ts rename to examples/app-showcase/src/data/objects/invoice.object.ts diff --git a/examples/app-showcase/src/objects/preference.object.ts b/examples/app-showcase/src/data/objects/preference.object.ts similarity index 100% rename from examples/app-showcase/src/objects/preference.object.ts rename to examples/app-showcase/src/data/objects/preference.object.ts diff --git a/examples/app-showcase/src/objects/private-note.object.ts b/examples/app-showcase/src/data/objects/private-note.object.ts similarity index 100% rename from examples/app-showcase/src/objects/private-note.object.ts rename to examples/app-showcase/src/data/objects/private-note.object.ts diff --git a/examples/app-showcase/src/objects/project.object.ts b/examples/app-showcase/src/data/objects/project.object.ts similarity index 100% rename from examples/app-showcase/src/objects/project.object.ts rename to examples/app-showcase/src/data/objects/project.object.ts diff --git a/examples/app-showcase/src/objects/semantic-zoo.object.ts b/examples/app-showcase/src/data/objects/semantic-zoo.object.ts similarity index 100% rename from examples/app-showcase/src/objects/semantic-zoo.object.ts rename to examples/app-showcase/src/data/objects/semantic-zoo.object.ts diff --git a/examples/app-showcase/src/objects/task.object.ts b/examples/app-showcase/src/data/objects/task.object.ts similarity index 100% rename from examples/app-showcase/src/objects/task.object.ts rename to examples/app-showcase/src/data/objects/task.object.ts diff --git a/examples/app-showcase/src/objects/team.object.ts b/examples/app-showcase/src/data/objects/team.object.ts similarity index 100% rename from examples/app-showcase/src/objects/team.object.ts rename to examples/app-showcase/src/data/objects/team.object.ts diff --git a/examples/app-showcase/src/data/index.ts b/examples/app-showcase/src/data/seed/index.ts similarity index 100% rename from examples/app-showcase/src/data/index.ts rename to examples/app-showcase/src/data/seed/index.ts diff --git a/examples/app-showcase/src/books/index.ts b/examples/app-showcase/src/system/books/index.ts similarity index 100% rename from examples/app-showcase/src/books/index.ts rename to examples/app-showcase/src/system/books/index.ts diff --git a/examples/app-showcase/src/datasources/external-fixture.ts b/examples/app-showcase/src/system/datasources/external-fixture.ts similarity index 100% rename from examples/app-showcase/src/datasources/external-fixture.ts rename to examples/app-showcase/src/system/datasources/external-fixture.ts diff --git a/examples/app-showcase/src/datasources/index.ts b/examples/app-showcase/src/system/datasources/index.ts similarity index 100% rename from examples/app-showcase/src/datasources/index.ts rename to examples/app-showcase/src/system/datasources/index.ts diff --git a/examples/app-showcase/src/datasources/showcase-external.datasource.ts b/examples/app-showcase/src/system/datasources/showcase-external.datasource.ts similarity index 100% rename from examples/app-showcase/src/datasources/showcase-external.datasource.ts rename to examples/app-showcase/src/system/datasources/showcase-external.datasource.ts diff --git a/examples/app-showcase/src/emails/index.ts b/examples/app-showcase/src/system/emails/index.ts similarity index 100% rename from examples/app-showcase/src/emails/index.ts rename to examples/app-showcase/src/system/emails/index.ts diff --git a/examples/app-showcase/src/server/recalc-endpoint.ts b/examples/app-showcase/src/system/server/recalc-endpoint.ts similarity index 100% rename from examples/app-showcase/src/server/recalc-endpoint.ts rename to examples/app-showcase/src/system/server/recalc-endpoint.ts diff --git a/examples/app-showcase/src/translations/index.ts b/examples/app-showcase/src/system/translations/index.ts similarity index 100% rename from examples/app-showcase/src/translations/index.ts rename to examples/app-showcase/src/system/translations/index.ts diff --git a/examples/app-showcase/src/actions/index.ts b/examples/app-showcase/src/ui/actions/index.ts similarity index 100% rename from examples/app-showcase/src/actions/index.ts rename to examples/app-showcase/src/ui/actions/index.ts diff --git a/examples/app-showcase/src/apps/index.ts b/examples/app-showcase/src/ui/apps/index.ts similarity index 100% rename from examples/app-showcase/src/apps/index.ts rename to examples/app-showcase/src/ui/apps/index.ts diff --git a/examples/app-showcase/src/dashboards/chart-gallery.dashboard.ts b/examples/app-showcase/src/ui/dashboards/chart-gallery.dashboard.ts similarity index 100% rename from examples/app-showcase/src/dashboards/chart-gallery.dashboard.ts rename to examples/app-showcase/src/ui/dashboards/chart-gallery.dashboard.ts diff --git a/examples/app-showcase/src/dashboards/index.ts b/examples/app-showcase/src/ui/dashboards/index.ts similarity index 100% rename from examples/app-showcase/src/dashboards/index.ts rename to examples/app-showcase/src/ui/dashboards/index.ts diff --git a/examples/app-showcase/src/dashboards/ops-dashboard.dashboard.ts b/examples/app-showcase/src/ui/dashboards/ops-dashboard.dashboard.ts similarity index 100% rename from examples/app-showcase/src/dashboards/ops-dashboard.dashboard.ts rename to examples/app-showcase/src/ui/dashboards/ops-dashboard.dashboard.ts diff --git a/examples/app-showcase/src/datasets/chart-gallery.dataset.ts b/examples/app-showcase/src/ui/datasets/chart-gallery.dataset.ts similarity index 100% rename from examples/app-showcase/src/datasets/chart-gallery.dataset.ts rename to examples/app-showcase/src/ui/datasets/chart-gallery.dataset.ts diff --git a/examples/app-showcase/src/datasets/index.ts b/examples/app-showcase/src/ui/datasets/index.ts similarity index 100% rename from examples/app-showcase/src/datasets/index.ts rename to examples/app-showcase/src/ui/datasets/index.ts diff --git a/examples/app-showcase/src/pages/account-cockpit.page.ts b/examples/app-showcase/src/ui/pages/account-cockpit.page.ts similarity index 100% rename from examples/app-showcase/src/pages/account-cockpit.page.ts rename to examples/app-showcase/src/ui/pages/account-cockpit.page.ts diff --git a/examples/app-showcase/src/pages/active-projects.page.ts b/examples/app-showcase/src/ui/pages/active-projects.page.ts similarity index 100% rename from examples/app-showcase/src/pages/active-projects.page.ts rename to examples/app-showcase/src/ui/pages/active-projects.page.ts diff --git a/examples/app-showcase/src/pages/command-center-jsx.page.ts b/examples/app-showcase/src/ui/pages/command-center-jsx.page.ts similarity index 100% rename from examples/app-showcase/src/pages/command-center-jsx.page.ts rename to examples/app-showcase/src/ui/pages/command-center-jsx.page.ts diff --git a/examples/app-showcase/src/pages/command-center.page.ts b/examples/app-showcase/src/ui/pages/command-center.page.ts similarity index 100% rename from examples/app-showcase/src/pages/command-center.page.ts rename to examples/app-showcase/src/ui/pages/command-center.page.ts diff --git a/examples/app-showcase/src/pages/contact-form.page.ts b/examples/app-showcase/src/ui/pages/contact-form.page.ts similarity index 100% rename from examples/app-showcase/src/pages/contact-form.page.ts rename to examples/app-showcase/src/ui/pages/contact-form.page.ts diff --git a/examples/app-showcase/src/pages/crm-workbench.page.ts b/examples/app-showcase/src/ui/pages/crm-workbench.page.ts similarity index 100% rename from examples/app-showcase/src/pages/crm-workbench.page.ts rename to examples/app-showcase/src/ui/pages/crm-workbench.page.ts diff --git a/examples/app-showcase/src/pages/index.ts b/examples/app-showcase/src/ui/pages/index.ts similarity index 100% rename from examples/app-showcase/src/pages/index.ts rename to examples/app-showcase/src/ui/pages/index.ts diff --git a/examples/app-showcase/src/pages/my-work.page.ts b/examples/app-showcase/src/ui/pages/my-work.page.ts similarity index 100% rename from examples/app-showcase/src/pages/my-work.page.ts rename to examples/app-showcase/src/ui/pages/my-work.page.ts diff --git a/examples/app-showcase/src/pages/new-project-wizard.page.ts b/examples/app-showcase/src/ui/pages/new-project-wizard.page.ts similarity index 100% rename from examples/app-showcase/src/pages/new-project-wizard.page.ts rename to examples/app-showcase/src/ui/pages/new-project-wizard.page.ts diff --git a/examples/app-showcase/src/pages/page-variables.page.ts b/examples/app-showcase/src/ui/pages/page-variables.page.ts similarity index 100% rename from examples/app-showcase/src/pages/page-variables.page.ts rename to examples/app-showcase/src/ui/pages/page-variables.page.ts diff --git a/examples/app-showcase/src/pages/project-detail.page.ts b/examples/app-showcase/src/ui/pages/project-detail.page.ts similarity index 100% rename from examples/app-showcase/src/pages/project-detail.page.ts rename to examples/app-showcase/src/ui/pages/project-detail.page.ts diff --git a/examples/app-showcase/src/pages/project-workspace.page.ts b/examples/app-showcase/src/ui/pages/project-workspace.page.ts similarity index 100% rename from examples/app-showcase/src/pages/project-workspace.page.ts rename to examples/app-showcase/src/ui/pages/project-workspace.page.ts diff --git a/examples/app-showcase/src/pages/renewals-pipeline.page.ts b/examples/app-showcase/src/ui/pages/renewals-pipeline.page.ts similarity index 100% rename from examples/app-showcase/src/pages/renewals-pipeline.page.ts rename to examples/app-showcase/src/ui/pages/renewals-pipeline.page.ts diff --git a/examples/app-showcase/src/pages/review-queue.page.ts b/examples/app-showcase/src/ui/pages/review-queue.page.ts similarity index 100% rename from examples/app-showcase/src/pages/review-queue.page.ts rename to examples/app-showcase/src/ui/pages/review-queue.page.ts diff --git a/examples/app-showcase/src/pages/settings.page.ts b/examples/app-showcase/src/ui/pages/settings.page.ts similarity index 100% rename from examples/app-showcase/src/pages/settings.page.ts rename to examples/app-showcase/src/ui/pages/settings.page.ts diff --git a/examples/app-showcase/src/pages/start-here.page.ts b/examples/app-showcase/src/ui/pages/start-here.page.ts similarity index 100% rename from examples/app-showcase/src/pages/start-here.page.ts rename to examples/app-showcase/src/ui/pages/start-here.page.ts diff --git a/examples/app-showcase/src/pages/styling-gallery.page.ts b/examples/app-showcase/src/ui/pages/styling-gallery.page.ts similarity index 100% rename from examples/app-showcase/src/pages/styling-gallery.page.ts rename to examples/app-showcase/src/ui/pages/styling-gallery.page.ts diff --git a/examples/app-showcase/src/pages/task-desk.page.ts b/examples/app-showcase/src/ui/pages/task-desk.page.ts similarity index 100% rename from examples/app-showcase/src/pages/task-desk.page.ts rename to examples/app-showcase/src/ui/pages/task-desk.page.ts diff --git a/examples/app-showcase/src/pages/task-detail.page.ts b/examples/app-showcase/src/ui/pages/task-detail.page.ts similarity index 100% rename from examples/app-showcase/src/pages/task-detail.page.ts rename to examples/app-showcase/src/ui/pages/task-detail.page.ts diff --git a/examples/app-showcase/src/pages/task-triage.page.ts b/examples/app-showcase/src/ui/pages/task-triage.page.ts similarity index 100% rename from examples/app-showcase/src/pages/task-triage.page.ts rename to examples/app-showcase/src/ui/pages/task-triage.page.ts diff --git a/examples/app-showcase/src/pages/task-visualizations.pages.ts b/examples/app-showcase/src/ui/pages/task-visualizations.pages.ts similarity index 100% rename from examples/app-showcase/src/pages/task-visualizations.pages.ts rename to examples/app-showcase/src/ui/pages/task-visualizations.pages.ts diff --git a/examples/app-showcase/src/pages/task-workbench.page.ts b/examples/app-showcase/src/ui/pages/task-workbench.page.ts similarity index 100% rename from examples/app-showcase/src/pages/task-workbench.page.ts rename to examples/app-showcase/src/ui/pages/task-workbench.page.ts diff --git a/examples/app-showcase/src/portals/index.ts b/examples/app-showcase/src/ui/portals/index.ts similarity index 100% rename from examples/app-showcase/src/portals/index.ts rename to examples/app-showcase/src/ui/portals/index.ts diff --git a/examples/app-showcase/src/reports/index.ts b/examples/app-showcase/src/ui/reports/index.ts similarity index 96% rename from examples/app-showcase/src/reports/index.ts rename to examples/app-showcase/src/ui/reports/index.ts index ab5df66044..a8d27a7798 100644 --- a/examples/app-showcase/src/reports/index.ts +++ b/examples/app-showcase/src/ui/reports/index.ts @@ -6,7 +6,7 @@ const task = 'showcase_task'; // ADR-0021 Phase 2: the former `TaskListReport` (showcase_task_list) — a flat // record list — was converted to the `tabular` ListView on showcase_task -// (src/views/task.view.ts). A flat list is an object-bound row lens (ADR-0017), +// (src/ui/views/task.view.ts). A flat list is an object-bound row lens (ADR-0017), // not analytics, so it is no longer a report. /** 2 ── Summary: grouped down by status with a sum. */ diff --git a/examples/app-showcase/src/themes/index.ts b/examples/app-showcase/src/ui/themes/index.ts similarity index 100% rename from examples/app-showcase/src/themes/index.ts rename to examples/app-showcase/src/ui/themes/index.ts diff --git a/examples/app-showcase/src/views/business-unit.view.ts b/examples/app-showcase/src/ui/views/business-unit.view.ts similarity index 100% rename from examples/app-showcase/src/views/business-unit.view.ts rename to examples/app-showcase/src/ui/views/business-unit.view.ts diff --git a/examples/app-showcase/src/views/contact.view.ts b/examples/app-showcase/src/ui/views/contact.view.ts similarity index 100% rename from examples/app-showcase/src/views/contact.view.ts rename to examples/app-showcase/src/ui/views/contact.view.ts diff --git a/examples/app-showcase/src/views/index.ts b/examples/app-showcase/src/ui/views/index.ts similarity index 100% rename from examples/app-showcase/src/views/index.ts rename to examples/app-showcase/src/ui/views/index.ts diff --git a/examples/app-showcase/src/views/inquiry.view.ts b/examples/app-showcase/src/ui/views/inquiry.view.ts similarity index 100% rename from examples/app-showcase/src/views/inquiry.view.ts rename to examples/app-showcase/src/ui/views/inquiry.view.ts diff --git a/examples/app-showcase/src/views/project.view.ts b/examples/app-showcase/src/ui/views/project.view.ts similarity index 100% rename from examples/app-showcase/src/views/project.view.ts rename to examples/app-showcase/src/ui/views/project.view.ts diff --git a/examples/app-showcase/src/views/task.view.ts b/examples/app-showcase/src/ui/views/task.view.ts similarity index 100% rename from examples/app-showcase/src/views/task.view.ts rename to examples/app-showcase/src/ui/views/task.view.ts diff --git a/examples/app-showcase/test/actions.test.ts b/examples/app-showcase/test/actions.test.ts index 265d1babac..4fb1d150e5 100644 --- a/examples/app-showcase/test/actions.test.ts +++ b/examples/app-showcase/test/actions.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect } from 'vitest'; import { actionBodyRunnerFactory, QuickJSScriptRunner } from '@objectstack/runtime'; -import { allActions, MarkDoneAction } from '../src/actions/index.js'; +import { allActions, MarkDoneAction } from '../src/ui/actions/index.js'; /** * Execution-path coverage for declared actions. diff --git a/examples/app-showcase/test/coverage.test.ts b/examples/app-showcase/test/coverage.test.ts index e55d5cd010..ac58795787 100644 --- a/examples/app-showcase/test/coverage.test.ts +++ b/examples/app-showcase/test/coverage.test.ts @@ -4,11 +4,11 @@ import { describe, it, expect } from 'vitest'; import { FieldType } from '@objectstack/spec/data'; import * as ui from '@objectstack/spec/ui'; -import * as objects from '../src/objects/index.js'; -import { TaskViews, ProjectViews } from '../src/views/index.js'; -import { ChartGalleryDashboard } from '../src/dashboards/index.js'; -import { allReports } from '../src/reports/index.js'; -import { allActions } from '../src/actions/index.js'; +import * as objects from '../src/data/objects/index.js'; +import { TaskViews, ProjectViews } from '../src/ui/views/index.js'; +import { ChartGalleryDashboard } from '../src/ui/dashboards/index.js'; +import { allReports } from '../src/ui/reports/index.js'; +import { allActions } from '../src/ui/actions/index.js'; import { LIST_VIEW_TYPES, FORM_VIEW_TYPES, @@ -66,7 +66,7 @@ describe('showcase coverage (introspected against the spec)', () => { // ADR-0021 single-form: `tabular` (a flat record list) is intentionally NOT // demonstrated as a report — a flat list is an object-bound ListView lens // (ADR-0017), not an analytics projection, so the former TaskListReport now - // lives on showcase_task as a `tabular` ListView (see src/reports/index.ts). + // lives on showcase_task as a `tabular` ListView (see src/ui/reports/index.ts). const expected = enumValues((ui as Record).ReportType ?? (ui as Record).ReportTypeSchema) .filter((t) => t !== 'tabular'); const used = new Set(); diff --git a/examples/app-showcase/test/external-datasource.test.ts b/examples/app-showcase/test/external-datasource.test.ts index 238ad97381..a8f7cdf9fd 100644 --- a/examples/app-showcase/test/external-datasource.test.ts +++ b/examples/app-showcase/test/external-datasource.test.ts @@ -9,8 +9,8 @@ */ import { describe, it, expect } from 'vitest'; -import { ShowcaseExternalDatasource } from '../src/datasources/showcase-external.datasource.js'; -import { ExternalCustomer, ExternalOrder } from '../src/objects/external/index.js'; +import { ShowcaseExternalDatasource } from '../src/system/datasources/showcase-external.datasource.js'; +import { ExternalCustomer, ExternalOrder } from '../src/data/objects/external/index.js'; describe('showcase external datasource (ADR-0015 federation)', () => { it('is an external, read-only datasource that degrades gracefully', () => { diff --git a/examples/app-showcase/test/page-variables.test.ts b/examples/app-showcase/test/page-variables.test.ts index 5d27166474..53f3dc0165 100644 --- a/examples/app-showcase/test/page-variables.test.ts +++ b/examples/app-showcase/test/page-variables.test.ts @@ -2,9 +2,9 @@ import { describe, it, expect } from 'vitest'; -import { PageVariablesPage } from '../src/pages/index.js'; +import { PageVariablesPage } from '../src/ui/pages/index.js'; import stack from '../objectstack.config.js'; -import { ShowcaseApp } from '../src/apps/index.js'; +import { ShowcaseApp } from '../src/ui/apps/index.js'; /** * Dogfood gate for page-local state (PageSchema.variables, ADR-0049). diff --git a/examples/app-showcase/test/recalc-endpoint.test.ts b/examples/app-showcase/test/recalc-endpoint.test.ts index bcbb21f2f0..38b9a0294d 100644 --- a/examples/app-showcase/test/recalc-endpoint.test.ts +++ b/examples/app-showcase/test/recalc-endpoint.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. import { describe, it, expect } from 'vitest'; -import { registerRecalcEndpoint } from '../src/server/recalc-endpoint.js'; +import { registerRecalcEndpoint } from '../src/system/server/recalc-endpoint.js'; /** * Unit coverage for the custom REST endpoint behind the `showcase_recalc_estimate` diff --git a/examples/app-showcase/test/validation.test.ts b/examples/app-showcase/test/validation.test.ts index adeddc8ed3..1f4d61b49f 100644 --- a/examples/app-showcase/test/validation.test.ts +++ b/examples/app-showcase/test/validation.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect } from 'vitest'; import { evaluateValidationRules, ValidationError } from '@objectstack/objectql'; -import { Account } from '../src/objects/account.object.js'; +import { Account } from '../src/data/objects/account.object.js'; /** * Verifies the Account object's declared validation rules actually enforce on From 7768a759356eb1303c4c22544978dc58c5b0d545 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Jul 2026 02:49:07 +0000 Subject: [PATCH 2/6] =?UTF-8?q?feat(showcase):=20registry-driven=20kind=20?= =?UTF-8?q?coverage=20=E2=80=94=20every=20metadata=20kind=20demonstrated?= =?UTF-8?q?=20or=20waived?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the coverage manifest from enum variants to metadata kinds: KIND_COVERAGE enumerates DEFAULT_METADATA_TYPE_REGISTRY, and the coverage test now fails when the platform gains a kind the showcase has not accounted for, when a demonstrated kind's proof files go missing, or when a waiver lacks a reason + tracking-issue link. STACK_COLLECTION_COVERAGE applies the same contract to stack collections that are not registry kinds (mappings, connectors). Waivers (Prime Directive #10 — never fake coverage, never hide a gap): - agent/tool/skill → #2610 (AI examples deferred; ADR-0063 cloud/MCP-only) - mappings → #2611 (registered but never consumed) - connectors → #2612 (declarative entries inert; live demo is plugin-based) - trigger/router/function/service/external_catalog → #2613 (no declarative authoring surface) - validation notes the 6 unenforced rule types → #1475 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01G6pPpRszk9cD3SxcKNFMWs --- examples/app-showcase/src/coverage.ts | 171 +++++++++++++++++++- examples/app-showcase/test/coverage.test.ts | 45 ++++++ examples/app-showcase/test/node-shim.d.ts | 12 ++ 3 files changed, 221 insertions(+), 7 deletions(-) create mode 100644 examples/app-showcase/test/node-shim.d.ts diff --git a/examples/app-showcase/src/coverage.ts b/examples/app-showcase/src/coverage.ts index 4ae5e0b972..27286ebbc5 100644 --- a/examples/app-showcase/src/coverage.ts +++ b/examples/app-showcase/src/coverage.ts @@ -5,14 +5,171 @@ * * This module declares *what the showcase is supposed to cover* and provides * the helpers the coverage test uses to prove it. The test (see - * `test/coverage.test.ts`) introspects the protocol's own Zod enums - * (`FieldTypeSchema`, `ChartTypeSchema`, `ReportType`, `ActionType`, - * `ACTION_LOCATIONS`) and asserts every member appears at least once across - * the registered metadata. Because the expected sets come from the *spec*, - * the test fails automatically when the platform gains a new field type, - * chart type, or report type that the showcase has not yet demonstrated — - * keeping this example a living conformance fixture, not a static snapshot. + * `test/coverage.test.ts`) introspects the protocol's own contracts at TWO + * levels and asserts the showcase keeps up with both: + * + * • Kind level — `DEFAULT_METADATA_TYPE_REGISTRY` (the definitive list of + * metadata kinds). Every kind must be either `demonstrated` (with the + * files that prove it) or explicitly `waived` (with a reason and a + * GitHub issue). A new registry kind fails CI until it is accounted for, + * and a silently-dropped demo fails the file-existence check. No kind + * can go missing without leaving a paper trail (Prime Directive #10: + * never advertise a capability the runtime doesn't deliver — and never + * let a gap hide). + * + * • Variant level — the spec's own Zod enums (`FieldTypeSchema`, + * `ChartTypeSchema`, `ReportType`, `ActionType`, `ACTION_LOCATIONS`). + * Every member must appear at least once across the registered metadata. + * + * Because the expected sets come from the *spec*, the tests fail + * automatically when the platform gains a new kind, field type, chart type, + * or report type that the showcase has not yet demonstrated — keeping this + * example a living conformance fixture, not a static snapshot. + */ + +import type { MetadataType } from '@objectstack/spec/kernel'; + +/** + * Kind-level coverage entry: either the showcase demonstrates the kind (and + * `files` point at the proof, relative to the package root), or it is waived + * with a reason and the GitHub issue that tracks closing the gap. + */ +export type KindCoverage = + | { status: 'demonstrated'; files: string[]; notes?: string } + | { status: 'waived'; reason: string; issue: string }; + +const ISSUE = { + aiDeferred: 'https://github.com/objectstack-ai/framework/issues/2610', + noAuthoringSurface: 'https://github.com/objectstack-ai/framework/issues/2613', +} as const; + +/** + * Every metadata kind in `DEFAULT_METADATA_TYPE_REGISTRY`, accounted for. + * The coverage test enumerates the registry and fails on any kind missing + * here (new platform kind) or any entry the registry no longer knows + * (stale manifest). */ +export const KIND_COVERAGE: Record = { + // ── data ── + object: { + status: 'demonstrated', + files: ['src/data/objects/index.ts', 'src/data/objects/field-zoo.object.ts'], + }, + field: { + status: 'demonstrated', + files: ['src/data/objects/field-zoo.object.ts'], + notes: + 'FieldSchema is authored inline on objects (the stack DSL has no standalone `fields` collection); field-zoo exhausts every field type — see the variant-level test.', + }, + trigger: { + status: 'waived', + reason: + 'No declarative authoring surface: no stack collection or defineTrigger helper. Record-change triggering is demonstrated behaviorally by the record-change flows (requires: ["triggers"]).', + issue: ISSUE.noAuthoringSurface, + }, + validation: { + status: 'demonstrated', + files: ['src/data/objects/account.object.ts', 'src/data/objects/task.object.ts'], + notes: + 'Authored inline via object `validations`. Only the runtime-enforced rule types (state_machine/script/cross_field) are demonstrated; the 6 unenforced types are tracked in https://github.com/objectstack-ai/framework/issues/1475 (Prime Directive #10).', + }, + hook: { status: 'demonstrated', files: ['src/data/hooks/index.ts'] }, + seed: { status: 'demonstrated', files: ['src/data/seed/index.ts'] }, + + // ── ui ── + view: { status: 'demonstrated', files: ['src/ui/views/task.view.ts', 'src/ui/views/project.view.ts'] }, + page: { status: 'demonstrated', files: ['src/ui/pages/index.ts'] }, + dashboard: { status: 'demonstrated', files: ['src/ui/dashboards/chart-gallery.dashboard.ts'] }, + app: { status: 'demonstrated', files: ['src/ui/apps/index.ts'] }, + action: { status: 'demonstrated', files: ['src/ui/actions/index.ts'] }, + report: { status: 'demonstrated', files: ['src/ui/reports/index.ts'] }, + dataset: { status: 'demonstrated', files: ['src/ui/datasets/index.ts'] }, + + // ── automation ── + flow: { status: 'demonstrated', files: ['src/automation/flows/index.ts'] }, + job: { status: 'demonstrated', files: ['src/automation/jobs/index.ts'] }, + + // ── system ── + datasource: { + status: 'demonstrated', + files: ['src/system/datasources/showcase-external.datasource.ts'], + }, + external_catalog: { + status: 'waived', + reason: + 'Runtime-created via Setup → Datasources → Sync (ADR-0062); no declarative artifact an app package can ship. The showcase demos the federation flow that produces one.', + issue: ISSUE.noAuthoringSurface, + }, + translation: { status: 'demonstrated', files: ['src/system/translations/index.ts'] }, + router: { + status: 'waived', + reason: + 'Code-only (allowRuntimeCreate: false). The code-level equivalent is the imperative HTTP mount in src/system/server/recalc-endpoint.ts.', + issue: ISSUE.noAuthoringSurface, + }, + function: { + status: 'waived', + reason: 'Code-only (allowRuntimeCreate: false); no declarative authoring surface.', + issue: ISSUE.noAuthoringSurface, + }, + service: { + status: 'waived', + reason: 'Code-only (allowRuntimeCreate: false); no declarative authoring surface.', + issue: ISSUE.noAuthoringSurface, + }, + email_template: { status: 'demonstrated', files: ['src/system/emails/index.ts'] }, + doc: { status: 'demonstrated', files: ['src/docs/showcase_index.md'] }, + book: { status: 'demonstrated', files: ['src/system/books/index.ts'] }, + + // ── security ── + permission: { status: 'demonstrated', files: ['src/security/index.ts'] }, + profile: { + status: 'demonstrated', + files: ['src/security/index.ts'], + notes: 'MemberDefaultProfile — a permission set with isProfile: true (ADR-0056 D7).', + }, + role: { status: 'demonstrated', files: ['src/security/index.ts'] }, + + // ── ai ── + agent: { + status: 'waived', + reason: + 'Agents are platform-owned — the kernel ships exactly ask/build and third parties never author *.agent.ts (ADR-0063). The in-UI AI runtime is cloud-only; the open framework exposes AI via @objectstack/mcp.', + issue: ISSUE.aiDeferred, + }, + tool: { + status: 'waived', + reason: + 'Deferred with the AI examples iteration — tools are the third-party AI extension primitive (ADR-0063) and belong here once the BYO-AI (MCP) verification story is worked out.', + issue: ISSUE.aiDeferred, + }, + skill: { + status: 'waived', + reason: + 'Deferred with the AI examples iteration — skills are the third-party AI extension primitive (ADR-0063) and belong here once the BYO-AI (MCP) verification story is worked out.', + issue: ISSUE.aiDeferred, + }, +}; + +/** + * Stack collections that are not registry kinds but that the showcase tracks + * for ≥ app-crm parity. Same demonstrated-or-waived contract as + * `KIND_COVERAGE`. + */ +export const STACK_COLLECTION_COVERAGE: Record = { + mappings: { + status: 'waived', + reason: + 'defineMapping artifacts are registered but never consumed — the REST import path only accepts an inline per-request mapping, so a stack-level mapping is inert.', + issue: 'https://github.com/objectstack-ai/framework/issues/2611', + }, + connectors: { + status: 'waived', + reason: + 'Declarative connectors: entries never reach the automation connector registry (plugin registerConnector only). Live connectors are demonstrated the delivered way: ConnectorRestPlugin/ConnectorSlackPlugin in objectstack.config.ts.', + issue: 'https://github.com/objectstack-ai/framework/issues/2612', + }, +}; /** List-view visualisation types (ListViewSchema `type`). */ export const LIST_VIEW_TYPES = [ diff --git a/examples/app-showcase/test/coverage.test.ts b/examples/app-showcase/test/coverage.test.ts index ac58795787..f38d598da2 100644 --- a/examples/app-showcase/test/coverage.test.ts +++ b/examples/app-showcase/test/coverage.test.ts @@ -1,7 +1,10 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. +import { existsSync } from 'node:fs'; + import { describe, it, expect } from 'vitest'; import { FieldType } from '@objectstack/spec/data'; +import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel'; import * as ui from '@objectstack/spec/ui'; import * as objects from '../src/data/objects/index.js'; @@ -10,6 +13,8 @@ import { ChartGalleryDashboard } from '../src/ui/dashboards/index.js'; import { allReports } from '../src/ui/reports/index.js'; import { allActions } from '../src/ui/actions/index.js'; import { + KIND_COVERAGE, + STACK_COLLECTION_COVERAGE, LIST_VIEW_TYPES, FORM_VIEW_TYPES, collectFieldTypes, @@ -17,6 +22,9 @@ import { collectFormViewTypes, } from '../src/coverage.js'; +// vitest runs with cwd = the package root (pnpm --filter executes there). +const PACKAGE_ROOT = process.cwd(); + /** Read the string members of a Zod enum (or a plain array constant). */ function enumValues(schema: unknown): string[] { const s = schema as { options?: string[]; _def?: { values?: string[] } }; @@ -77,6 +85,43 @@ describe('showcase coverage (introspected against the spec)', () => { expectFullCoverage('ReportType', expected, used); }); + describe('metadata kinds (introspected against DEFAULT_METADATA_TYPE_REGISTRY)', () => { + const registryKinds = DEFAULT_METADATA_TYPE_REGISTRY.map((e) => e.type); + const manifests = { KIND_COVERAGE, STACK_COLLECTION_COVERAGE } as const; + + it('accounts for every kind in the registry — demonstrated or waived', () => { + const missing = registryKinds.filter((k) => !(k in KIND_COVERAGE)); + expect(missing, `new registry kinds not yet in KIND_COVERAGE → ${missing.join(', ')}`).toEqual([]); + }); + + it('carries no entry the registry no longer knows', () => { + const stale = Object.keys(KIND_COVERAGE).filter((k) => !registryKinds.includes(k as never)); + expect(stale, `stale KIND_COVERAGE entries → ${stale.join(', ')}`).toEqual([]); + }); + + for (const [manifestName, manifest] of Object.entries(manifests)) { + it(`${manifestName}: demonstrated entries point at files that exist`, () => { + for (const [kind, entry] of Object.entries(manifest)) { + if (entry.status !== 'demonstrated') continue; + expect(entry.files.length, `${kind}: demonstrated but lists no files`).toBeGreaterThan(0); + for (const file of entry.files) { + expect(existsSync(`${PACKAGE_ROOT}/${file}`), `${kind}: missing proof file ${file}`).toBe(true); + } + } + }); + + it(`${manifestName}: waived entries carry a reason and a GitHub issue link`, () => { + for (const [kind, entry] of Object.entries(manifest)) { + if (entry.status !== 'waived') continue; + expect(entry.reason.length, `${kind}: waiver without a substantive reason`).toBeGreaterThan(20); + expect(entry.issue, `${kind}: waiver must link the tracking issue`).toMatch( + /^https:\/\/github\.com\/[\w.-]+\/[\w.-]+\/(issues|pull)\/\d+$/, + ); + } + }); + } + }); + it('covers every action type and location', () => { const types = enumValues((ui as Record).ActionType ?? (ui as Record).ActionTypeSchema); const locations = enumValues((ui as Record).ACTION_LOCATIONS ?? (ui as Record).ActionLocationSchema); diff --git a/examples/app-showcase/test/node-shim.d.ts b/examples/app-showcase/test/node-shim.d.ts new file mode 100644 index 0000000000..dc22e7b3ed --- /dev/null +++ b/examples/app-showcase/test/node-shim.d.ts @@ -0,0 +1,12 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. + +// Minimal ambient surface for the node builtins the tests touch. The +// showcase tsconfig deliberately omits `@types/node` (see the ambient +// `process` note in objectstack.config.ts); vitest provides the real +// implementations at runtime. + +declare module 'node:fs' { + export function existsSync(path: string): boolean; +} + +declare const process: { cwd(): string }; From 25f7035d777ec143eda594baaf25545a30ecba55 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Jul 2026 02:52:42 +0000 Subject: [PATCH 3/6] feat(showcase): demonstrate defineCube and defineObjectExtension on the backbone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Close the two ≥app-crm parity gaps the coverage manifest can prove today: - src/data/analytics/showcase.cube.ts — showcase_delivery cube over showcase_task (count / estimate-hours aggregates / done-rate; status, priority, assignee, due-date time dimension; many_to_one join to showcase_project), wired via analyticsCubes and served by the foundational analytics capability at /api/v1/analytics/*. - src/data/extensions/account.extension.ts — additive overlay merged into showcase_account (loyalty_tier / linkedin_url / csat_score, priority 210), wired via objectExtensions. test/gap-fill.test.ts proves both: the stack wiring, the cube shape, and the extension merge against the REAL SchemaRegistry (the same merge registerApp performs at boot). STACK_COLLECTION_COVERAGE flips analyticsCubes/objectExtensions to demonstrated. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01G6pPpRszk9cD3SxcKNFMWs --- examples/app-showcase/objectstack.config.ts | 9 ++ examples/app-showcase/src/coverage.ts | 11 +++ .../src/data/analytics/showcase.cube.ts | 88 +++++++++++++++++++ .../src/data/extensions/account.extension.ts | 44 ++++++++++ examples/app-showcase/test/gap-fill.test.ts | 68 ++++++++++++++ 5 files changed, 220 insertions(+) create mode 100644 examples/app-showcase/src/data/analytics/showcase.cube.ts create mode 100644 examples/app-showcase/src/data/extensions/account.extension.ts create mode 100644 examples/app-showcase/test/gap-fill.test.ts diff --git a/examples/app-showcase/objectstack.config.ts b/examples/app-showcase/objectstack.config.ts index 4d570fbdf7..5a29f36741 100644 --- a/examples/app-showcase/objectstack.config.ts +++ b/examples/app-showcase/objectstack.config.ts @@ -38,6 +38,8 @@ import { allThemes } from './src/ui/themes/index.js'; import { ShowcaseTranslationBundle } from './src/system/translations/index.js'; import { allPortals } from './src/ui/portals/index.js'; import { ShowcaseSeedData } from './src/data/seed/index.js'; +import { allCubes } from './src/data/analytics/showcase.cube.js'; +import { allObjectExtensions } from './src/data/extensions/account.extension.js'; // Ambient `process` for the env-var overrides below — the showcase tsconfig // doesn't pull in `@types/node`, but the CLI provides the real `process` at @@ -149,6 +151,13 @@ export default defineStack({ // Data objects: [...Object.values(objects), ExternalCustomer, ExternalOrder], + // Additive overlay merged into showcase_account at registration — the + // package-extends-an-object mechanism (see src/data/extensions/). + objectExtensions: allObjectExtensions, + // Analytics semantic layer served by the foundational analytics capability + // (`/api/v1/analytics/*`) — no `requires` token needed; the CLI always + // loads it and registers these cubes (see src/data/analytics/). + analyticsCubes: allCubes, // UI apps: [ShowcaseApp], diff --git a/examples/app-showcase/src/coverage.ts b/examples/app-showcase/src/coverage.ts index 27286ebbc5..de9193b1aa 100644 --- a/examples/app-showcase/src/coverage.ts +++ b/examples/app-showcase/src/coverage.ts @@ -157,6 +157,17 @@ export const KIND_COVERAGE: Record = { * `KIND_COVERAGE`. */ export const STACK_COLLECTION_COVERAGE: Record = { + analyticsCubes: { + status: 'demonstrated', + files: ['src/data/analytics/showcase.cube.ts'], + notes: + 'Served by the foundational analytics capability (/api/v1/analytics/*); complements the dataset semantic layer (ADR-0021).', + }, + objectExtensions: { + status: 'demonstrated', + files: ['src/data/extensions/account.extension.ts'], + notes: 'Merged into showcase_account by the ObjectQL engine at registerApp (priority overlay).', + }, mappings: { status: 'waived', reason: diff --git a/examples/app-showcase/src/data/analytics/showcase.cube.ts b/examples/app-showcase/src/data/analytics/showcase.cube.ts new file mode 100644 index 0000000000..bb228a32ee --- /dev/null +++ b/examples/app-showcase/src/data/analytics/showcase.cube.ts @@ -0,0 +1,88 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. + +import { defineCube } from '@objectstack/spec/data'; + +/** + * Delivery cube — the analytics semantic layer (`defineCube`) over the + * project-delivery backbone. Sits alongside the `dataset` demos + * (src/ui/datasets/) to show BOTH analytics surfaces: datasets feed + * reports/dashboards (ADR-0021), cubes feed the analytics service + * (`/api/v1/analytics/*` — the CLI auto-loads the foundational analytics + * capability and registers `analyticsCubes` with it). + * + * Base table = `showcase_task` (object name IS the table name, Prime + * Directive #6); the join reaches the parent project through the + * master-detail column. + */ +export const DeliveryCube = defineCube({ + name: 'showcase_delivery', + title: 'Delivery Analytics', + description: 'Task throughput and effort analytics across the delivery backbone.', + sql: 'showcase_task', + measures: { + count: { + name: 'count', + label: 'Task Count', + type: 'count', + sql: '*', + }, + total_estimate_hours: { + name: 'total_estimate_hours', + label: 'Total Estimated Hours', + type: 'sum', + sql: 'estimate_hours', + }, + avg_estimate_hours: { + name: 'avg_estimate_hours', + label: 'Average Estimate (h)', + type: 'avg', + sql: 'estimate_hours', + }, + done_rate: { + name: 'done_rate', + label: 'Done Rate (%)', + type: 'number', + sql: "SUM(CASE WHEN status = 'done' THEN 1 ELSE 0 END) * 100.0 / COUNT(*)", + format: 'percent', + }, + }, + dimensions: { + status: { + name: 'status', + label: 'Status', + type: 'string', + sql: 'status', + }, + priority: { + name: 'priority', + label: 'Priority', + type: 'string', + sql: 'priority', + }, + due_date: { + name: 'due_date', + label: 'Due Date', + type: 'time', + sql: 'due_date', + }, + assignee: { + name: 'assignee', + label: 'Assignee', + type: 'string', + sql: 'assignee', + }, + }, + joins: { + showcase_project: { + name: 'showcase_project', + relationship: 'many_to_one', + sql: '${showcase_delivery}.project = ${showcase_project}.id', + }, + }, + refreshKey: { + every: '1 hour', + }, + public: false, +}); + +export const allCubes = [DeliveryCube]; diff --git a/examples/app-showcase/src/data/extensions/account.extension.ts b/examples/app-showcase/src/data/extensions/account.extension.ts new file mode 100644 index 0000000000..cfb70e0b9c --- /dev/null +++ b/examples/app-showcase/src/data/extensions/account.extension.ts @@ -0,0 +1,44 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. + +import { defineObjectExtension } from '@objectstack/spec/data'; + +/** + * Object extension (overlay) demo — additive fields merged into + * `showcase_account` at registration time WITHOUT re-declaring the object. + * The ObjectQL engine merges extension fields into the target during + * `registerApp` (higher `priority` wins on conflict), so these fields show + * up on the Account form/list exactly as if they were authored inline — + * the mechanism packages use to extend objects they don't own. + */ +export const AccountExtension = defineObjectExtension({ + extend: 'showcase_account', + label: 'Account (Success Overlay)', + fields: { + loyalty_tier: { + name: 'loyalty_tier', + label: 'Loyalty Tier', + type: 'select', + options: [ + { value: 'bronze', label: 'Bronze' }, + { value: 'silver', label: 'Silver' }, + { value: 'gold', label: 'Gold' }, + { value: 'platinum', label: 'Platinum' }, + ], + }, + linkedin_url: { + name: 'linkedin_url', + label: 'LinkedIn URL', + type: 'url', + }, + csat_score: { + name: 'csat_score', + label: 'CSAT Score', + type: 'number', + min: 0, + max: 100, + }, + }, + priority: 210, +}); + +export const allObjectExtensions = [AccountExtension]; diff --git a/examples/app-showcase/test/gap-fill.test.ts b/examples/app-showcase/test/gap-fill.test.ts new file mode 100644 index 0000000000..9b165d31ca --- /dev/null +++ b/examples/app-showcase/test/gap-fill.test.ts @@ -0,0 +1,68 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. + +import { describe, it, expect } from 'vitest'; +import { SchemaRegistry } from '@objectstack/objectql'; + +import stack from '../objectstack.config.js'; +import { DeliveryCube } from '../src/data/analytics/showcase.cube.js'; +import { AccountExtension } from '../src/data/extensions/account.extension.js'; +import { Account } from '../src/data/objects/account.object.js'; + +/** + * Proof for the two STACK_COLLECTION_COVERAGE entries marked `demonstrated` + * (see src/coverage.ts): the cube and the object extension are not just + * declared — they are wired into the stack, and the extension merge is + * exercised against the REAL SchemaRegistry (the same merge `registerApp` + * step 2b performs at boot). + */ +describe('showcase gap fill — analytics cube', () => { + it('is wired into the stack definition', () => { + const cubes = (stack as { analyticsCubes?: Array<{ name: string }> }).analyticsCubes ?? []; + expect(cubes.map((c) => c.name)).toContain('showcase_delivery'); + }); + + it('declares measures and dimensions over the delivery backbone', () => { + expect(DeliveryCube.sql).toBe('showcase_task'); + expect(Object.keys(DeliveryCube.measures ?? {})).toEqual( + expect.arrayContaining(['count', 'total_estimate_hours', 'avg_estimate_hours', 'done_rate']), + ); + expect(Object.keys(DeliveryCube.dimensions ?? {})).toEqual( + expect.arrayContaining(['status', 'priority', 'due_date']), + ); + expect(DeliveryCube.joins?.showcase_project?.relationship).toBe('many_to_one'); + }); +}); + +describe('showcase gap fill — object extension (overlay merge)', () => { + it('is wired into the stack definition', () => { + const exts = (stack as { objectExtensions?: Array<{ extend: string }> }).objectExtensions ?? []; + expect(exts.map((e) => e.extend)).toContain('showcase_account'); + }); + + it('merges its fields into showcase_account via the real SchemaRegistry', () => { + const registry = new SchemaRegistry(); + registry.registerObject(Account as never, 'com.example.showcase', undefined, 'own'); + registry.registerObject( + { + name: AccountExtension.extend, + label: AccountExtension.label, + fields: AccountExtension.fields, + } as never, + 'com.example.showcase.overlay', + undefined, + 'extend', + AccountExtension.priority, + ); + + const merged = registry.getObject('showcase_account') as { + fields?: Record; + }; + expect(merged).toBeDefined(); + // Extension fields landed… + expect(merged.fields?.loyalty_tier?.type).toBe('select'); + expect(merged.fields?.linkedin_url?.type).toBe('url'); + expect(merged.fields?.csat_score?.type).toBe('number'); + // …without clobbering the owner's fields. + expect(merged.fields?.annual_revenue).toBeDefined(); + }); +}); From aa8f8dc47c8a51c6838c6569ccf4e006c41a1fbc Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Jul 2026 02:58:32 +0000 Subject: [PATCH 4/6] feat(showcase): capability-map landing + per-domain guided tour MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Give the showcase a front door that indexes every demo: - Five tour docs (src/docs/showcase_tour_{data,ui,automation,system, security}.md) — one per protocol domain, mirroring the src/ layout, with live ```metadata embeds (ADR-0051: state machine, flow at business detail, permission matrix) and honest pointers to the coverage waivers. Docs-as-metadata demoing itself. - ShowcaseBook gains a 'Guided Tour' group (explicit page list to fix the domain order). - showcase_capability_map — an html-kind SDUI home page with one card per domain linking flagship demos + the domain tour; the AI card is explicitly 'deferred' (ADR-0063, #2610), never a fake demo. Inserted as the FIRST navigation item (the delivered landing convention — homePageId has no console consumer). Start Here stays as the page- authoring teaching index, relabeled 'Page Authoring'. - e2e smoke sweeps the new landing; doc/book coverage entries point at the tour files. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01G6pPpRszk9cD3SxcKNFMWs --- .../app-showcase/e2e/showcase-smoke.spec.ts | 1 + examples/app-showcase/objectstack.config.ts | 4 +- examples/app-showcase/src/coverage.ts | 12 ++- .../app-showcase/src/docs/showcase_index.md | 21 +++++ .../src/docs/showcase_tour_automation.md | 44 +++++++++ .../src/docs/showcase_tour_data.md | 56 ++++++++++++ .../src/docs/showcase_tour_security.md | 38 ++++++++ .../src/docs/showcase_tour_system.md | 44 +++++++++ .../app-showcase/src/docs/showcase_tour_ui.md | 50 +++++++++++ .../app-showcase/src/system/books/index.ts | 15 +++- examples/app-showcase/src/ui/apps/index.ts | 9 +- .../src/ui/pages/capability-map.page.ts | 90 +++++++++++++++++++ examples/app-showcase/src/ui/pages/index.ts | 1 + .../src/ui/pages/start-here.page.ts | 2 +- 14 files changed, 379 insertions(+), 8 deletions(-) create mode 100644 examples/app-showcase/src/docs/showcase_tour_automation.md create mode 100644 examples/app-showcase/src/docs/showcase_tour_data.md create mode 100644 examples/app-showcase/src/docs/showcase_tour_security.md create mode 100644 examples/app-showcase/src/docs/showcase_tour_system.md create mode 100644 examples/app-showcase/src/docs/showcase_tour_ui.md create mode 100644 examples/app-showcase/src/ui/pages/capability-map.page.ts diff --git a/examples/app-showcase/e2e/showcase-smoke.spec.ts b/examples/app-showcase/e2e/showcase-smoke.spec.ts index 837853a7d5..92f2acd25f 100644 --- a/examples/app-showcase/e2e/showcase-smoke.spec.ts +++ b/examples/app-showcase/e2e/showcase-smoke.spec.ts @@ -10,6 +10,7 @@ const APP = process.env.SHOWCASE_APP || 'com.example.showcase'; const base = (seg: string) => `/_console/apps/${APP}/${seg}`; const SURFACES: { name: string; path: string; chart?: boolean }[] = [ + { name: 'Capability Map', path: base('page/showcase_capability_map') }, { name: 'My Work', path: base('page/showcase_my_work') }, { name: 'Approvals', path: base('page/showcase_review_queue') }, { name: 'New Project Wizard', path: base('page/showcase_new_project_wizard') }, diff --git a/examples/app-showcase/objectstack.config.ts b/examples/app-showcase/objectstack.config.ts index 5a29f36741..ef1a395775 100644 --- a/examples/app-showcase/objectstack.config.ts +++ b/examples/app-showcase/objectstack.config.ts @@ -22,7 +22,7 @@ import { ChartGalleryDashboard, OpsDashboard } from './src/ui/dashboards/index.j import { ShowcaseTaskDataset, ShowcaseProjectDataset } from './src/ui/datasets/index.js'; import { allReports } from './src/ui/reports/index.js'; import { allActions } from './src/ui/actions/index.js'; -import { StartHerePage, ComponentGalleryPage, ProjectWorkspacePage, ProjectDetailPage, TaskWorkbenchPage, TaskTriagePage, TaskBoardPage, TaskCalendarPage, TaskGalleryPage, TaskSchedulePage, TaskTimelinePage, TaskMapPage, TaskAllViewsPage, ActiveProjectsPage, TaskDetailPage, ReviewQueuePage, NewProjectWizardPage, MyWorkPage, SettingsPage, StylingGalleryPage, CommandCenterPage, CommandCenterJsxPage, CrmWorkbenchPage, AccountCockpitPage, TaskDeskPage, PageVariablesPage, ContactFormPage, RenewalsPipelinePage } from './src/ui/pages/index.js'; +import { CapabilityMapPage, StartHerePage, ComponentGalleryPage, ProjectWorkspacePage, ProjectDetailPage, TaskWorkbenchPage, TaskTriagePage, TaskBoardPage, TaskCalendarPage, TaskGalleryPage, TaskSchedulePage, TaskTimelinePage, TaskMapPage, TaskAllViewsPage, ActiveProjectsPage, TaskDetailPage, ReviewQueuePage, NewProjectWizardPage, MyWorkPage, SettingsPage, StylingGalleryPage, CommandCenterPage, CommandCenterJsxPage, CrmWorkbenchPage, AccountCockpitPage, TaskDeskPage, PageVariablesPage, ContactFormPage, RenewalsPipelinePage } from './src/ui/pages/index.js'; import { allFlows } from './src/automation/flows/index.js'; import { allWebhooks } from './src/automation/webhooks/index.js'; import { allHooks } from './src/data/hooks/index.js'; @@ -163,7 +163,7 @@ export default defineStack({ apps: [ShowcaseApp], portals: allPortals, views: [TaskViews, ProjectViews, InquiryViews, BusinessUnitViews], - pages: [StartHerePage, ComponentGalleryPage, ProjectWorkspacePage, ProjectDetailPage, TaskWorkbenchPage, TaskTriagePage, TaskBoardPage, TaskCalendarPage, TaskGalleryPage, TaskSchedulePage, TaskTimelinePage, TaskMapPage, TaskAllViewsPage, ActiveProjectsPage, TaskDetailPage, ReviewQueuePage, NewProjectWizardPage, MyWorkPage, SettingsPage, StylingGalleryPage, CommandCenterPage, CommandCenterJsxPage, CrmWorkbenchPage, AccountCockpitPage, TaskDeskPage, PageVariablesPage, ContactFormPage, RenewalsPipelinePage], + pages: [CapabilityMapPage, StartHerePage, ComponentGalleryPage, ProjectWorkspacePage, ProjectDetailPage, TaskWorkbenchPage, TaskTriagePage, TaskBoardPage, TaskCalendarPage, TaskGalleryPage, TaskSchedulePage, TaskTimelinePage, TaskMapPage, TaskAllViewsPage, ActiveProjectsPage, TaskDetailPage, ReviewQueuePage, NewProjectWizardPage, MyWorkPage, SettingsPage, StylingGalleryPage, CommandCenterPage, CommandCenterJsxPage, CrmWorkbenchPage, AccountCockpitPage, TaskDeskPage, PageVariablesPage, ContactFormPage, RenewalsPipelinePage], dashboards: [ChartGalleryDashboard, OpsDashboard], books: allBooks, datasets: [ShowcaseTaskDataset, ShowcaseProjectDataset], diff --git a/examples/app-showcase/src/coverage.ts b/examples/app-showcase/src/coverage.ts index de9193b1aa..d8efbd733b 100644 --- a/examples/app-showcase/src/coverage.ts +++ b/examples/app-showcase/src/coverage.ts @@ -118,8 +118,16 @@ export const KIND_COVERAGE: Record = { issue: ISSUE.noAuthoringSurface, }, email_template: { status: 'demonstrated', files: ['src/system/emails/index.ts'] }, - doc: { status: 'demonstrated', files: ['src/docs/showcase_index.md'] }, - book: { status: 'demonstrated', files: ['src/system/books/index.ts'] }, + doc: { + status: 'demonstrated', + files: ['src/docs/showcase_index.md', 'src/docs/showcase_tour_data.md'], + notes: 'Includes the five per-domain guided-tour docs (showcase_tour_*) with live metadata embeds (ADR-0051).', + }, + book: { + status: 'demonstrated', + files: ['src/system/books/index.ts'], + notes: 'ShowcaseBook curates a Guided Tour group in fixed domain order.', + }, // ── security ── permission: { status: 'demonstrated', files: ['src/security/index.ts'] }, diff --git a/examples/app-showcase/src/docs/showcase_index.md b/examples/app-showcase/src/docs/showcase_index.md index 91fde6fae5..1b1e798bf8 100644 --- a/examples/app-showcase/src/docs/showcase_index.md +++ b/examples/app-showcase/src/docs/showcase_index.md @@ -19,3 +19,24 @@ inside the package artifact, and renders in the console at For the authoring rules this page must itself obey, see the [documentation guide](./showcase_docs_guide.md) — or jump straight to its [cross-reference section](./showcase_docs_guide.md#cross-references). + +## Guided tour + +One walkthrough per protocol domain, mirroring the `src/` layout: + +- [Data](./showcase_tour_data.md) — objects, fields, validations, hooks, + seed, extensions, the analytics cube +- [UI](./showcase_tour_ui.md) — apps, views, pages, dashboards, reports, + datasets, actions, themes, portals +- [Automation](./showcase_tour_automation.md) — flows & approvals, jobs, + webhooks, connectors +- [System](./showcase_tour_system.md) — datasources & federation, i18n, + email, docs-as-metadata, custom endpoints +- [Security](./showcase_tour_security.md) — roles, permission sets, + profile, sharing, row-level security + +**AI (agent / tool / skill)** is the sixth protocol domain and is +deliberately absent here: agents are platform-owned (ADR-0063) and the +open framework exposes AI via MCP only. The coverage manifest records the +waiver — see +[framework#2610](https://github.com/objectstack-ai/framework/issues/2610). diff --git a/examples/app-showcase/src/docs/showcase_tour_automation.md b/examples/app-showcase/src/docs/showcase_tour_automation.md new file mode 100644 index 0000000000..f763f4fdb1 --- /dev/null +++ b/examples/app-showcase/src/docs/showcase_tour_automation.md @@ -0,0 +1,44 @@ +--- +title: "Tour · Automation" +description: Guided tour of the automation domain — flows (with approvals), scheduled jobs, webhooks, and connector actions. +--- + +# Guided tour — Automation + +Everything in this domain lives under `src/automation/`. + +## Flows + +`src/automation/flows/` is the largest module in the showcase: record-change +flows, a screen wizard, an approval chain (approvals are **nodes inside a +flow**, ADR-0019 — there is no separate approval metadata type), scheduled +flows, and connector actions. + +The reassignment wizard, projected at the business altitude (technical +nodes folded away): + +```metadata +type: flow +name: showcase_reassign_wizard +detail: business +``` + +Trigger the automation yourself: complete a Task (Mark Done) and watch the +`showcase_task_completed` flow fire; submit a Project budget over the +threshold and the `showcase_budget_approval` chain lands in **Workspace → +Approvals**. + +## Jobs, webhooks, connectors + +- `src/automation/jobs/` — interval/cron jobs behind the schedule trigger + (the `job` capability token wires the timing backend). +- `src/automation/webhooks/` — inbound webhook endpoints. +- Connector flows (`showcase_task_completed_rest_ping`, + `showcase_task_completed_slack`) dispatch through **live connectors** + registered by `ConnectorRestPlugin` / `ConnectorSlackPlugin` in + `objectstack.config.ts` — the delivered way to wire connectors (a purely + declarative `connectors:` stack entry is inert today; the coverage + manifest waives it with the tracking issue). + +Continue with the [System tour](./showcase_tour_system.md), or go back to +the [overview](./showcase_index.md). diff --git a/examples/app-showcase/src/docs/showcase_tour_data.md b/examples/app-showcase/src/docs/showcase_tour_data.md new file mode 100644 index 0000000000..5161b4ec6c --- /dev/null +++ b/examples/app-showcase/src/docs/showcase_tour_data.md @@ -0,0 +1,56 @@ +--- +title: "Tour · Data" +description: Guided tour of the data domain — objects, fields, relationships, validations, hooks, seed data, extensions, and the analytics cube. +--- + +# Guided tour — Data + +Everything in this domain lives under `src/data/` and follows the dual-track +design: a **realistic backbone** (Account → Project → Task, plus Team, +Category, Invoice…) so every screen renders real seeded data, and **specimen +objects** (`field-zoo`, `semantic-zoo`) that exhaust protocol variants. + +## Objects & fields + +- **Field Zoo** (navigation → Data Model → Field Zoo) declares every field + type the protocol knows — the coverage test introspects `FieldTypeSchema` + and fails if a new type appears without a specimen here. +- The backbone shows the relationship kinds in context: `lookup` + (Project → Account), `master_detail` (Task → Project), a self-referencing + tree (Category → parent), and a many-to-many junction + (`showcase_project_membership`). + +## Validations — enforced, and only enforced, rules + +Rules are authored inline on the object. This tour only demonstrates the +rule types the runtime actually enforces on the write path +(`state_machine`, `script`/`cross_field`, `format`, `json_schema`, +`conditional` — see `src/data/objects/account.object.ts` and the write-path +test in `test/validation.test.ts`). + +A Task's legal status moves are governed by a `state_machine` rule — +rendered live from the metadata, not a screenshot: + +```metadata +type: state_machine +object: showcase_task +name: task_status_flow +``` + +## Hooks & seed data + +- `src/data/hooks/` — data-layer lifecycle hooks (before/after CRUD). +- `src/data/seed/` — the seed dataset that makes every view render something + real on first boot. + +## Extensions & analytics + +- `src/data/extensions/account.extension.ts` — an **object extension** + merged additively into `showcase_account` at registration (the mechanism a + package uses to extend an object it doesn't own). Open an Account form: + Loyalty Tier / LinkedIn URL / CSAT Score come from the overlay. +- `src/data/analytics/showcase.cube.ts` — the `showcase_delivery` **cube**, + served by the analytics service at `/api/v1/analytics/*`. + +Continue with the [UI tour](./showcase_tour_ui.md), or go back to the +[overview](./showcase_index.md). diff --git a/examples/app-showcase/src/docs/showcase_tour_security.md b/examples/app-showcase/src/docs/showcase_tour_security.md new file mode 100644 index 0000000000..e6a1e4036c --- /dev/null +++ b/examples/app-showcase/src/docs/showcase_tour_security.md @@ -0,0 +1,38 @@ +--- +title: "Tour · Security" +description: Guided tour of the security domain — roles, permission sets, the default profile, sharing rules, and row-level security. +--- + +# Guided tour — Security + +Everything in this domain lives under `src/security/index.ts`. + +## Roles, permission sets, profile + +The showcase ships a role hierarchy, permission sets with object CRUD + +field-level security + row-level security, and `showcase_member_default` — +a permission set with `isProfile: true`, the fallback **profile** +(ADR-0056). + +The `showcase_contributor` permission set as a live object-access matrix: + +```metadata +type: permission +name: showcase_contributor +``` + +## Sharing + +Sharing rules extend record access beyond ownership — see the +`sharingRules` wired in `objectstack.config.ts` and the private-note +object for an owner-only counter-example. + +## See it enforced + +Log in as a non-admin member (create one in Setup → Users, assign the +member profile): the Field Zoo's permission-gated fields mask, private +notes vanish, and write attempts outside your row scope are rejected — +the declared model, enforced end to end. + +This is the last stop — back to the [overview](./showcase_index.md), or +jump to the [Data tour](./showcase_tour_data.md) to start again. diff --git a/examples/app-showcase/src/docs/showcase_tour_system.md b/examples/app-showcase/src/docs/showcase_tour_system.md new file mode 100644 index 0000000000..aa212e1e14 --- /dev/null +++ b/examples/app-showcase/src/docs/showcase_tour_system.md @@ -0,0 +1,44 @@ +--- +title: "Tour · System" +description: Guided tour of the system domain — datasources & federation, i18n, email templates, docs-as-metadata, and the custom endpoint. +--- + +# Guided tour — System + +Everything in this domain lives under `src/system/` (plus the pinned flat +`src/docs/` directory this page is part of). + +## Datasources & federation + +`src/system/datasources/` declares a second, read-only SQLite file as an +**external datasource** (ADR-0015 / ADR-0062). It auto-connects at boot with +no driver wiring; its federated objects (External Customer / External +Order) are queryable via REST and visible in **Setup → Datasources**. The +`external_catalog` snapshot is produced at runtime by the Sync wizard — +there is no declarative artifact, so the coverage manifest waives that kind +with the tracking issue. + +## i18n & email + +- `src/system/translations/` — the `en` + `zh-CN` bundles; switch locale in + the console and the navigation follows. +- `src/system/emails/` — outbound email templates used by the automation + chain. + +## Docs & books — this manual is metadata + +Every Markdown file in flat `src/docs/` compiles to a `doc` item +(ADR-0046); `src/system/books/` curates them into the spine you are +reading, exposed publicly through the library portal. The +[documentation guide](./showcase_docs_guide.md) states the authoring rules +this very page obeys. + +## Code-only surfaces + +`src/system/server/recalc-endpoint.ts` mounts a custom REST endpoint +imperatively — the code-level counterpart of the `router` / `function` / +`service` kinds, which have no declarative authoring surface today (waived +in the coverage manifest with the tracking issue). + +Continue with the [Security tour](./showcase_tour_security.md), or go back +to the [overview](./showcase_index.md). diff --git a/examples/app-showcase/src/docs/showcase_tour_ui.md b/examples/app-showcase/src/docs/showcase_tour_ui.md new file mode 100644 index 0000000000..febf10fbe6 --- /dev/null +++ b/examples/app-showcase/src/docs/showcase_tour_ui.md @@ -0,0 +1,50 @@ +--- +title: "Tour · UI" +description: Guided tour of the UI domain — apps, views, pages, dashboards, reports, datasets, actions, themes, and portals. +--- + +# Guided tour — UI + +Everything in this domain lives under `src/ui/`. + +## App & navigation + +`src/ui/apps/index.ts` is the application shell — the navigation you are +clicking through, grouped to teach: Workspace / Data Model / Analytics work +like a real product; the **Authoring · \*** groups are the page-authoring +gallery. + +## Views — every visualization, every form layout + +- **All Views** (navigation → Authoring · Visualizations → All Views) shows + the same Task object through every list-view type: grid, kanban, gallery, + calendar, timeline, gantt, map, chart. +- `src/ui/views/task.view.ts` also declares every form-view layout: + simple, tabbed, wizard, split, drawer. + +## Pages — four authoring models + +**Start Here** (navigation, second item) teaches the decision: structured +regions (full/slotted) → constrained-JSX `html` → real `react`, with the +canonical example of each linked from that page. + +## Dashboards, reports, datasets, actions + +- **Chart Gallery** — one widget per chart family; the coverage test + introspects `ChartTypeSchema` so a new chart type fails CI until it + appears here. +- Reports (summary / matrix / joined) live in the Analytics group; + a flat "tabular report" is deliberately a ListView lens (ADR-0021). +- `src/ui/datasets/` — the dataset semantic layer feeding reports and + dashboards; the cube in the data domain covers the service-side analytics + surface. +- `src/ui/actions/` — the ActionType × location matrix (script / url / + modal / flow / api / form), visible as buttons across Task screens. + +## Themes & portals + +`src/ui/themes/` ships light + dark; `src/ui/portals/` exposes the public +library portal that serves this very manual. + +Continue with the [Automation tour](./showcase_tour_automation.md), or go +back to the [overview](./showcase_index.md). diff --git a/examples/app-showcase/src/system/books/index.ts b/examples/app-showcase/src/system/books/index.ts index 336821f98a..3835fa5bf7 100644 --- a/examples/app-showcase/src/system/books/index.ts +++ b/examples/app-showcase/src/system/books/index.ts @@ -24,10 +24,23 @@ export const ShowcaseBook = defineBook({ // Explicit override: pin the index first, then sweep any other intro docs. pages: ['showcase_index', '...'], }, + { + key: 'tour', + label: 'Guided Tour', + order: 2, + // Explicit list: fixes the domain order (include-globs sort by name). + pages: [ + 'showcase_tour_data', + 'showcase_tour_ui', + 'showcase_tour_automation', + 'showcase_tour_system', + 'showcase_tour_security', + ], + }, { key: 'guides', label: 'Guides', - order: 2, + order: 3, include: 'showcase_*_guide', // derived: every guide doc, present and future }, ], diff --git a/examples/app-showcase/src/ui/apps/index.ts b/examples/app-showcase/src/ui/apps/index.ts index 51cb4c11b4..8c8572c230 100644 --- a/examples/app-showcase/src/ui/apps/index.ts +++ b/examples/app-showcase/src/ui/apps/index.ts @@ -5,7 +5,9 @@ import { App } from '@objectstack/spec/ui'; /** * Showcase app — navigation organized to TEACH page authoring. * - * "Start Here" is the default landing: it explains the two axes every page has — + * "Capability Map" is the default landing (first nav item): one card per + * protocol domain, indexing every demo. "Page Authoring" (Start Here) is the + * page-authoring teaching index: it explains the two axes every page has — * `type` (the surface role) and `kind` (the authoring model) — and links to the * canonical example of each kind. The first three groups (Workspace / Data Model * / Analytics) are the app *working* like a real product. The "Authoring · *" @@ -23,7 +25,10 @@ export const ShowcaseApp = App.create({ branding: { primaryColor: '#7C3AED' }, navigation: [ - { id: 'nav_start_here', type: 'page', pageName: 'showcase_start_here', label: 'Start Here', icon: 'compass' }, + // First item = the app's landing surface (the delivered convention; the + // spec's homePageId has no console consumer yet). + { id: 'nav_capability_map', type: 'page', pageName: 'showcase_capability_map', label: 'Capability Map', icon: 'map' }, + { id: 'nav_start_here', type: 'page', pageName: 'showcase_start_here', label: 'Page Authoring', icon: 'compass' }, { id: 'grp_workspace', type: 'group', diff --git a/examples/app-showcase/src/ui/pages/capability-map.page.ts b/examples/app-showcase/src/ui/pages/capability-map.page.ts new file mode 100644 index 0000000000..ecfc553cb1 --- /dev/null +++ b/examples/app-showcase/src/ui/pages/capability-map.page.ts @@ -0,0 +1,90 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. + +import { definePage } from '@objectstack/spec/ui'; + +/** + * Capability Map — the showcase's landing page and index. + * + * One card per protocol domain (the same six-domain taxonomy as + * `DEFAULT_METADATA_TYPE_REGISTRY` and the `src/` layout), each linking the + * domain's flagship demos plus its guided-tour doc. Self-referential by + * design: the map of the platform's capabilities is itself built from them + * (`kind: 'html'` constrained JSX — parsed, never executed, ADR-0080; theme + * tokens per ADR-0065). + * + * The AI card is deliberately a "deferred" card, not a demo: agents are + * platform-owned (ADR-0063) and the open framework exposes AI via MCP only — + * the coverage manifest waives agent/tool/skill with issue #2610. Never + * advertise what the runtime doesn't deliver (Prime Directive #10). + */ + +const card = ( + eyebrow: string, + title: string, + body: string, + links: Array<[href: string, label: string]>, +) => ` + +
${eyebrow}
+
${title}
+
${body}
+ +${links + .map( + ([href, label]) => + ` → ${label}`, + ) + .join('\n')} + +
`; + +export const CapabilityMapPage = definePage({ + name: 'showcase_capability_map', + label: 'Capability Map', + type: 'home', + kind: 'html', + isDefault: true, + source: ` + + + +
ObjectStack Showcase
+
The capability map
+
Every metadata capability the platform delivers, demonstrated once and indexed here — one card per protocol domain, mirroring the registry and the src/ layout. The coverage test keeps this map honest: a new capability fails CI until it is demonstrated or explicitly waived.
+
+ +
+${card('data', 'Objects, fields & rules', 'The Account → Project → Task backbone plus the Field Zoo specimen (every field type), relationships, enforced validation rules, hooks, seed data, an object-extension overlay, and the analytics cube.', [ + ['apps/com.example.showcase/showcase_field_zoo', 'Field Zoo'], + ['apps/com.example.showcase/showcase_project', 'Projects (backbone)'], + ['apps/com.example.showcase/showcase_account', 'Accounts (extension overlay)'], + ['docs/showcase_tour_data', 'Read the Data tour'], +])} +${card('ui', 'Views, pages & analytics surfaces', 'Every list-view and form-view type on one object, four page-authoring models, one widget per chart family, summary/matrix/joined reports, datasets, and the action matrix.', [ + ['apps/com.example.showcase/page/showcase_task_all_views', 'All Views'], + ['apps/com.example.showcase/dashboard/showcase_chart_gallery', 'Chart Gallery'], + ['apps/com.example.showcase/page/showcase_start_here', 'Page authoring (Start Here)'], + ['docs/showcase_tour_ui', 'Read the UI tour'], +])} +${card('automation', 'Flows, approvals & schedules', 'Record-change flows, a screen wizard, an approval chain with escalation, scheduled jobs, webhooks, and live REST/Slack connector actions.', [ + ['apps/com.example.showcase/page/showcase_review_queue', 'Approvals inbox'], + ['apps/com.example.showcase/page/showcase_command_center', 'Command Center'], + ['docs/showcase_tour_automation', 'Read the Automation tour'], +])} +${card('system', 'Datasources, i18n & docs', 'External-datasource federation (auto-connecting, read-only), en/zh-CN translations, email templates, a custom REST endpoint, and this manual itself — docs and books as metadata.', [ + ['apps/com.example.showcase/showcase_external_customer', 'Federated External Customers'], + ['docs/showcase_index', 'The manual (docs-as-metadata)'], + ['docs/showcase_tour_system', 'Read the System tour'], +])} +${card('security', 'Roles, permissions & sharing', 'A role hierarchy, permission sets with field- and row-level security, the fallback profile, and sharing rules — declared in metadata and enforced end to end.', [ + ['apps/com.example.showcase/showcase_private_note', 'Private Notes (owner-only)'], + ['docs/showcase_tour_security', 'Read the Security tour'], +])} +${card('ai · deferred', 'Agents, tools & skills', 'Deliberately not demonstrated here: agents are platform-owned (ADR-0063) and the open framework exposes AI via MCP only. The coverage manifest waives agent/tool/skill with a tracking issue instead of faking a demo.', [ + ['https://github.com/objectstack-ai/framework/issues/2610', 'Tracking issue #2610'], +])} +
+ +
Provenance: src/coverage.ts — every metadata kind in the registry is either demonstrated (with proof files) or waived (with a reason and an issue). Run pnpm verify to hold the map to it.
+
`, +}); diff --git a/examples/app-showcase/src/ui/pages/index.ts b/examples/app-showcase/src/ui/pages/index.ts index 0a4e8663cb..4fc6846f78 100644 --- a/examples/app-showcase/src/ui/pages/index.ts +++ b/examples/app-showcase/src/ui/pages/index.ts @@ -3,6 +3,7 @@ import { definePage } from '@objectstack/spec/ui'; export { StartHerePage } from './start-here.page.js'; +export { CapabilityMapPage } from './capability-map.page.js'; export { ProjectWorkspacePage } from './project-workspace.page.js'; export { ProjectDetailPage } from './project-detail.page.js'; export { TaskWorkbenchPage } from './task-workbench.page.js'; diff --git a/examples/app-showcase/src/ui/pages/start-here.page.ts b/examples/app-showcase/src/ui/pages/start-here.page.ts index 8f687d594f..54d3eef183 100644 --- a/examples/app-showcase/src/ui/pages/start-here.page.ts +++ b/examples/app-showcase/src/ui/pages/start-here.page.ts @@ -20,7 +20,7 @@ export const StartHerePage = definePage({ label: 'Start Here', type: 'home', kind: 'html', - isDefault: true, + isDefault: false, source: ` From 3688cfc51b1d6271fc8317e2b3921fb07221b4cd Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Jul 2026 03:01:36 +0000 Subject: [PATCH 5/6] =?UTF-8?q?docs(showcase,examples):=20truth=20pass=20?= =?UTF-8?q?=E2=80=94=20remove=20phantom=20AI=20chain,=20document=20the=20n?= =?UTF-8?q?ew=20layout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - app-showcase/README.md: drop the false 'AI: an agent wired to a tool and a skill' claim and the nonexistent src/agents/ tree entry; replace with an explicit 'Where is AI?' waiver note (ADR-0063, #2610). Document the six-domain directory layout (with the two pinned exceptions and why), the two-level coverage contract, the guided tour + capability map, and the cube/extension additions. Fix the report- types claim (tabular is a ListView lens per ADR-0021). - package.json: description no longer advertises an AI capability chain. - objectstack.config.ts: header comment reflects the tour layer and the kind-level verification contract. - examples/README.md: rewrite the stale catalog — plugin-bi does not exist; app-crm, app-showcase, and embed-objectql do. Point 'which example demonstrates what' at the CI-enforced coverage manifest instead of a hand-maintained protocol table. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01G6pPpRszk9cD3SxcKNFMWs --- examples/README.md | 387 ++++---------------- examples/app-showcase/README.md | 123 +++++-- examples/app-showcase/objectstack.config.ts | 11 +- examples/app-showcase/package.json | 2 +- 4 files changed, 155 insertions(+), 368 deletions(-) diff --git a/examples/README.md b/examples/README.md index 2f11627cbc..f5270ec6ab 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,356 +1,93 @@ # ObjectStack Examples Catalog -> **Comprehensive examples demonstrating all ObjectStack protocols and features** +> In-repo examples, organized by what you want to learn. Each one compiles, +> boots, and is exercised by CI (`pnpm --filter verify` / `test`). -Welcome to the ObjectStack examples catalog! This directory contains carefully crafted examples organized by complexity and use case to help you get started quickly and learn the platform effectively. +## The examples -## 📚 Quick Navigation +| Example | Level | What it is | +|---------|-------|------------| +| [App Todo](./app-todo/) | 🟢 Beginner | A complete but small task-management app — the fastest way to learn the by-type conventions (objects, actions, dashboards, reports, flows, i18n). | +| [App CRM](./app-crm/) | 🟡 Intermediate | A deliberately minimal CRM that smoke-tests the metadata application loading pipeline (Account/Contact/Lead/Opportunity + cube, mapping, connector, extension, portal). Not a feature showcase. | +| [App Showcase](./app-showcase/) | 🟣 Reference | The **kitchen-sink conformance fixture** — `src/` mirrors the six protocol domains; every metadata kind in the registry is demonstrated or explicitly waived, every field/view/chart/report/action variant appears at least once, and the coverage test enforces it. Start at its Capability Map landing page. | +| [Embed ObjectQL](./embed-objectql/) | 🟢 Focused | Using the ObjectQL data engine as a plain library — no kernel, no plugins (ADR-0076). | +| [HotCRM](https://github.com/objectstack-ai/hotcrm) | 🔴 Production reference | Full enterprise CRM in its own repository (10+ objects, sharing rules, approval flows, multi-driver E2E). | -### By Learning Path +**Note:** in-repo examples are intentionally lean; production-grade reference +apps (HotCRM, …) live in dedicated repositories under the +[objectstack-ai org](https://github.com/objectstack-ai). -| Level | Examples | Description | -|-------|----------|-------------| -| 🟢 **Beginner** | [App Todo](#app-todo), [Plugin BI](#plugin-bi) | Start here - simple, focused examples | -| 🟡 **Intermediate** | [HotCRM](https://github.com/objectstack-ai/hotcrm) | Real-world enterprise application (separate repo) | -| 🟣 **Reference** | [App Showcase](./app-showcase/) | Kitchen-sink — **every** metadata type, view type, and chart type, plus a coverage test | -| 🔴 **Advanced** | [Server](../apps/objectos/) | Server hosting & plugin orchestration | +## Which example demonstrates what? -### By Protocol Category +The authoritative, CI-enforced answer is the showcase's coverage manifest: +[`app-showcase/src/coverage.ts`](./app-showcase/src/coverage.ts). It maps +**every metadata kind** in `DEFAULT_METADATA_TYPE_REGISTRY` to the files that +demonstrate it — or to an explicit waiver with a reason and tracking issue +(e.g. AI agent/tool/skill are waived per ADR-0063, not faked). The test suite +fails whenever the platform gains a capability no example demonstrates. -| Protocol | Examples | Status | -|----------|----------|--------| -| **Data (ObjectQL)** | [App Todo](./app-todo/), [HotCRM](https://github.com/objectstack-ai/hotcrm) | ✅ Complete | -| **UI (ObjectUI)** | [App Todo](./app-todo/), [HotCRM](https://github.com/objectstack-ai/hotcrm) | ✅ Complete | -| **System (ObjectOS)** | [HotCRM](https://github.com/objectstack-ai/hotcrm) | ✅ Complete | -| **Automation** | [App Todo](./app-todo/), [HotCRM](https://github.com/objectstack-ai/hotcrm) | ✅ Complete | -| **API** | [HotCRM](https://github.com/objectstack-ai/hotcrm) | ✅ Complete | -| **BI / Analytics** | [Plugin BI](./plugin-bi/) | 🔴 Stub | -| **Hub & Marketplace** | _Coming soon_ | 🔴 Planned | - -## 🎯 Example Descriptions - -### App Todo -**Path:** [`examples/app-todo/`](./app-todo/) -**Level:** 🟢 Beginner -**Protocols:** Data, UI, Automation - -A complete task management application demonstrating all core ObjectStack protocols using the by-type directory convention. - -**What you'll learn:** -- Object definitions with validations and workflows -- Actions (complete, defer, clone, bulk operations) -- Dashboards with 10 widgets (metrics, charts, tables) -- Reports (6 types: tabular, summary, matrix) -- Automation flows (reminders, escalation, recurring tasks) -- App navigation and branding configuration -- **I18n translations** (English, Chinese, Japanese) -- Package structure with `objectstack.config.ts` - -**Directory Structure:** -``` -app-todo/ -├── objectstack.config.ts # Main manifest (defineStack) -├── src/ -│ ├── objects/ # Object & hook definitions -│ │ ├── task.object.ts -│ │ └── task.hook.ts -│ ├── actions/ # Action definitions -│ │ └── task.actions.ts -│ ├── apps/ # App navigation -│ │ └── todo.app.ts -│ ├── dashboards/ # Dashboard widgets -│ │ └── task.dashboard.ts -│ ├── reports/ # Report definitions -│ │ └── task.report.ts -│ ├── flows/ # Automation flows -│ │ └── task.flow.ts -│ └── translations/ # I18n translations (en, zh-CN, ja-JP) -│ └── todo.translation.ts -└── test/ - └── seed.test.ts -``` - -**Quick Start:** -```bash -cd examples/app-todo -pnpm install -pnpm typecheck -``` - ---- - -### App Showcase -**Path:** [`examples/app-showcase/`](./app-showcase/) -**Level:** 🟣 Reference -**Protocols:** Data, UI, System, Automation, AI, Auth - -A **kitchen-sink** workspace built for demonstration, debugging, and -coverage-driven verification. It pairs a realistic project-delivery domain -with synthetic "gallery" objects that exhaust protocol variants, and ties them -together with a coverage manifest that the test suite checks against the -protocol's own Zod enums. - -**What's included:** -- **All 49 field types** (`src/objects/field-zoo.object.ts`) + every relationship kind (lookup, master-detail, self-referencing tree, many-to-many junction) -- **All 8 list-view types** on one object (grid, kanban, gallery, calendar, timeline, gantt, map, chart) + all 5 form types -- **All 38 chart types** in one dashboard + all 4 report types (tabular/summary/matrix/joined) -- The action **type × location** matrix and a component-gallery page -- Capability chains: security (RBAC + FLS + RLS + sharing + policy), automation (flow → approval → webhook → job → email), and AI (agent + tool + skill) -- A **coverage test** that introspects the spec enums and fails when a new variant is left uncovered +For a guided walkthrough, boot the showcase and follow its per-domain tour: ```bash cd examples/app-showcase -pnpm verify # typecheck + coverage test -pnpm dev # → http://localhost:3000/_studio -``` - ---- - -### App CRM (external) -**Repo:** [github.com/objectstack-ai/hotcrm](https://github.com/objectstack-ai/hotcrm) -**Level:** 🟡 Intermediate -**Protocols:** Data, UI, Automation, AI - -**Full-featured CRM** demonstrating enterprise-grade patterns and all major field types. The CRM example has been extracted into its own repository (HotCRM) so it can evolve independently of the framework. Clone it side-by-side to follow along with the docs: - -```bash -git clone https://github.com/objectstack-ai/hotcrm.git -cd hotcrm -pnpm install -pnpm dev -``` - -**What's included:** -- 12 interconnected objects (Account, Contact, Opportunity, Lead, Case, Task, Campaign, Contract, Product, Quote) -- All 28 field types demonstrated -- Multiple view types (Grid, Kanban, Calendar, Gantt) -- Validation rules and workflows -- 3 dashboards (Executive, Sales, Service) plus a unified CRM overview -- 6 reports (by account, contact, lead, opportunity, case, task) -- 5 automation flows (lead conversion, case escalation, opportunity approval, etc.) -- AI agents and RAG pipelines -- Sharing rules, profiles, and role hierarchy -- **I18n translations** (English, Chinese, Japanese, Spanish) -- Multi-driver E2E acceptance harness (sqlite / mongodb / postgres) - ---- - -**Note:** Each example app in the framework monorepo is intentionally minimal. Production-grade reference apps (HotCRM, …) live in dedicated repositories under the [objectstack-ai org](https://github.com/objectstack-ai). - ---- - -### Plugin BI -**Path:** [`examples/plugin-bi/`](./plugin-bi/) -**Level:** 🟢 Beginner -**Protocols:** Data, UI (Dashboards) - -**BI Plugin stub** demonstrating how to create an ObjectStack plugin that provides analytics objects and dashboards. Currently a placeholder for adding business intelligence capabilities. - -**What you'll learn:** -- Plugin manifest structure (`type: 'plugin'`) -- Extending an app with analytics objects -- Dashboard widget definitions - -**Directory Structure:** +pnpm dev # → http://localhost:3000 — landing = Capability Map +pnpm verify # validate + typecheck + coverage tests ``` -plugin-bi/ -├── objectstack.config.ts # Plugin manifest (defineStack) -└── package.json -``` - -**Quick Start:** -```bash -cd examples/plugin-bi -pnpm install -pnpm typecheck -``` - ---- - -## 🗺️ Protocol Coverage Map - -### Data Protocol (ObjectQL) -| Protocol | Example | Location | -|----------|---------|----------| -| Object Definition | ✅ Complete | [Todo Objects](./app-todo/src/objects/), [HotCRM Objects](https://github.com/objectstack-ai/hotcrm/tree/main/src/objects) | -| Field Types (28 types) | ✅ Complete | [HotCRM Account](https://github.com/objectstack-ai/hotcrm/blob/main/src/objects/account.object.ts) | -| Validation Rules | ✅ Complete | [Todo](./app-todo/src/objects/task.object.ts), [HotCRM](https://github.com/objectstack-ai/hotcrm) | -| Relationships | ✅ Complete | [HotCRM Contact](https://github.com/objectstack-ai/hotcrm/blob/main/src/objects/contact.object.ts) | -| Formulas | ✅ Complete | [HotCRM Account](https://github.com/objectstack-ai/hotcrm/blob/main/src/objects/account.object.ts) | -| Hooks | ✅ Complete | [Todo Hooks](./app-todo/src/objects/task.hook.ts), [HotCRM Hooks](https://github.com/objectstack-ai/hotcrm/tree/main/src/objects) | -| State Machines | ✅ Complete | [HotCRM Lead State](https://github.com/objectstack-ai/hotcrm/blob/main/src/objects/lead.state.ts) | -| Query & Filters | ✅ Complete | [Todo](./app-todo/), [HotCRM](https://github.com/objectstack-ai/hotcrm) | -| Document Storage | 🔴 Missing | _Planned_ | -### UI Protocol (ObjectUI) -| Protocol | Example | Location | -|----------|---------|----------| -| List Views | ✅ Complete | [HotCRM](https://github.com/objectstack-ai/hotcrm) - Grid, Kanban, Calendar, Gantt | -| Form Views | ✅ Complete | [HotCRM](https://github.com/objectstack-ai/hotcrm) - Simple, Tabbed, Wizard | -| Actions | ✅ Complete | [Todo Actions](./app-todo/src/actions/), [HotCRM Actions](https://github.com/objectstack-ai/hotcrm/tree/main/src/actions) | -| Dashboards | ✅ Complete | [Todo Dashboard](./app-todo/src/dashboards/), [HotCRM Dashboards](https://github.com/objectstack-ai/hotcrm/tree/main/src/dashboards) | -| Reports | ✅ Complete | [Todo Reports](./app-todo/src/reports/), [HotCRM Reports](https://github.com/objectstack-ai/hotcrm/tree/main/src/reports) | -| Apps | ✅ Complete | [Todo App](./app-todo/src/apps/todo.app.ts), [HotCRM App](https://github.com/objectstack-ai/hotcrm/blob/main/src/apps/crm.app.ts) | -| Charts | ✅ Complete | [HotCRM Dashboards](https://github.com/objectstack-ai/hotcrm/tree/main/src/dashboards) | -| Widgets | ✅ Complete | [Todo Dashboard](./app-todo/src/dashboards/task.dashboard.ts) | -| Components | 🔴 Missing | _Planned_ | +## Getting started -### System Protocol (ObjectOS) -| Protocol | Example | Location | -|----------|---------|----------| -| Manifest | ✅ Complete | All examples with `objectstack.config.ts` | -| Plugin System | ✅ Complete | [HotCRM](https://github.com/objectstack-ai/hotcrm) | -| Preview Mode | ✅ Complete | [HotCRM](https://github.com/objectstack-ai/hotcrm) — `OS_MODE=preview` | -| Datasources | 🟡 Partial | [HotCRM](https://github.com/objectstack-ai/hotcrm) | -| I18n / Translations | ✅ Complete | [Todo Translations](./app-todo/src/translations/), [HotCRM Translations](https://github.com/objectstack-ai/hotcrm/tree/main/src/translations) | -| Job Scheduling | 🔴 Missing | _Planned_ | -| Metrics | 🔴 Missing | _Planned_ | - -### AI Protocol -| Protocol | Example | Location | -|----------|---------|----------| -| Agent | ✅ Complete | [HotCRM Agents](https://github.com/objectstack-ai/hotcrm/tree/main/src/agents) | -| RAG Pipeline | ✅ Complete | [HotCRM RAG](https://github.com/objectstack-ai/hotcrm/tree/main/src/rag) | -| Model Registry | ✅ Complete | _Spec Only_ | - -### Automation Protocol -| Protocol | Example | Location | -|----------|---------|----------| -| Workflow Rules | ✅ Complete | [Todo](./app-todo/src/objects/task.object.ts), [HotCRM](https://github.com/objectstack-ai/hotcrm) | -| Flow (Visual) | ✅ Complete | [Todo Flows](./app-todo/src/flows/), [HotCRM Flows](https://github.com/objectstack-ai/hotcrm/tree/main/src/flows) | -| Approval Processes | ✅ Complete | [HotCRM Opportunity Approval](https://github.com/objectstack-ai/hotcrm/blob/main/src/flows/opportunity-approval.flow.ts) | -| Triggers | ✅ Complete | [Todo](./app-todo/), [HotCRM](https://github.com/objectstack-ai/hotcrm) | - -### Auth & Permissions -| Protocol | Example | Location | -|----------|---------|----------| -| Profiles | ✅ Complete | [HotCRM Profiles](https://github.com/objectstack-ai/hotcrm/tree/main/src/profiles) | -| Sharing Rules | ✅ Complete | [HotCRM Sharing](https://github.com/objectstack-ai/hotcrm/tree/main/src/sharing) | -| RBAC | 🟡 Partial | [HotCRM](https://github.com/objectstack-ai/hotcrm) | - -### API Protocol -| Protocol | Example | Location | -|----------|---------|----------| -| REST Server | ✅ Complete | [HotCRM](https://github.com/objectstack-ai/hotcrm) | -| Custom APIs | ✅ Complete | [HotCRM APIs](https://github.com/objectstack-ai/hotcrm/tree/main/src/apis) | -| GraphQL | 🔴 Missing | _Planned_ | -| WebSocket/Realtime | 🔴 Missing | _Planned_ | - ---- - -## 🚀 Getting Started - -### Prerequisites ```bash -# Ensure you have Node.js 18+ and pnpm installed -node --version # >= 18.0.0 -pnpm --version # >= 8.0.0 -``` - -### Quick Start -```bash -# 1. Clone and install -git clone https://github.com/objectstack-ai/spec.git -cd spec +# From the repo root pnpm install +pnpm setup # first-time: install + build spec -# 2. Build the spec package -pnpm --filter @objectstack/spec build +# Beginner path +cd examples/app-todo && pnpm typecheck -# 3. Explore examples -cd examples/app-todo -pnpm typecheck - -# 4. Or explore the CRM (separate repository) -git clone https://github.com/objectstack-ai/hotcrm.git -cd hotcrm && pnpm install && pnpm build +# Reference path +cd examples/app-showcase && pnpm verify && pnpm dev ``` -### Learning Path - -#### Path 1: Quick Start (1-2 hours) -1. Read [Todo Example](./app-todo/) - Understand basic structure and conventions -2. Explore [Todo objectstack.config.ts](./app-todo/objectstack.config.ts) - See manifest patterns -3. Browse [HotCRM](https://github.com/objectstack-ai/hotcrm) - Learn advanced features - -#### Path 2: Deep Dive (1-2 days) -1. Complete Path 1 -2. Study [HotCRM Objects](https://github.com/objectstack-ai/hotcrm/tree/main/src/objects) - Master field types and relationships -3. Review [HotCRM Flows](https://github.com/objectstack-ai/hotcrm/tree/main/src/flows) - Understand automation patterns - ---- - -## 📝 Example Standards - -All examples in this directory follow these standards: - -### Code Quality -- ✅ **Type-safe**: All examples use TypeScript and pass `typecheck` -- ✅ **Zod-first**: Schemas defined with Zod, types inferred -- ✅ **Naming conventions**: `camelCase` for config, `snake_case` for data -- ✅ **Documented**: Comprehensive inline comments -- ✅ **Best practices**: Follow ObjectStack conventions - -### File Structure (By-Type Convention) -``` -example-name/ -├── README.md # Comprehensive documentation -├── package.json # Package definition -├── tsconfig.json # TypeScript config -├── objectstack.config.ts # Main manifest (defineStack) -├── src/ -│ ├── objects/ # *.object.ts, *.hook.ts, *.state.ts -│ ├── actions/ # *.actions.ts -│ ├── apps/ # *.app.ts -│ ├── dashboards/ # *.dashboard.ts -│ ├── reports/ # *.report.ts -│ ├── flows/ # *.flow.ts -│ └── translations/ # *.translation.ts (i18n bundles) -└── test/ - └── seed.test.ts -``` - -### Documentation Requirements -Each example MUST have: -- Clear purpose statement -- Prerequisites and dependencies -- Quick start instructions -- Protocol coverage explanation -- Key concepts highlighted -- Related examples linked - ---- - -## 🤝 Contributing Examples +### Learning path -Want to add an example? Great! Please ensure: +1. **App Todo** — structure, conventions, `objectstack.config.ts`. +2. **App Showcase** — click through the Capability Map; read the Guided Tour + docs (one per protocol domain); grep the file the coverage manifest points + at whenever you wonder "how do I author X?". +3. **App CRM / HotCRM** — realistic relational modeling and enterprise + patterns. -1. **Follow the standards** above -2. **Fill a gap** in protocol coverage -3. **Add documentation** (README.md) -4. **Test thoroughly** (must compile and run) -5. **Submit PR** with clear description +## Example standards -See [CONTRIBUTING.md](../CONTRIBUTING.md) for details. +- ✅ **Type-safe** — passes `typecheck`; Zod-first, types inferred. +- ✅ **Named right** — `camelCase` config keys, `snake_case` machine names. +- ✅ **Verified** — `pnpm --filter verify` must pass after any metadata + edit (metadata errors fail silently at runtime; see [AGENTS.md](./AGENTS.md)). +- ✅ **Honest** — never demo a capability the runtime doesn't deliver + (Prime Directive #10); waive it with an issue instead. ---- +### File structure -## 📚 Additional Resources +Small examples use the flat by-type convention (`src/objects/`, +`src/actions/`, …— see app-todo). The showcase, being domain-complete, groups +the same per-type directories under the six protocol domains +(`src/data/`, `src/ui/`, `src/automation/`, `src/system/`, `src/security/`) — +see its README for the full tree and the two pinned exceptions +(`src/coverage.ts`, flat `src/docs/`). -- **[Main Documentation](../content/docs/)** - Complete protocol reference -- **[Architecture Guide](../ARCHITECTURE.md)** - System architecture -- **[Quick Reference](../QUICK-REFERENCE.md)** - Fast lookup -- **[Package Dependencies](../PACKAGE-DEPENDENCIES.md)** - Build order +## Contributing examples ---- +1. Follow the standards above and fill a real gap. +2. Add a README with purpose, quick start, and what it demonstrates. +3. If you demonstrate a previously-waived capability, flip its entry in the + showcase coverage manifest. +4. Submit a PR — see [CONTRIBUTING.md](../CONTRIBUTING.md). -## 📄 License +## Additional resources -All examples are licensed under Apache 2.0. See [LICENSE](../LICENSE) for details. +- **[Main Documentation](../content/docs/)** — complete protocol reference +- **[examples/AGENTS.md](./AGENTS.md)** — agent rules for editing examples ---- +## License -**Last Updated:** 2026-02-12 -**Protocol Version:** 3.0.0 -**Total Examples:** 2 in-repo (app-todo, plugin-bi) + 1 external ([HotCRM](https://github.com/objectstack-ai/hotcrm)) -**Directory Convention:** By-Type (Salesforce DX style) +All examples are licensed under Apache 2.0. See [LICENSE](../LICENSE). diff --git a/examples/app-showcase/README.md b/examples/app-showcase/README.md index d14f2c59aa..4d4f276230 100644 --- a/examples/app-showcase/README.md +++ b/examples/app-showcase/README.md @@ -43,28 +43,36 @@ pnpm verify ## What it covers ### Data layer (ObjectQL) -- **All 49 field types** — `src/objects/field-zoo.object.ts` carries one field - of every `FieldType`, with the remainder appearing naturally on the backbone - objects. +- **All 49 field types** — `src/data/objects/field-zoo.object.ts` carries one + field of every `FieldType`, with the remainder appearing naturally on the + backbone objects. - **Every relationship kind** — `lookup` (project → account, category → self), `master_detail` (task → project), self-referencing **hierarchy/tree** (`Category.parent`), and **many-to-many** via the `showcase_project_membership` junction. - **Formulas, validations, and a status state machine** on `Project` and - `Task`. + `Task` — only the rule types the runtime actually **enforces** are + demonstrated (the unenforced ones are tracked in #1475, not faked here). +- **An object extension** (`src/data/extensions/account.extension.ts`) merged + additively into `showcase_account` at registration — the package-extends- + an-object mechanism. +- **An analytics cube** (`src/data/analytics/showcase.cube.ts`) served by the + analytics service at `/api/v1/analytics/*`. ### View layer (ObjectUI) -- **All 8 list-view types** on a single object (`src/views/task.view.ts`): +- **All 8 list-view types** on a single object (`src/ui/views/task.view.ts`): grid, kanban, gallery, calendar, timeline, gantt, map, chart. The Task object's fields are chosen so one object can back every type. - **All 5 form-view types**: simple, tabbed, wizard, split, drawer. -- **The full chart taxonomy** — `src/dashboards/chart-gallery.dashboard.ts` +- **The full chart taxonomy** — `src/ui/dashboards/chart-gallery.dashboard.ts` has one widget per chart family (all 38 `ChartType`s). -- **All 4 report types**: tabular, summary, matrix, joined - (`src/reports/index.ts`). +- **Every analytics report type**: summary, matrix, joined + (`src/ui/reports/index.ts`) — a flat *tabular* list is deliberately an + object-bound ListView lens, not a report (ADR-0021). - **The action matrix** — every `ActionType` (script/url/flow/modal/api/form) across every `ActionLocation`. -- **A component-gallery page** placing the standard page components. +- **Four page-authoring models** — structured (full/slotted), constrained-JSX + `html`, and executed `react`, taught by the **Page Authoring** index page. ### Capability chains (the "complex abilities") - **Security** (`src/security/index.ts`): a role hierarchy + a permission set @@ -73,68 +81,105 @@ pnpm verify and an org **policy**. - **Automation**: a record-triggered flow → a screen-flow wizard → a multi-step **approval** → an outbound **webhook** → a scheduled **job** → an **email** - template. -- **AI**: an **agent** wired to a **tool** and a **skill**. + template, plus live REST/Slack **connector actions**. - **i18n / theming / portals**: `en` + `zh-CN` translations, light + dark themes, and an external client portal. +> **Where is AI?** Deliberately absent. Agents are platform-owned (ADR-0063 — +> third parties author skills/tools, never agents), and the open framework +> exposes AI via `@objectstack/mcp` only. Rather than fake a demo, the +> coverage manifest **waives** `agent`/`tool`/`skill` with tracking issue +> [#2610](https://github.com/objectstack-ai/framework/issues/2610). + ## The coverage manifest — how "confirm" works -`src/coverage.ts` declares what the showcase is supposed to cover and provides -the collectors the test uses. `test/coverage.test.ts` then **introspects the -protocol's own enums** (`FieldType`, `ChartTypeSchema`, `ReportType`, -`ActionType`, `ACTION_LOCATIONS`) and asserts every member appears at least -once across the registered metadata. +`src/coverage.ts` declares what the showcase is supposed to cover at **two +levels**, and `test/coverage.test.ts` proves both: + +- **Kind level** — `KIND_COVERAGE` enumerates every metadata kind in + `DEFAULT_METADATA_TYPE_REGISTRY`. Each kind is either `demonstrated` + (pointing at the proof files, which must exist) or explicitly `waived` + (with a reason **and a tracking-issue link**). A new registry kind fails CI + until it is accounted for; nothing can silently go missing. The same + contract covers stack collections that aren't registry kinds + (`STACK_COLLECTION_COVERAGE`: analyticsCubes, objectExtensions, and the + waived mappings/connectors). +- **Variant level** — the test **introspects the protocol's own enums** + (`FieldType`, `ChartTypeSchema`, `ReportType`, `ActionType`, + `ACTION_LOCATIONS`) and asserts every member appears at least once across + the registered metadata. Because the expected sets come from the **spec** — not a hand-maintained list — -the test fails automatically when the platform gains a new field type, chart -type, or report type that the showcase hasn't demonstrated yet. That keeps this -example a **living conformance fixture**, not a static snapshot. (`defineStack` -itself also runs full schema + cross-reference validation when the config is -imported, so `pnpm test` proves the whole stack loads cleanly.) +the tests fail automatically when the platform gains a new kind, field type, +chart type, or report type that the showcase hasn't demonstrated yet. That +keeps this example a **living conformance fixture**, not a static snapshot. +(`defineStack` itself also runs full schema + cross-reference validation when +the config is imported, so `pnpm test` proves the whole stack loads cleanly.) + +The waiver policy is Prime Directive #10 in action: a capability the runtime +doesn't deliver is **never demoed** — it is waived, loudly, with an issue. + +## Guided tour & capability map + +The app's landing page is the **Capability Map** (`src/ui/pages/ +capability-map.page.ts`) — one card per protocol domain linking the flagship +demos. Five **tour docs** (`src/docs/showcase_tour_*.md`) walk each domain, +with live ` ```metadata ` embeds (ADR-0051) rendered from the running +metadata; the **Showcase Manual** book curates them into a Guided Tour group, +served publicly via the library portal. ## Directory layout +`src/` mirrors the six protocol domains of the metadata registry +(`DEFAULT_METADATA_TYPE_REGISTRY`), with per-type directories inside each: + ``` app-showcase/ ├── objectstack.config.ts # defineStack — registers everything ├── src/ -│ ├── coverage.ts # coverage manifest + collectors (the soul) -│ ├── objects/ # field-zoo + backbone + junction + tree -│ ├── views/ # all 8 list types + all 5 form types -│ ├── dashboards/ # chart gallery (all 38 chart types) -│ ├── reports/ # tabular / summary / matrix / joined -│ ├── actions/ # type × location matrix -│ ├── pages/ # component gallery -│ ├── apps/ # navigation linking every surface -│ ├── security/ # roles + FLS + RLS + sharing + policy -│ ├── flows/ approvals/ webhooks/ jobs/ emails/ # automation chain -│ ├── agents/ # agent + tool + skill -│ ├── themes/ translations/ datasources/ portals/ -│ └── data/ # seed data sized to feed every view +│ ├── coverage.ts # coverage manifest (PINNED here: package export) +│ ├── docs/ # doc metadata (PINNED here & flat: CLI contract, ADR-0046) +│ ├── data/ # ── data domain ── +│ │ ├── objects/ # field-zoo + backbone + junction + tree + federated +│ │ ├── extensions/ # object-extension overlay on showcase_account +│ │ ├── analytics/ # showcase_delivery cube +│ │ ├── hooks/ seed/ # lifecycle hooks · seed data sized to feed every view +│ ├── ui/ # ── ui domain ── +│ │ ├── apps/ views/ pages/ dashboards/ datasets/ reports/ actions/ +│ │ └── themes/ portals/ +│ ├── automation/ # ── automation domain ── +│ │ └── flows/ jobs/ webhooks/ +│ ├── system/ # ── system domain ── +│ │ └── datasources/ emails/ translations/ books/ server/ +│ └── security/ # ── security domain: roles + FLS + RLS + sharing └── test/ - ├── coverage.test.ts # introspects spec enums, asserts coverage + ├── coverage.test.ts # registry kinds + spec enums, asserted + ├── gap-fill.test.ts # cube shape + extension merge (real registry) └── seed.test.ts # stack-loads + breadth smoke test ``` +(The AI domain has no `src/ai/` on purpose — see the waiver note above.) + ## Extending it When you add a new variant to the platform, the coverage test will go red and point at the gap. Add a field/view/widget that uses it, reference it in -`COVERAGE`, and the test goes green again. +`COVERAGE`, and the test goes green again. When you add a new **metadata +kind**, the kind-level test goes red instead: demonstrate it under the right +domain directory, or waive it with a reason + issue in `KIND_COVERAGE`. ## External datasource federation (ADR-0015) The showcase ships a **code-defined external datasource** that demonstrates the full federation path with **no external server** — `os dev` just works. -- `src/datasources/showcase-external.datasource.ts` — a second, read-only SQLite +- `src/system/datasources/showcase-external.datasource.ts` — a second, read-only SQLite database (`schemaMode: 'external'`), separate from the managed standalone DB. -- `src/objects/external/{customer,order}.object.ts` — federated objects +- `src/data/objects/external/{customer,order}.object.ts` — federated objects (`showcase_ext_customer`, `showcase_ext_order`) bound to the remote tables `customers` / `orders` via `external.remoteName`. The object names deliberately differ from the table names to show the remote-table remap. -- `src/datasources/external-fixture.ts` — the stack's `onEnable` hook. It +- `src/system/datasources/external-fixture.ts` — the stack's `onEnable` hook. It idempotently provisions the fixture SQLite file (tables + seed rows), registers a live read-only driver under the datasource name, and registers the federated objects' read metadata. diff --git a/examples/app-showcase/objectstack.config.ts b/examples/app-showcase/objectstack.config.ts index ef1a395775..b8ae9ab06c 100644 --- a/examples/app-showcase/objectstack.config.ts +++ b/examples/app-showcase/objectstack.config.ts @@ -56,12 +56,17 @@ const marketplaceUrl = resolveCloudUrl(); * chains. It is built for three audiences at once: * * • Demonstration — a coherent project-delivery domain with seeded data - * so every view renders something real. + * so every view renders something real. The Capability Map landing page + * indexes every demo by protocol domain, and five tour docs + * (src/docs/showcase_tour_*.md) walk each domain with live metadata + * embeds. * • Debugging — open in Studio (`pnpm dev` → http://localhost:3000/_studio) * and click through the gallery navigation. * • Verification — `pnpm verify` runs typecheck + the coverage test, which - * introspects the protocol's own enums and fails if any field/chart/ - * report type is left uncovered. + * introspects the protocol's own contracts at two levels: every metadata + * kind in DEFAULT_METADATA_TYPE_REGISTRY must be demonstrated or + * explicitly waived (reason + issue), and every enum variant + * (field/chart/report/action) must appear at least once. */ export default defineStack({ manifest: { diff --git a/examples/app-showcase/package.json b/examples/app-showcase/package.json index 8c0b0e1c1a..f7bf5ba0ce 100644 --- a/examples/app-showcase/package.json +++ b/examples/app-showcase/package.json @@ -1,7 +1,7 @@ { "name": "@objectstack/example-showcase", "version": "0.2.16", - "description": "Kitchen-sink showcase workspace — exercises every metadata type, every view type, every chart type, and the major end-to-end capability chains (security, automation, AI). Built for demonstration, debugging, and coverage-driven verification.", + "description": "Kitchen-sink showcase workspace — exercises every metadata type, every view type, every chart type, and the major end-to-end capability chains (security, automation, analytics). Built for demonstration, debugging, and coverage-driven verification.", "license": "Apache-2.0", "private": true, "main": "./objectstack.config.ts", From 3ddf1e83d5c1fc4b2a616dc3236096edfbe22551 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Jul 2026 04:17:05 +0000 Subject: [PATCH 6/6] ci: trigger checks for #2619 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01G6pPpRszk9cD3SxcKNFMWs