spine-framework 0.4.4 → 0.5.0-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +13 -3
- package/.framework/cli/commands/bootstrap.ts +392 -0
- package/.framework/cli/commands/dev.ts +91 -218
- package/.framework/cli/commands/doctor.ts +132 -26
- package/.framework/cli/commands/generate.ts +38 -298
- package/.framework/cli/commands/init.ts +20 -1
- package/.framework/cli/commands/install-app.ts +183 -13
- package/.framework/cli/commands/migrate.ts +36 -30
- package/.framework/cli/commands/migrations.ts +16 -56
- package/.framework/cli/commands/upgrade.ts +35 -0
- package/.framework/cli/commands/validate-app.ts +127 -1
- package/.framework/cli/commands/validate-schema.ts +147 -0
- package/.framework/cli/context.ts +223 -110
- package/.framework/cli/index.ts +6 -0
- package/.framework/cli/lib/project-files.ts +241 -1
- package/.framework/functions/_shared/app-manifest.ts +2 -0
- package/.framework/functions/_shared/hash-api-key.ts +183 -79
- package/.framework/functions/_shared/logging.ts +562 -224
- package/.framework/functions/_shared/middleware.ts +47 -44
- package/.framework/functions/_shared/permissions.ts +4 -0
- package/.framework/functions/admin-data.ts +51 -28
- package/.framework/functions/ai-agents.ts +10 -6
- package/.framework/functions/api-keys.ts +301 -122
- package/.framework/functions/apps.ts +63 -29
- package/.framework/functions/cil-ingest.ts +48 -15
- package/.framework/functions/debug-auth.ts +14 -8
- package/.framework/functions/deliveries.ts +39 -12
- package/.framework/functions/embeddings.ts +149 -78
- package/.framework/functions/integration-outbound.ts +67 -27
- package/.framework/functions/integrations.ts +254 -127
- package/.framework/functions/logs.ts +273 -133
- package/.framework/functions/observability.ts +278 -148
- package/.framework/functions/pipeline-executions.ts +1 -1
- package/.framework/functions/pipelines.ts +14 -9
- package/.framework/functions/prompt-configs.ts +7 -4
- package/.framework/functions/roles.ts +8 -5
- package/.framework/functions/security.ts +30 -17
- package/.framework/functions/system-cron.ts +240 -127
- package/.framework/functions/tests.ts +7 -6
- package/.framework/functions/timers.ts +310 -107
- package/.framework/functions/triggers.ts +28 -13
- package/.framework/functions/types.ts +13 -10
- package/.framework/migrations/069_log_stats_window.sql +54 -0
- package/.framework/migrations/070_app_type_schema_rpcs.sql +166 -0
- package/.framework/migrations/071_timer_interval_keys.sql +96 -0
- package/.framework/migrations/072_slug_autofill.sql +77 -0
- package/.framework/migrations/073_seed_member_roles.sql +26 -0
- package/.framework/migrations/074_app_route_prefix_domains.sql +168 -0
- package/.framework/migrations/075_security_definer_grants.sql +157 -0
- package/.framework/src/apps/admin/index.tsx +1141 -775
- package/.framework/src/components/ErrorBoundary.tsx +136 -0
- package/.framework/src/components/admin/AdminListPage.tsx +81 -73
- package/.framework/src/components/admin/AdminSidebar.tsx +88 -79
- package/.framework/src/components/admin/EntitySelect.tsx +181 -0
- package/.framework/src/components/admin/PageInstructions.tsx +86 -127
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
- package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
- package/.framework/src/components/layout/Sidebar.tsx +300 -177
- package/.framework/src/components/runtime/DataDetailPage.tsx +67 -35
- package/.framework/src/components/runtime/DataHeader.tsx +96 -48
- package/.framework/src/components/runtime/DataListPage.tsx +3 -26
- package/.framework/src/components/runtime/DataStats.tsx +10 -10
- package/.framework/src/components/runtime/DataTable.tsx +12 -10
- package/.framework/src/components/ui/sheet.tsx +24 -26
- package/.framework/src/components/ui/textarea.tsx +74 -48
- package/.framework/src/contexts/AppContext.tsx +5 -12
- package/.framework/src/contexts/AuthContext.tsx +17 -9
- package/.framework/src/hooks/useApps.ts +18 -22
- package/.framework/src/hooks/useEntityList.ts +12 -9
- package/.framework/src/hooks/useListSchema.ts +108 -58
- package/.framework/src/instructions/README.md +74 -0
- package/.framework/src/instructions/accounts.md +97 -0
- package/.framework/src/instructions/ai-agents.md +69 -0
- package/.framework/src/instructions/alerts.md +80 -0
- package/.framework/src/instructions/api-keys.md +87 -0
- package/.framework/src/instructions/apps.md +77 -0
- package/.framework/src/instructions/concepts/ai-and-prompts.md +150 -0
- package/.framework/src/instructions/concepts/apps-and-renderers.md +228 -0
- package/.framework/src/instructions/concepts/auth-and-api-keys.md +133 -0
- package/.framework/src/instructions/concepts/automation.md +186 -0
- package/.framework/src/instructions/concepts/concurrency-and-idempotency.md +125 -0
- package/.framework/src/instructions/concepts/core-vs-custom.md +124 -0
- package/.framework/src/instructions/concepts/design-schema.md +152 -0
- package/.framework/src/instructions/concepts/integrations-and-webhooks.md +127 -0
- package/.framework/src/instructions/concepts/observability.md +116 -0
- package/.framework/src/instructions/concepts/permissions.md +150 -0
- package/.framework/src/instructions/concepts/runtime-records.md +111 -0
- package/.framework/src/instructions/concepts/scope-and-accounts.md +112 -0
- package/.framework/src/instructions/concepts/spine-way.md +172 -0
- package/.framework/src/instructions/deliveries.md +59 -0
- package/.framework/src/instructions/guide/00-orientation.md +106 -0
- package/.framework/src/instructions/guide/01-install-and-first-run.md +253 -0
- package/.framework/src/instructions/guide/02-project-map.md +147 -0
- package/.framework/src/instructions/guide/03-domain-model.md +223 -0
- package/.framework/src/instructions/guide/04-first-app.md +227 -0
- package/.framework/src/instructions/guide/05-glossary.md +183 -0
- package/.framework/src/instructions/integrations.md +96 -0
- package/.framework/src/instructions/internal/cil-markers.md +83 -0
- package/.framework/src/instructions/internal/docs-contract.md +158 -0
- package/.framework/src/instructions/invites.md +60 -0
- package/.framework/src/instructions/kb.md +80 -0
- package/.framework/src/instructions/logs.md +77 -0
- package/.framework/src/instructions/notifications.md +77 -0
- package/.framework/src/instructions/observability.md +61 -0
- package/.framework/src/instructions/operations/deployment.md +115 -0
- package/.framework/src/instructions/operations/migrations.md +125 -0
- package/.framework/src/instructions/operations/security-checklist.md +170 -0
- package/.framework/src/instructions/operations/troubleshooting.md +170 -0
- package/.framework/src/instructions/operations/upgrade.md +116 -0
- package/.framework/src/instructions/people.md +91 -0
- package/.framework/src/instructions/pipeline-executions.md +62 -0
- package/.framework/src/instructions/pipelines.md +78 -0
- package/.framework/src/instructions/prompts.md +95 -0
- package/.framework/src/instructions/reference/api/admin-data.md +162 -0
- package/.framework/src/instructions/reference/api/auth.md +113 -0
- package/.framework/src/instructions/reference/api/errors-and-envelopes.md +113 -0
- package/.framework/src/instructions/reference/app-manifest.md +184 -0
- package/.framework/src/instructions/reference/cli.md +124 -0
- package/.framework/src/instructions/reference/database.md +132 -0
- package/.framework/src/instructions/reference/design-schema.md +280 -0
- package/.framework/src/instructions/reference/env-vars.md +133 -0
- package/.framework/src/instructions/reference/frontend-sdk.md +207 -0
- package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
- package/.framework/src/instructions/reference/functions/admin-data.md +197 -0
- package/.framework/src/instructions/reference/functions/admin-funnel.md +90 -0
- package/.framework/src/instructions/reference/functions/admin-install-funnel.md +107 -0
- package/.framework/src/instructions/reference/functions/admin-signals.md +102 -0
- package/.framework/src/instructions/reference/functions/ai-agents.md +148 -0
- package/.framework/src/instructions/reference/functions/ai-interactions.md +156 -0
- package/.framework/src/instructions/reference/functions/api-keys.md +130 -0
- package/.framework/src/instructions/reference/functions/apps.md +140 -0
- package/.framework/src/instructions/reference/functions/auth.md +99 -0
- package/.framework/src/instructions/reference/functions/debug-auth.md +91 -0
- package/.framework/src/instructions/reference/functions/deliveries.md +110 -0
- package/.framework/src/instructions/reference/functions/embeddings.md +166 -0
- package/.framework/src/instructions/reference/functions/eval-harness.md +149 -0
- package/.framework/src/instructions/reference/functions/index.md +103 -0
- package/.framework/src/instructions/reference/functions/integration-outbound.md +200 -0
- package/.framework/src/instructions/reference/functions/integration-routes.md +151 -0
- package/.framework/src/instructions/reference/functions/integrations.md +130 -0
- package/.framework/src/instructions/reference/functions/invites.md +182 -0
- package/.framework/src/instructions/reference/functions/item-progress.md +107 -0
- package/.framework/src/instructions/reference/functions/kb.md +166 -0
- package/.framework/src/instructions/reference/functions/logs.md +141 -0
- package/.framework/src/instructions/reference/functions/observability.md +105 -0
- package/.framework/src/instructions/reference/functions/pipeline-executions.md +132 -0
- package/.framework/src/instructions/reference/functions/pipeline-runner-background.md +121 -0
- package/.framework/src/instructions/reference/functions/pipelines.md +136 -0
- package/.framework/src/instructions/reference/functions/prompt-configs.md +121 -0
- package/.framework/src/instructions/reference/functions/roles.md +121 -0
- package/.framework/src/instructions/reference/functions/security.md +140 -0
- package/.framework/src/instructions/reference/functions/system-cron.md +136 -0
- package/.framework/src/instructions/reference/functions/system.md +106 -0
- package/.framework/src/instructions/reference/functions/tests.md +97 -0
- package/.framework/src/instructions/reference/functions/timers.md +117 -0
- package/.framework/src/instructions/reference/functions/trigger-scheduler.md +102 -0
- package/.framework/src/instructions/reference/functions/triggers.md +140 -0
- package/.framework/src/instructions/reference/functions/types.md +146 -0
- package/.framework/src/instructions/reference/seeds.md +126 -0
- package/.framework/src/instructions/reference/shared-modules.md +127 -0
- package/.framework/src/instructions/reference/spine-config.md +114 -0
- package/.framework/src/instructions/roles.md +80 -0
- package/.framework/src/instructions/runtime-collaboration.md +106 -0
- package/.framework/src/instructions/runtime-detail.md +70 -0
- package/.framework/src/instructions/runtime-list.md +77 -0
- package/.framework/src/instructions/security.md +67 -0
- package/.framework/src/instructions/tasks/add-a-pipeline.md +110 -0
- package/.framework/src/instructions/tasks/add-a-role.md +96 -0
- package/.framework/src/instructions/tasks/add-a-timer.md +114 -0
- package/.framework/src/instructions/tasks/add-a-trigger.md +106 -0
- package/.framework/src/instructions/tasks/add-a-type.md +151 -0
- package/.framework/src/instructions/tasks/add-a-webhook.md +108 -0
- package/.framework/src/instructions/tasks/add-an-ai-agent.md +108 -0
- package/.framework/src/instructions/tasks/add-an-integration.md +124 -0
- package/.framework/src/instructions/tasks/build-a-custom-page.md +118 -0
- package/.framework/src/instructions/tasks/document-your-app.md +117 -0
- package/.framework/src/instructions/tasks/package-and-install-an-app.md +136 -0
- package/.framework/src/instructions/tasks/register-an-app.md +137 -0
- package/.framework/src/instructions/tasks/test-your-code.md +108 -0
- package/.framework/src/instructions/tasks/write-a-function.md +132 -0
- package/.framework/src/instructions/tasks/write-a-migration.md +110 -0
- package/.framework/src/instructions/testing.md +59 -0
- package/.framework/src/instructions/timers.md +84 -0
- package/.framework/src/instructions/triggers.md +69 -0
- package/.framework/src/instructions/types.md +93 -0
- package/.framework/src/lib/app-mount.ts +88 -0
- package/.framework/src/pages/NotFoundPage.tsx +62 -31
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +117 -106
- package/.framework/src/pages/admin/AIAgentsPage.tsx +86 -48
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +153 -152
- package/.framework/src/pages/admin/APIKeysPage.tsx +91 -90
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +29 -18
- package/.framework/src/pages/admin/AppDetailPage.tsx +6 -60
- package/.framework/src/pages/admin/AppsPage.tsx +104 -66
- package/.framework/src/pages/admin/DeliveriesPage.tsx +5 -5
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +5 -23
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +5 -26
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +7 -7
- package/.framework/src/pages/admin/IntegrationsPage.tsx +85 -43
- package/.framework/src/pages/admin/LogsPage.tsx +144 -76
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +223 -140
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +134 -113
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +108 -45
- package/.framework/src/pages/admin/PipelinesPage.tsx +160 -115
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +96 -98
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +101 -59
- package/.framework/src/pages/admin/RoleDetailPage.tsx +90 -79
- package/.framework/src/pages/admin/RolesPage.tsx +86 -43
- package/.framework/src/pages/admin/SecurityPage.tsx +1 -1
- package/.framework/src/pages/admin/TestingDashboard.tsx +16 -28
- package/.framework/src/pages/admin/TimerDetailPage.tsx +117 -100
- package/.framework/src/pages/admin/TimersPage.tsx +90 -62
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +90 -83
- package/.framework/src/pages/admin/TriggersPage.tsx +124 -99
- package/.framework/src/pages/admin/TypeDetailPage.tsx +152 -138
- package/.framework/src/pages/admin/TypesPage.tsx +90 -48
- package/.framework/src/pages/admin/cil/CILAgentPage.tsx +17 -25
- package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +17 -27
- package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +72 -40
- package/.framework/src/pages/admin/cil/CILFileListPage.tsx +321 -129
- package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +18 -29
- package/.framework/src/pages/admin/cil/CILGraphPage.tsx +26 -38
- package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +34 -46
- package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +189 -103
- package/.framework/src/pages/admin/cil/CILSearchPage.tsx +246 -136
- package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +11 -22
- package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +9 -20
- package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +26 -38
- package/.framework/src/pages/admin/kb/KBAdminPage.tsx +7 -26
- package/.framework/src/pages/auth/LoginPage.tsx +3 -2
- package/.framework/src/pages/auth/RegisterPage.tsx +16 -8
- package/.framework/templates/agent/AGENTS.md +167 -0
- package/.framework/templates/agent/skills/spine-add-ai-agent/SKILL.md +58 -0
- package/.framework/templates/agent/skills/spine-add-automation/SKILL.md +62 -0
- package/.framework/templates/agent/skills/spine-add-integration/SKILL.md +63 -0
- package/.framework/templates/agent/skills/spine-architect/SKILL.md +86 -0
- package/.framework/templates/agent/skills/spine-install-app/SKILL.md +79 -0
- package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
- package/.framework/templates/agent/skills/spine-new-app/SKILL.md +79 -0
- package/.framework/templates/agent/skills/spine-new-function/SKILL.md +71 -0
- package/.framework/templates/agent/skills/spine-new-type/SKILL.md +72 -0
- package/.framework/templates/agent/skills/spine-schema/SKILL.md +77 -0
- package/.framework/templates/agent/skills/spine-test/SKILL.md +67 -0
- package/.framework/templates/agent/skills/spine-troubleshoot/SKILL.md +74 -0
- package/.framework/templates/agent/skills/spine-upgrade/SKILL.md +69 -0
- package/.framework/templates/project.json +4 -1
- package/.framework/tests/unit/account-scoping-gates.test.ts +394 -0
- package/.framework/tests/unit/agent-files.test.ts +355 -0
- package/.framework/tests/unit/agent-runner.test.ts +479 -44
- package/.framework/tests/unit/api-keys-create-scope-gate.test.ts +160 -0
- package/.framework/tests/unit/api-keys-omit-secret-fields.test.ts +38 -0
- package/.framework/tests/unit/app-mount.test.ts +80 -0
- package/.framework/tests/unit/bootstrap.test.ts +242 -0
- package/.framework/tests/unit/cli-context-api-key.test.ts +114 -0
- package/.framework/tests/unit/debug-auth.test.ts +38 -0
- package/.framework/tests/unit/deliveries-account-scoping.test.ts +185 -0
- package/.framework/tests/unit/integration-outbound-credentials.test.ts +167 -0
- package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
- package/.framework/tests/unit/logging-app-id.test.ts +75 -0
- package/.framework/tests/unit/security-definer-grants.test.ts +154 -0
- package/.framework/tests/unit/system-context-denial.test.ts +145 -0
- package/.framework/tests/unit/system-cron.test.ts +240 -0
- package/.framework/tests/unit/timers.test.ts +159 -0
- package/README.md +125 -166
- package/config/tsconfig.cli.json +2 -2
- package/package.json +15 -4
- package/templates/project/package-lock.json +2 -2
- package/.framework/docs/admin-routes.md +0 -76
- package/.framework/docs/api-docs-progress.md +0 -38
- package/.framework/docs/api-governance.md +0 -146
- package/.framework/docs/api-testing-results.md +0 -212
- package/.framework/docs/assembly-launch-guide.md +0 -138
- package/.framework/docs/audit-results.md +0 -590
- package/.framework/docs/db-api-inventory.md +0 -95
- package/.framework/docs/gap-remediation-backlog.md +0 -103
- package/.framework/docs/ui-adoption-verification.md +0 -111
- package/.framework/docs/ui-api-coverage.md +0 -84
- package/.framework/docs/v2-compatibility-audit.md +0 -228
- package/.framework/mcp/index.ts +0 -133
- package/.framework/mcp/load-env.ts +0 -60
- package/.framework/mcp/shared/app-manifest.ts +0 -275
- package/.framework/mcp/shared/db.ts +0 -477
- package/.framework/mcp/shared/kb.ts +0 -118
- package/.framework/mcp/tools/architect.ts +0 -431
- package/.framework/mcp/tools/cil/context.ts +0 -345
- package/.framework/mcp/tools/cil/graph.ts +0 -242
- package/.framework/mcp/tools/cil/ingest.ts +0 -1217
- package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +0 -693
- package/.framework/mcp/tools/cil/pipeline.ts +0 -767
- package/.framework/mcp/tools/cil/project.ts +0 -791
- package/.framework/mcp/tools/cil/retrieval.ts +0 -336
- package/.framework/mcp/tools/cil/search-core.ts +0 -454
- package/.framework/mcp/tools/cil/search.ts +0 -97
- package/.framework/mcp/tools/cil/suggest.ts +0 -186
- package/.framework/mcp/tools/cil/validate.ts +0 -1369
- package/.framework/mcp/tools/kb/delete.ts +0 -139
- package/.framework/mcp/tools/kb/index.ts +0 -52
- package/.framework/mcp/tools/kb/ingest.ts +0 -84
- package/.framework/mcp/tools/kb/list-types.ts +0 -102
- package/.framework/mcp/tools/kb/publish.ts +0 -100
- package/.framework/mcp/tools/kb/reindex.ts +0 -86
- package/.framework/mcp/tools/kb/revoke.ts +0 -85
- package/.framework/mcp/tools/kb/search.ts +0 -101
- package/.framework/mcp/tools/kb/upsert.ts +0 -125
- package/.framework/mcp/tools/observability.ts +0 -510
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: account-nodes
|
|
3
|
+
audience: both
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/account-nodes.ts
|
|
9
|
+
- .framework/functions/_shared/middleware.ts
|
|
10
|
+
- .framework/public/_redirects
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# account-nodes
|
|
14
|
+
|
|
15
|
+
## What it does
|
|
16
|
+
|
|
17
|
+
Returns one account ("node") plus its position in the account hierarchy:
|
|
18
|
+
ancestors, direct children, and full descendants. Used to render account
|
|
19
|
+
trees and breadcrumb navigation.
|
|
20
|
+
|
|
21
|
+
## Route and methods
|
|
22
|
+
|
|
23
|
+
`/.netlify/functions/account-nodes`, reachable as
|
|
24
|
+
`GET /api/account-nodes?node_id=<uuid>` (`/api/*` →
|
|
25
|
+
`/.netlify/functions/:splat`). The handler performs no method check — any
|
|
26
|
+
HTTP method with the query parameter works.
|
|
27
|
+
|
|
28
|
+
## Authentication
|
|
29
|
+
|
|
30
|
+
Standard `createHandler`: `x-api-key`, internal secret, or
|
|
31
|
+
`Bearer <jwt>` required; anonymous is rejected with 401. Queries run on
|
|
32
|
+
`ctx.db`, so human callers see only what row-level security allows; machine
|
|
33
|
+
and system principals use the service role.
|
|
34
|
+
|
|
35
|
+
## Request contract
|
|
36
|
+
|
|
37
|
+
One query parameter:
|
|
38
|
+
|
|
39
|
+
- `node_id` — required, the `accounts.id` to center the lookup on.
|
|
40
|
+
|
|
41
|
+
## Response contract
|
|
42
|
+
|
|
43
|
+
`{ node, ancestors, children, descendants }` inside the standard
|
|
44
|
+
`{ data, error, meta }` envelope:
|
|
45
|
+
|
|
46
|
+
- `node` — the account row's embedded `type` (`{ type: { id, slug, name,
|
|
47
|
+
icon, color } }`); the account's own columns are not selected.
|
|
48
|
+
- `ancestors` — rows returned by the `get_account_ancestors` RPC, else `[]`.
|
|
49
|
+
- `children` — active `accounts` rows with `parent_id = node_id`, each
|
|
50
|
+
shaped `{ type: {...} }`, ordered by `display_name`.
|
|
51
|
+
- `descendants` — rows returned by the `get_account_descendants` RPC, else `[]`.
|
|
52
|
+
|
|
53
|
+
## Side effects
|
|
54
|
+
|
|
55
|
+
None — read-only. A `request.get` span is emitted by the middleware like
|
|
56
|
+
every `createHandler` call.
|
|
57
|
+
|
|
58
|
+
## Errors
|
|
59
|
+
|
|
60
|
+
| Condition | HTTP | Message |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| Missing `node_id` | 500 | `node_id is required` |
|
|
63
|
+
| No active account with that id | 500 | `Account node not found` |
|
|
64
|
+
| RPC/query failure | 500 | underlying PostgREST message |
|
|
65
|
+
|
|
66
|
+
## Agent notes
|
|
67
|
+
|
|
68
|
+
- The `select` on `node` and `children` contains only the embedded `type`
|
|
69
|
+
relation — do not expect `display_name`, `slug`, or other account
|
|
70
|
+
columns on those payloads; fetch the account itself via
|
|
71
|
+
`/api/admin-data?entity=accounts&id=…` when you need them.
|
|
72
|
+
- `ancestors`/`descendants` come from database functions; their row shape
|
|
73
|
+
is whatever the RPC returns, not the `{ type }` shape used for `node`.
|
|
74
|
+
- Inactive accounts are filtered from `node` and `children`
|
|
75
|
+
(`is_active = true`) but RPC results are whatever the functions return.
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: admin-data
|
|
3
|
+
audience: both
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-11-01
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/admin-data.ts
|
|
9
|
+
- .framework/functions/_shared/middleware.ts
|
|
10
|
+
- .framework/functions/_shared/permissions.ts
|
|
11
|
+
- .framework/functions/_shared/access.ts
|
|
12
|
+
- .framework/functions/_shared/security.ts
|
|
13
|
+
- .framework/public/_redirects
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# admin-data
|
|
17
|
+
|
|
18
|
+
## What it does
|
|
19
|
+
|
|
20
|
+
`admin-data` is the generic CRUD endpoint for Spine's runtime and config
|
|
21
|
+
tables. One function exposes list, get, stats, create, update, and remove
|
|
22
|
+
across a fixed allowlist of entities, with record-permission evaluation,
|
|
23
|
+
field-level sanitization, type-schema stamping, and trigger firing built in.
|
|
24
|
+
Custom apps are expected to use this endpoint for all runtime-record CRUD
|
|
25
|
+
rather than wrapping it.
|
|
26
|
+
|
|
27
|
+
## Route and methods
|
|
28
|
+
|
|
29
|
+
`/.netlify/functions/admin-data`, reachable publicly as `/api/admin-data`
|
|
30
|
+
(`/api/*` is redirected to `/.netlify/functions/:splat` in
|
|
31
|
+
`.framework/public/_redirects`).
|
|
32
|
+
|
|
33
|
+
Dispatch is by `?action=` plus HTTP method (`.framework/functions/admin-data.ts`
|
|
34
|
+
handler):
|
|
35
|
+
|
|
36
|
+
| Call | Action |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `GET /api/admin-data?action=list&entity=…` | `list` |
|
|
39
|
+
| `GET /api/admin-data?action=get&entity=…&id=…` | `get` |
|
|
40
|
+
| `GET /api/admin-data?action=stats&entity=…` | `stats` |
|
|
41
|
+
| `POST /api/admin-data?action=create` | `create` |
|
|
42
|
+
| `PATCH /api/admin-data?action=update&entity=…&id=…` | `update` |
|
|
43
|
+
| `DELETE /api/admin-data?action=remove&entity=…&id=…` | `remove` |
|
|
44
|
+
|
|
45
|
+
Without a recognized `action` (absent or unmatched), the HTTP method alone
|
|
46
|
+
dispatches: `GET` → `get` when `id` is present else `list`, `POST` →
|
|
47
|
+
`create`, `PATCH` → `update`, `DELETE` → `remove`. A `?method=` query
|
|
48
|
+
parameter overrides the HTTP method for dispatch (the middleware copies
|
|
49
|
+
`event.httpMethod` into `query.method` only when the query did not set
|
|
50
|
+
it). A matched action with the wrong method (e.g. `action=list` on `POST`)
|
|
51
|
+
falls through to `Invalid action or method`.
|
|
52
|
+
|
|
53
|
+
## Authentication
|
|
54
|
+
|
|
55
|
+
Standard `createHandler` authentication: `x-api-key` (machine), internal
|
|
56
|
+
invoke secret (system), or `Authorization: Bearer <jwt>` (human). Anonymous
|
|
57
|
+
requests are rejected with 401 before the handler runs.
|
|
58
|
+
|
|
59
|
+
- Humans query through a JWT-scoped client (`ctx.db`), so row-level security
|
|
60
|
+
applies. Machines, anonymous, and system principals get the service-role
|
|
61
|
+
client — endpoint-level checks carry the weight.
|
|
62
|
+
- `list` switches to `adminDb` when `principal.isInternal` or the principal
|
|
63
|
+
is a system admin; otherwise it uses `ctx.db`.
|
|
64
|
+
- `create` calls `resolveFirstSurfacePermissions(personId, accountId,
|
|
65
|
+
typeSlug, 'create', designSchema)` — a machine whose scopes contain `*:*`
|
|
66
|
+
bypasses (`isMachineGlobal`); a `*:*` machine also resolves as
|
|
67
|
+
`system_admin`.
|
|
68
|
+
- `update` runs `validateUpdatePermissions` and lets system admins through
|
|
69
|
+
regardless of the result.
|
|
70
|
+
- `remove` calls `resolveFirstSurfacePermissions(..., 'delete',
|
|
71
|
+
existing.design_schema)` with a system-admin bypass.
|
|
72
|
+
- Non-internal audiences never see `visibility: 'internal'` threads, and
|
|
73
|
+
internal messages on external threads are projected to label-only
|
|
74
|
+
skeletons via `projectMessageForCustomer`.
|
|
75
|
+
|
|
76
|
+
## Request contract
|
|
77
|
+
|
|
78
|
+
`entity` is required on every call and must be one of `VALID_ENTITIES`:
|
|
79
|
+
`accounts`, `people`, `items`, `threads`, `messages`, `links`,
|
|
80
|
+
`attachments`, `watchers`, `item_progress`, `logs`, `test_runs`,
|
|
81
|
+
`observability`, `test_cases`, `types`, `link_types`, `ai_agents`,
|
|
82
|
+
`prompt_configs`.
|
|
83
|
+
|
|
84
|
+
**list** (query params): `entity`, `search` (ILIKE against a per-entity
|
|
85
|
+
field — e.g. `title` for items, `full_name` for people, `message` for
|
|
86
|
+
logs), `sort_field` (default `created_at`), `sort_direction` (`desc`
|
|
87
|
+
default), `limit` (default 50), `offset` (default 0), `type_slug`,
|
|
88
|
+
`type_slugs` (comma-separated, resolved through `ENTITY_TO_KIND` to
|
|
89
|
+
`type_id` filters), `view`, `flag_only` / `exclude_flag` (filter on
|
|
90
|
+
`data->><flag>` for `items`, `threads`, `messages`, `links`, `attachments`,
|
|
91
|
+
`watchers`). Every other query parameter becomes an `.eq(key, value)`
|
|
92
|
+
filter, with boolean coercion for `is_active`, `is_verified`, `is_primary`.
|
|
93
|
+
Soft-deleted rows are hidden by default — pass `is_active=false` or
|
|
94
|
+
`include_inactive=true` to include them. The default `is_active` filter
|
|
95
|
+
only applies to tables that have the column; `links`, `attachments`,
|
|
96
|
+
`watchers`, `logs`, and `observability` skip it.
|
|
97
|
+
|
|
98
|
+
**get** (query params): `entity`, `id`, `view`, `lineage` — for `items`,
|
|
99
|
+
parent/child lineage is attached unless `lineage=0`.
|
|
100
|
+
|
|
101
|
+
**stats** (query params): `entity` only.
|
|
102
|
+
|
|
103
|
+
**create** (body): `entity` (or `?entity=`), `type_slug` or `type_id` —
|
|
104
|
+
required for every non-raw entity; `account_id` (optional override);
|
|
105
|
+
arbitrary record fields. `design_schema`, `validation_schema`, and `id` in
|
|
106
|
+
the body are stripped. `thread_id`, `target_id`, or `parent_id` in the body
|
|
107
|
+
pull account and `scope` from the parent record.
|
|
108
|
+
|
|
109
|
+
**update** (query `entity` + `id`; body): record fields; `design_schema`,
|
|
110
|
+
`validation_schema`, `type_id`, `type_slug`, `id`, `entity` are stripped.
|
|
111
|
+
`account_id` is honored only for `entity=people`.
|
|
112
|
+
|
|
113
|
+
**remove** (query params): `entity`, `id`, `soft` (default `'true'`).
|
|
114
|
+
|
|
115
|
+
## Response contract
|
|
116
|
+
|
|
117
|
+
All responses use the middleware envelope:
|
|
118
|
+
`{ "data": <result>, "error": null, "meta": { "requestId", "duration" } }`
|
|
119
|
+
with HTTP 200. Errors return `{ "data": null, "error": "<message>" }` with
|
|
120
|
+
HTTP `err.statusCode || 500`.
|
|
121
|
+
|
|
122
|
+
- `list` → array of sanitized records; when both `view` and `type_slug`
|
|
123
|
+
are given and the type resolves, `{ data, schema, view }` (schema = the
|
|
124
|
+
type's `design_schema`, `view` = `schema.views[view]` or `null`). The
|
|
125
|
+
exact-count probe runs but the count is not returned.
|
|
126
|
+
- `get` → one sanitized record; `items` records additionally carry
|
|
127
|
+
`parents`, `children`, and `type_slug` fields from
|
|
128
|
+
`design_schema.functionality.lineage`. With `view`, returns
|
|
129
|
+
`{ data, schema, view }`.
|
|
130
|
+
- `stats` → `{ entity, count }`.
|
|
131
|
+
- `create` / `update` → the sanitized row.
|
|
132
|
+
- `remove` → `{ deleted: true, soft: true, data }` on soft delete, or
|
|
133
|
+
`{ deleted: true, soft: false }` on hard delete.
|
|
134
|
+
|
|
135
|
+
## Side effects
|
|
136
|
+
|
|
137
|
+
- Writes to the target table. On create, `design_schema` and
|
|
138
|
+
`validation_schema` are stamped onto the record from the resolved type
|
|
139
|
+
(missing `record_permissions` defaults to `PERMISSIONS_ALL` — all
|
|
140
|
+
actions for everyone), plus `created_by`, `created_at`, `updated_at`,
|
|
141
|
+
and `account_id` (except `accounts`, which gets `parent_id` instead).
|
|
142
|
+
`types` and `link_types` skip `created_by`/`updated_by` (`UNAUDITED_ENTITIES`).
|
|
143
|
+
- `scope: 'platform'` schemas force `account_id` to the `spine-system`
|
|
144
|
+
account; a `thread_id`/`target_id`/`parent_id` parent overrides both
|
|
145
|
+
account and scope.
|
|
146
|
+
- `fireCreateTriggers` runs after every create (including raw entities) and
|
|
147
|
+
`fireUpdateTriggers` after every update; trigger errors are logged, not
|
|
148
|
+
raised. **`fireDeleteTriggers` is imported but never called** — removing
|
|
149
|
+
a record fires no triggers.
|
|
150
|
+
- `logs`, `test_runs`, `observability`, `test_cases` (`RAW_ENTITIES`)
|
|
151
|
+
bypass type resolution entirely: create inserts a fixed column whitelist
|
|
152
|
+
(e.g. `level`+`message` required for `logs`, `suite` for `test_runs`,
|
|
153
|
+
`metric_name`+`metric_value` for `observability`, `slug`+`name` for
|
|
154
|
+
`test_cases`) and update skips the `updated_by`/`updated_at` stamps.
|
|
155
|
+
- `checkTypeSlugRateLimit` enforces the app's `rate_limits.<type_slug>_create`
|
|
156
|
+
policies on create.
|
|
157
|
+
- Every action is wrapped in `withAdminLogging`, which writes
|
|
158
|
+
admin-operation log rows (entity, action, method, duration, row count).
|
|
159
|
+
|
|
160
|
+
## Errors
|
|
161
|
+
|
|
162
|
+
| Condition | HTTP | Message |
|
|
163
|
+
|---|---|---|
|
|
164
|
+
| No/invalid `entity` | 400 (create) / 500 (others) | `Valid entity parameter is required` |
|
|
165
|
+
| Missing `id` on get/update/remove | 500 | `ID parameter is required` / `ID is required for update|delete` |
|
|
166
|
+
| Missing type on create | 400 | `type_id or type_slug is required — every runtime record must reference a type` |
|
|
167
|
+
| Bad `type_id` / inactive type | 500 | `type_id not found: …` / `type_id references an inactive type: …` |
|
|
168
|
+
| `type_slug` on an unmappable entity | 400 | `type_slug is not supported for entity: …` |
|
|
169
|
+
| Missing parent | 403 | `Parent record not found or not accessible` |
|
|
170
|
+
| Permission denied | 403 | `Insufficient permissions to create|update|delete this record` |
|
|
171
|
+
| Record missing or RLS-hidden | 500 | `Record not found or access denied` |
|
|
172
|
+
| Bad flag name | 400 | `Invalid flag field name` |
|
|
173
|
+
| Unknown action/method | 500 | `Invalid action or method` |
|
|
174
|
+
|
|
175
|
+
## Agent notes
|
|
176
|
+
|
|
177
|
+
- Always pass `type_slug` (not `type_id`) when creating runtime records —
|
|
178
|
+
the endpoint resolves it per `kind` via `ENTITY_TO_KIND`
|
|
179
|
+
(`items`→`item`, `threads`→`thread`, `messages`→`message`, `links`→`link`,
|
|
180
|
+
`attachments`→`attachment`, `watchers`→`watcher`, `accounts`→`account`,
|
|
181
|
+
`people`→`person`, `item_progress`→`item_progress`).
|
|
182
|
+
- Soft delete is the default and only applies to `accounts`, `people`,
|
|
183
|
+
`items`, `threads`, `messages`, `watchers`, `item_progress`,
|
|
184
|
+
`test_cases` — it sets `is_active=false`. Everything else hard-deletes
|
|
185
|
+
even without `soft=false`; pass `soft=false` explicitly when you mean it.
|
|
186
|
+
- Delete triggers do not exist in practice: `remove` never calls
|
|
187
|
+
`fireDeleteTriggers`, so automations keyed on delete events will not run.
|
|
188
|
+
- `?id=` is mirrored into the JSON body by the middleware before the
|
|
189
|
+
handler runs.
|
|
190
|
+
- Record reads are sanitized per principal: second/third-surface tables and
|
|
191
|
+
field-level `permissions` in `design_schema` shape what comes back, and a
|
|
192
|
+
principal with no account context gets only `id`/`created_at`/`updated_at`.
|
|
193
|
+
- A machine API key without `*:*` scope cannot create (the permission
|
|
194
|
+
lookup is by `people.id`, which machine keys don't have) — use `*:*`
|
|
195
|
+
keys for server-side writes, or a human JWT.
|
|
196
|
+
- `update` does not re-stamp `design_schema`; changing a record's type
|
|
197
|
+
is not possible through this endpoint (`type_id` is stripped).
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: admin-funnel
|
|
3
|
+
audience: developers, agents
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/admin-funnel.ts
|
|
9
|
+
- .framework/functions/_shared/middleware.ts
|
|
10
|
+
- .framework/functions/_shared/funnel.ts
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# admin-funnel
|
|
14
|
+
|
|
15
|
+
## What it does
|
|
16
|
+
|
|
17
|
+
Computes anonymous-visitor → identified-user → installed-instance
|
|
18
|
+
conversion metrics for the platform funnel. It reads `items` rows under
|
|
19
|
+
three app-seeded item types — `funnel_signal`, `anonymous_session`, and
|
|
20
|
+
`spine_instance` — and returns counts, conversion rates, and a
|
|
21
|
+
stage distribution over the last 30 days.
|
|
22
|
+
|
|
23
|
+
This is framework-internal telemetry for the platform operator (growth
|
|
24
|
+
and install funnel for Spine itself), not per-customer app analytics.
|
|
25
|
+
|
|
26
|
+
## Route and methods
|
|
27
|
+
|
|
28
|
+
**None — this file exports no `handler`.** It exports a single
|
|
29
|
+
`createHandler`-wrapped const, `metrics`, which nothing else imports or
|
|
30
|
+
dispatches. As shipped there is no HTTP entrypoint: the file is deployed
|
|
31
|
+
to the functions directory by the assembly script like every other
|
|
32
|
+
`.framework/functions` file, but Netlify has no `handler` export to
|
|
33
|
+
invoke. Treat it as a dormant module pending a dispatch handler (or a
|
|
34
|
+
wiring bug); do not cite it as a live endpoint.
|
|
35
|
+
|
|
36
|
+
## Authentication
|
|
37
|
+
|
|
38
|
+
If `metrics` were wired: `createHandler` requires any non-anonymous
|
|
39
|
+
principal — no role check inside the function. All queries run on
|
|
40
|
+
`adminDb` across **all accounts** (see Agent notes).
|
|
41
|
+
|
|
42
|
+
## Request contract
|
|
43
|
+
|
|
44
|
+
`metrics(ctx, body)` takes no parameters — the 30-day window is hardcoded.
|
|
45
|
+
|
|
46
|
+
## Response contract
|
|
47
|
+
|
|
48
|
+
`{ anonymous_visitors, identified_users, installed_instances,
|
|
49
|
+
conversion_rates: { anonymous_to_identified, identified_to_installed },
|
|
50
|
+
stage_distribution: [{ stage, count, percentage }] }`
|
|
51
|
+
|
|
52
|
+
- `anonymous_visitors` — `anonymous_session` items in the last 30 days
|
|
53
|
+
whose `data->lifecycle->>stitched_to_account_id` is null.
|
|
54
|
+
- `identified_users` — distinct `account_id`s on `funnel_signal` items in
|
|
55
|
+
the last 30 days.
|
|
56
|
+
- `installed_instances` — `spine_instance` items with a non-null
|
|
57
|
+
`data->>claimed_by_person_id` (all time).
|
|
58
|
+
- `stage_distribution` — counts of `funnel_signal.data->>stage` over the
|
|
59
|
+
last 30 days, only counting rows that have an `account_id`.
|
|
60
|
+
|
|
61
|
+
## Side effects
|
|
62
|
+
|
|
63
|
+
None — read-only aggregation.
|
|
64
|
+
|
|
65
|
+
## Errors
|
|
66
|
+
|
|
67
|
+
No explicit error paths. Missing signal types are expected on a clean
|
|
68
|
+
install (the types are seeded by installed apps): when none of the three
|
|
69
|
+
type slugs resolves, `metrics` returns a zeroed payload instead of
|
|
70
|
+
failing. Database errors would surface as thrown errors through
|
|
71
|
+
`createHandler`'s 500 path.
|
|
72
|
+
|
|
73
|
+
## Agent notes
|
|
74
|
+
|
|
75
|
+
- **Verify before use.** The missing `handler` export means there is no
|
|
76
|
+
routed endpoint; check the current tree before assuming
|
|
77
|
+
`GET /api/admin-funnel` does anything.
|
|
78
|
+
- **Unscoped platform-wide reads.** If wired as-is, every query runs on
|
|
79
|
+
`adminDb` with no `account_id` filter and no `isSystemAdmin` gate — any
|
|
80
|
+
authenticated principal would see cross-account funnel data. A
|
|
81
|
+
`system_admin` check belongs in any dispatch handler that exposes it.
|
|
82
|
+
- Signal data is written elsewhere — `emitSignal` in
|
|
83
|
+
`.framework/functions/_shared/funnel.ts` inserts typed signal items
|
|
84
|
+
(`funnel_signal` among them) and fires `item_created` triggers; these
|
|
85
|
+
admin modules only read the results.
|
|
86
|
+
- The `funnel_signal_use` type seed exists in the framework
|
|
87
|
+
(`.framework/seeds/types/funnel_signal_use.json`); the
|
|
88
|
+
`funnel_signal`/`anonymous_session`/`spine_instance` slugs the queries
|
|
89
|
+
look for are seeded by installed apps, hence the degrade-to-zeros
|
|
90
|
+
behavior.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: admin-install-funnel
|
|
3
|
+
audience: developers, agents
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/admin-install-funnel.ts
|
|
9
|
+
- .framework/functions/_shared/middleware.ts
|
|
10
|
+
- .framework/functions/_shared/funnel.ts
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# admin-install-funnel
|
|
14
|
+
|
|
15
|
+
## What it does
|
|
16
|
+
|
|
17
|
+
Install-funnel analytics for the platform operator: download/install/
|
|
18
|
+
first-use/claimed counts, per-day install trends over 30 days, recent
|
|
19
|
+
install records with contact and account joins, a version breakdown, and
|
|
20
|
+
a full export of install-instance records. All data comes from `items`
|
|
21
|
+
rows under the `funnel_signal` type where `data->>source = 'inst'`, plus
|
|
22
|
+
`spine_instance` rows for the version split.
|
|
23
|
+
|
|
24
|
+
Framework-internal telemetry about Spine installations (serials, app
|
|
25
|
+
versions, environments), not customer app data.
|
|
26
|
+
|
|
27
|
+
## Route and methods
|
|
28
|
+
|
|
29
|
+
**None — this file exports no `handler`.** It exports two
|
|
30
|
+
`createHandler`-wrapped consts, `metrics` and `exportData`, which nothing
|
|
31
|
+
imports or dispatches. As shipped there is no HTTP entrypoint; treat it
|
|
32
|
+
as a dormant module pending a dispatch handler (or a wiring bug), not a
|
|
33
|
+
live endpoint.
|
|
34
|
+
|
|
35
|
+
## Authentication
|
|
36
|
+
|
|
37
|
+
If wired: `createHandler` requires any non-anonymous principal — no role
|
|
38
|
+
check inside either export. All queries run on `adminDb` across **all
|
|
39
|
+
accounts** (see Agent notes).
|
|
40
|
+
|
|
41
|
+
## Request contract
|
|
42
|
+
|
|
43
|
+
Neither export reads parameters — `metrics` uses fixed windows and
|
|
44
|
+
`exportData` returns the full matching set.
|
|
45
|
+
|
|
46
|
+
## Response contract
|
|
47
|
+
|
|
48
|
+
`metrics` →
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"total_downloads": 0,
|
|
53
|
+
"unique_installers": 0,
|
|
54
|
+
"active_instances": 0,
|
|
55
|
+
"conversion_funnel": { "downloads": 0, "installations": 0, "first_use": 0, "claimed": 0 },
|
|
56
|
+
"install_trends": [{ "date": "YYYY-MM-DD", "downloads": 0, "installations": 0 }],
|
|
57
|
+
"recent_installs": [{ "id": "…", "serial": "…", "app_version": "…", "environment": "…",
|
|
58
|
+
"first_seen_at": "…", "last_signal_at": "…",
|
|
59
|
+
"claimed_by": "…", "claimed_at": "…",
|
|
60
|
+
"account_name": "…", "person_email": "…" }],
|
|
61
|
+
"top_versions": [{ "version": "…", "count": 0, "percentage": 0 }]
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Field semantics:
|
|
66
|
+
|
|
67
|
+
- `total_downloads` / `installations` / `first_use` — `funnel_signal`
|
|
68
|
+
rows with `action_type` `download` / `install` / `first_command`.
|
|
69
|
+
- `unique_installers` — distinct `data->>serial` values.
|
|
70
|
+
- `active_instances` — serialized signals seen in the last 7 days.
|
|
71
|
+
- `claimed` — signals with non-null `data->>claimed_by_person_id`.
|
|
72
|
+
- `install_trends` — 30 daily buckets of download + install counts.
|
|
73
|
+
- `recent_installs` — latest 20 serialized signals joined to
|
|
74
|
+
`people(email)` and `accounts(name)`.
|
|
75
|
+
- `top_versions` — up to 10 versions by `spine_instance` count share.
|
|
76
|
+
|
|
77
|
+
`exportData` → `{ instances, exported_at, total_count }` — every
|
|
78
|
+
`funnel_signal` row with `source='inst'` and a serial, full `data` bag,
|
|
79
|
+
`account_id`, and the same people/accounts joins, newest first.
|
|
80
|
+
|
|
81
|
+
## Side effects
|
|
82
|
+
|
|
83
|
+
None — read-only.
|
|
84
|
+
|
|
85
|
+
## Errors
|
|
86
|
+
|
|
87
|
+
No explicit error paths. When the `funnel_signal` type is absent
|
|
88
|
+
(app-seeded; missing on a clean install) `metrics` returns a zeroed
|
|
89
|
+
payload and `exportData` returns `{ instances: [], …, total_count: 0 }`.
|
|
90
|
+
The 30-day trend loop issues two count queries per day (60 sequential
|
|
91
|
+
queries) — slow but not an error.
|
|
92
|
+
|
|
93
|
+
## Agent notes
|
|
94
|
+
|
|
95
|
+
- **Verify before use** — no `handler` export means no routed endpoint;
|
|
96
|
+
`?action=export`-style dispatch does not exist in the file.
|
|
97
|
+
- **Platform-wide PII.** `exportData` and the `recent_installs` join
|
|
98
|
+
return `people.email` and `accounts.name` across all accounts via
|
|
99
|
+
`adminDb`, gated only by authentication if this is ever wired. Any
|
|
100
|
+
dispatch handler added later must gate on `system_admin` (or narrower)
|
|
101
|
+
before exposing these exports.
|
|
102
|
+
- The daily-trend loop is 60 round trips per call — do not put it on a
|
|
103
|
+
hot path; if the endpoint is wired for real use, fold the loop into a
|
|
104
|
+
single grouped query or RPC.
|
|
105
|
+
- `serial` is the installer-instance identity; `claimed_by_person_id`
|
|
106
|
+
links an install to a registered person — that join is what makes the
|
|
107
|
+
export PII-bearing.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: admin-signals
|
|
3
|
+
audience: developers, agents
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/admin-signals.ts
|
|
9
|
+
- .framework/functions/_shared/middleware.ts
|
|
10
|
+
- .framework/functions/_shared/funnel.ts
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# admin-signals
|
|
14
|
+
|
|
15
|
+
## What it does
|
|
16
|
+
|
|
17
|
+
Two read surfaces over `funnel_signal` items for the platform operator:
|
|
18
|
+
`metrics` aggregates signal totals (all-time, today, this week), a
|
|
19
|
+
conversion rate from high-value signals, top sources, and the 20 most
|
|
20
|
+
recent signals; `audit` lists individual signal records with filters,
|
|
21
|
+
pagination, attribution fields (UTM/referrer/anonymous identity), and an
|
|
22
|
+
unpaginated export mode.
|
|
23
|
+
|
|
24
|
+
Signals record product-journey events (`action_type`, `stage`, `source`,
|
|
25
|
+
`rating`) — framework-internal telemetry for the platform, not customer
|
|
26
|
+
app analytics.
|
|
27
|
+
|
|
28
|
+
## Route and methods
|
|
29
|
+
|
|
30
|
+
**None — this file exports no `handler`.** It exports `metrics` and
|
|
31
|
+
`audit`, both `createHandler`-wrapped, which nothing imports or
|
|
32
|
+
dispatches. As shipped there is no HTTP entrypoint; treat it as a dormant
|
|
33
|
+
module pending a dispatch handler, not a live endpoint.
|
|
34
|
+
|
|
35
|
+
## Authentication
|
|
36
|
+
|
|
37
|
+
If wired: `createHandler` requires any non-anonymous principal — no role
|
|
38
|
+
check in either export. All queries run on `adminDb` across **all
|
|
39
|
+
accounts** (see Agent notes).
|
|
40
|
+
|
|
41
|
+
## Request contract
|
|
42
|
+
|
|
43
|
+
`metrics` — no parameters; windows are hardcoded (today, trailing 7
|
|
44
|
+
days).
|
|
45
|
+
|
|
46
|
+
`audit` — query parameters:
|
|
47
|
+
|
|
48
|
+
| Param | Effect |
|
|
49
|
+
|---|---|
|
|
50
|
+
| `page` (default 1), `limit` (default 50) | Pagination (ignored in export mode) |
|
|
51
|
+
| `date_from`, `date_to` | `created_at` bounds |
|
|
52
|
+
| `source`, `stage`, `action_type` | Exact match on `data->>` fields |
|
|
53
|
+
| `min_rating` | `data->>rating >= value` |
|
|
54
|
+
| `account_id` | Caller-supplied account filter |
|
|
55
|
+
| `search` | OR-match on `accounts.name`, `people.email`, `data->attribution->>first_touch_referrer_domain` (ILIKE) |
|
|
56
|
+
| `export` (any value) | Returns the full formatted array with no range limit |
|
|
57
|
+
|
|
58
|
+
## Response contract
|
|
59
|
+
|
|
60
|
+
`metrics` → `{ total_signals, signals_today, signals_this_week,
|
|
61
|
+
conversion_rate, top_sources: [{source, count}], recent_signals: [{id,
|
|
62
|
+
action_type, source, stage, created_at, account_name, rating}] }`.
|
|
63
|
+
`conversion_rate` is the share of this week's signals with
|
|
64
|
+
`data->>rating >= '4'` — a "high-value signal" rate, not a funnel
|
|
65
|
+
conversion.
|
|
66
|
+
|
|
67
|
+
`audit` → `{ signals, total, page, limit }`, or in export mode the bare
|
|
68
|
+
formatted array. Each signal row: `{ id, action_type, action_value,
|
|
69
|
+
stage, source, created_at, account_name, person_email, anonymous_id,
|
|
70
|
+
session_id, rating, raw_score, referrer_domain, utm_source, utm_medium,
|
|
71
|
+
utm_campaign }` — joined to `people(email)` and `accounts(name)`, with
|
|
72
|
+
identity fields from `data->identity` and attribution from
|
|
73
|
+
`data->attribution`.
|
|
74
|
+
|
|
75
|
+
## Side effects
|
|
76
|
+
|
|
77
|
+
None — read-only.
|
|
78
|
+
|
|
79
|
+
## Errors
|
|
80
|
+
|
|
81
|
+
- `Failed to fetch signals: <message>` — the one thrown error, on query
|
|
82
|
+
failure inside `audit`.
|
|
83
|
+
- Missing `funnel_signal` type (app-seeded; absent on clean installs)
|
|
84
|
+
degrades gracefully: `metrics` returns zeroed metrics, `audit` returns
|
|
85
|
+
an empty page or `[]` in export mode.
|
|
86
|
+
|
|
87
|
+
## Agent notes
|
|
88
|
+
|
|
89
|
+
- **Verify before use** — no `handler` export means no routed endpoint.
|
|
90
|
+
- **Cross-account PII, caller-steered.** `audit` accepts an arbitrary
|
|
91
|
+
`account_id` filter and returns `people.email`, `accounts.name`,
|
|
92
|
+
anonymous IDs, session IDs, and UTM attribution across all accounts via
|
|
93
|
+
`adminDb`. If a dispatch handler is ever added, it needs a
|
|
94
|
+
`system_admin` gate before this surface is exposed.
|
|
95
|
+
- `export` mode drops the range limit — it returns the entire filtered
|
|
96
|
+
set, so combined with the joins it is the heaviest call in the file.
|
|
97
|
+
- Rating comparisons are string comparisons on `data->>rating`
|
|
98
|
+
(`gte '4'`) — works for single digits, but `"10" >= "4"` is false
|
|
99
|
+
lexicographically; values above 9 would misclassify.
|
|
100
|
+
- Signals are produced by `emitSignal` in
|
|
101
|
+
`.framework/functions/_shared/funnel.ts` (deduped once per account +
|
|
102
|
+
`action_type` by default) — this module only reads them.
|