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,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Docs contract — how the instruction book is written, tested, and kept true"
|
|
3
|
+
audience: developers, agents
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: b3b3ae0
|
|
6
|
+
verified_on: 2026-11-01
|
|
7
|
+
source_files:
|
|
8
|
+
- scripts/docs-lint.mts
|
|
9
|
+
- .framework/src/instructions/README.md
|
|
10
|
+
- docs/audit/clean-room-walkthrough.md
|
|
11
|
+
- .framework/cli/commands/install-app.ts
|
|
12
|
+
- .framework/cli/commands/validate-app.ts
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Docs contract
|
|
16
|
+
|
|
17
|
+
This file is the editorial contract for the Spine instruction book and for
|
|
18
|
+
the instruction books that installed apps carry at
|
|
19
|
+
`custom/apps/<slug>/src/instructions/`. It lives under `internal/` because it
|
|
20
|
+
governs the book itself rather than describing the framework.
|
|
21
|
+
|
|
22
|
+
## The test rule
|
|
23
|
+
|
|
24
|
+
Every miss is a documentation bug — fix the book, not the test.
|
|
25
|
+
|
|
26
|
+
The book must carry a cold agent — one with zero memory of this codebase —
|
|
27
|
+
through installation, orientation, and building on Spine without touching
|
|
28
|
+
monorepo-only material. If an agent has to grep `.framework/functions/` to
|
|
29
|
+
answer a customer-facing question, or has to ask the user something that
|
|
30
|
+
isn't a real-world checkpoint (credentials, first-admin choice), the book is
|
|
31
|
+
missing a page, a section, or a line. Write it. Do not explain around the
|
|
32
|
+
gap in the moment and move on.
|
|
33
|
+
|
|
34
|
+
Corollaries:
|
|
35
|
+
|
|
36
|
+
- Retrieval beats derivation. A fact an agent can *find* is safe; a fact it
|
|
37
|
+
has to *derive* from source is a latent bug.
|
|
38
|
+
- Code is truth; docs are interpretation. When they disagree, the doc is
|
|
39
|
+
wrong — fix the doc and bump `verified_against`/`verified_on`.
|
|
40
|
+
- "It figured it out eventually" is a fail. Time-to-answer is the metric.
|
|
41
|
+
|
|
42
|
+
## The capture contract
|
|
43
|
+
|
|
44
|
+
A code change is **incomplete until the docs covering it are re-synced**.
|
|
45
|
+
Docs are part of the change, not a follow-up chore. One rule, two layers:
|
|
46
|
+
|
|
47
|
+
**Core.** Every book page lists `source_files` in its frontmatter — the code
|
|
48
|
+
files whose behavior the page interprets. Editing any file in that set
|
|
49
|
+
requires re-verifying every page that lists it, in the same change, and
|
|
50
|
+
bumping `verified_against` (commit short hash) and `verified_on` (date).
|
|
51
|
+
`npm run docs:lint` is the gate.
|
|
52
|
+
|
|
53
|
+
**Custom.** Editing anything under `custom/apps/<slug>/` requires updating
|
|
54
|
+
that app's `src/instructions/` in the same change:
|
|
55
|
+
|
|
56
|
+
- New or changed page/route → the app's tasks doc and README route map.
|
|
57
|
+
- New or changed function → the app's `reference/functions` page.
|
|
58
|
+
- New or changed seeded type → the app's entity page for that type.
|
|
59
|
+
- New skill → the app's README skills list.
|
|
60
|
+
|
|
61
|
+
**Capture, not just sync.** Non-obvious decisions, cautions, and traps
|
|
62
|
+
discovered while doing the work are appended to the relevant page's
|
|
63
|
+
`## Agent notes` section — the next cold agent reads what the last one
|
|
64
|
+
learned. Positive-only corrections: update the source; never append
|
|
65
|
+
"correction notes" that contradict an untouched stale claim.
|
|
66
|
+
|
|
67
|
+
### Relationship to CIL
|
|
68
|
+
|
|
69
|
+
CIL (Code Intelligence Layer, the `cil_*` tooling used inside this monorepo)
|
|
70
|
+
is **bottom-up code truth**: per-chunk contracts, `stored_hash` drift
|
|
71
|
+
detection, a call/import/test graph, semantic code search. The book is
|
|
72
|
+
**top-down narrative comprehension**: curated prose at system granularity.
|
|
73
|
+
|
|
74
|
+
Neither replaces the other:
|
|
75
|
+
|
|
76
|
+
- CIL can *prove* a doc is stale (hash recompute); the book can only *assert*
|
|
77
|
+
freshness (`verified_*` stamps + lint gate). The capture contract above is
|
|
78
|
+
the manual stand-in for CIL's automatic re-sync pipeline.
|
|
79
|
+
- The book carries CIL's *purpose* into shipped projects where no `cil_*`
|
|
80
|
+
tooling runs — it is the portable distillation, not a replacement.
|
|
81
|
+
|
|
82
|
+
The definition of done for `.framework/` edits in this repo is therefore
|
|
83
|
+
**both**: CIL re-sync (`cil_store_markers` → `cil_generate_embeddings` →
|
|
84
|
+
`cil_generate_mappings` → `cil_validate_file`) for code truth, and book
|
|
85
|
+
re-verify for narrative truth. Mechanical pages (`reference/functions/*`,
|
|
86
|
+
entity field tables) may eventually be generated/verified from CIL chunks;
|
|
87
|
+
narrative layers stay hand-authored.
|
|
88
|
+
|
|
89
|
+
## Layer taxonomy — which layer answers which question
|
|
90
|
+
|
|
91
|
+
| Layer | Kind | Answers | Mandatory shape |
|
|
92
|
+
|---|---|---|---|
|
|
93
|
+
| `guide/` | guide | "Walk me through it end to end" | Numbered sequence (`00` → `05`); each step verifiable |
|
|
94
|
+
| `concepts/` | concept | "Why is it like this? What's the mental model?" | Model first, mechanics second; links to tasks |
|
|
95
|
+
| `tasks/` | task | "How do I do X?" | `## Goal` → `## Steps` → `## Verify` → `## Common mistakes` → `## Reference` |
|
|
96
|
+
| `reference/` | reference | "What's the exact spelling/shape?" | Tables over prose; every row verifiable against `source_files` |
|
|
97
|
+
| Entity pages (root) | entity | "What is this thing and how do I configure it?" | The 7-section template (enforced by docs:lint) |
|
|
98
|
+
| `operations/` | ui | "How do I run/diagnose it in the admin UI?" | Runbook steps anchored to real pages/endpoints |
|
|
99
|
+
| `internal/` | * | "How is the book itself maintained?" | Repo-only; exempt from archived-docs and cil_* reference rules |
|
|
100
|
+
|
|
101
|
+
Frontmatter on every page: `title`, `audience`, `kind`,
|
|
102
|
+
`verified_against`, `verified_on`, `source_files` (list). Exemption:
|
|
103
|
+
`status: LEGACY_UNVERIFIED` — still checked for links and paths.
|
|
104
|
+
|
|
105
|
+
## Skills
|
|
106
|
+
|
|
107
|
+
Invocable procedures live in `.devin/skills/spine-*` (framework-owned,
|
|
108
|
+
dir-replaced on upgrade) and `custom/.devin/skills/<prefix>_*`
|
|
109
|
+
(app-owned — see below). A task doc should name its companion skill where
|
|
110
|
+
one exists; a skill should link its task/reference doc.
|
|
111
|
+
|
|
112
|
+
**App-shipped skills.** An installed app may ship skills under
|
|
113
|
+
`agent/skills/<prefix>_*/` in its package; `install-app` copies them to
|
|
114
|
+
`custom/.devin/skills/` and `uninstall-app` removes `custom/.devin/skills/<prefix>_*`.
|
|
115
|
+
`manifest.agent.skill_prefix` is required when `agent/skills/` exists and
|
|
116
|
+
must match `^[a-z][a-z0-9]{0,7}$` and be unique across installed apps.
|
|
117
|
+
See [App manifest reference](../reference/app-manifest.md) and
|
|
118
|
+
[Document your app](../tasks/document-your-app.md).
|
|
119
|
+
|
|
120
|
+
## Acceptance matrix — the regression suite
|
|
121
|
+
|
|
122
|
+
The cold-agent test protocol lives in the repo at
|
|
123
|
+
`docs/audit/clean-room-walkthrough.md`. Each row is a verbatim prompt; the
|
|
124
|
+
pages listed must satisfy it **alone**. New pages earn a row; a failing row
|
|
125
|
+
means the book regressed, not that the agent did.
|
|
126
|
+
|
|
127
|
+
| # | Verbatim prompt (abridged) | Pages that must carry it |
|
|
128
|
+
|---|---|---|
|
|
129
|
+
| T1 | Explain this project: what's mine vs framework, how do primitives compose, where does my code go, what do I never touch | `guide/00-orientation.md`, `guide/02-project-map.md`, `concepts/core-vs-custom.md`, `concepts/spine-way.md` |
|
|
130
|
+
| T2 | Create a design_schema for a "task" type (fields, selects, permissions, list+detail views) and validate it | `tasks/add-a-type.md`, `reference/design-schema.md`, `concepts/design-schema.md`; skill `spine_schema_validate`/`spine-schema` |
|
|
131
|
+
| T3 | Create the type and register a generic app at /taskboard that lists it — visible in the UI | `tasks/register-an-app.md`, `apps.md`, `reference/app-manifest.md`, `concepts/apps-and-renderers.md` |
|
|
132
|
+
| T4 | Add a custom page that lists tasks with a working create form — no server-side code | `tasks/build-a-custom-page.md`, `reference/frontend-sdk.md`, `reference/api/admin-data.md` |
|
|
133
|
+
| T5 | Wire a pipeline on task create + a 2-minute timer; show a completed execution | `tasks/add-a-pipeline.md`, `tasks/add-a-trigger.md`, `tasks/add-a-timer.md`, `timers.md` |
|
|
134
|
+
| T6 | Add an AI agent with its own prompt config and send a test message | `tasks/add-an-ai-agent.md`, `ai-agents.md`, `prompts.md` |
|
|
135
|
+
| T7 | Package this app; install it into another Spine project; show create/list working | `tasks/package-and-install-an-app.md`, `reference/app-manifest.md`, `reference/seeds.md`; skill `spine-install-app` |
|
|
136
|
+
| T8 | Something broke — creating a task fails / the pipeline fails. Find and fix it | `operations/troubleshooting.md`, `pipeline-executions.md`, `logs.md`, `deliveries.md`; skill `spine-troubleshoot` |
|
|
137
|
+
| T9 | (cold) Create a type, register an app, wire a pipeline — from docs alone | The whole book end to end |
|
|
138
|
+
| M1 | Mount this app at the site root (served from `/`) | `tasks/register-an-app.md`, `apps.md`, `reference/app-manifest.md`, `concepts/apps-and-renderers.md` |
|
|
139
|
+
| M2 | What can I import in a custom app? Build a page using the framework's UI kit | `reference/frontend-sdk.md`, `tasks/build-a-custom-page.md` |
|
|
140
|
+
| M3 | Install this app, then use its shipped skill and its instruction book | `tasks/package-and-install-an-app.md`, `reference/app-manifest.md` (`agent` block), app `src/instructions/` |
|
|
141
|
+
| M4 | You changed file X — which docs did you update? | This contract (capture section), `AGENTS.md` verify rules |
|
|
142
|
+
|
|
143
|
+
## Authoring checklist for a new page
|
|
144
|
+
|
|
145
|
+
- [ ] Correct `kind` + full frontmatter; `source_files` lists the real files
|
|
146
|
+
that produce the page's truth.
|
|
147
|
+
- [ ] Placed in the layer whose question it answers (taxonomy table above).
|
|
148
|
+
- [ ] Linked from `README.md`'s index and from at least one sibling page.
|
|
149
|
+
- [ ] A row in the acceptance matrix if it covers a cold-agent task.
|
|
150
|
+
- [ ] `npm run docs:lint` clean.
|
|
151
|
+
|
|
152
|
+
## The gate
|
|
153
|
+
|
|
154
|
+
`npm run docs:lint` (`scripts/docs-lint.mts`, repo-only) checks: required
|
|
155
|
+
frontmatter, kind taxonomy, the 7-section headings on `entity`/`ui` pages,
|
|
156
|
+
relative-link resolution, existence of every backticked `.framework/...`
|
|
157
|
+
path, and (outside `internal/`) no `docs/archive` references and no `cil_*`
|
|
158
|
+
tool references.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Invites & Access
|
|
3
|
+
audience: both
|
|
4
|
+
kind: entity
|
|
5
|
+
verified_against: e13127d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/src/pages/admin/InvitesPage.tsx
|
|
9
|
+
- .framework/src/components/shared/InviteManager.tsx
|
|
10
|
+
- .framework/functions/invites.ts
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## What this is for
|
|
14
|
+
|
|
15
|
+
**Invites & Access** (`/spine-framework/admin/access/invites`) manages how people join the account: generating invite links, tracking their status, re-sending or revoking them, and approving access requests. The admin page is scoped to the `system_admin` role; the shared `InviteManager` component underneath supports other role sets.
|
|
16
|
+
|
|
17
|
+
## Fields and settings
|
|
18
|
+
|
|
19
|
+
- **Generate invite form** — name, email, and role picker (shown only when more than one role is allowed; here it is fixed to `system_admin`). Produces an `invite_url` copied to the clipboard: `{APP_BASE_URL}/register?t=<invite_token>`.
|
|
20
|
+
- **Invites table** — the person's name/email, assigned role, status, and actions: reinvite (new link), revoke, approve (access requests), block.
|
|
21
|
+
- **Status semantics** — `invited`/`pending` (link issued or request awaiting approval), `approved`, `active`/`claimed` (registered), `revoked` (`inactive`), `expired`, `blocked`. `kind` distinguishes `invite` from `access_request`.
|
|
22
|
+
- **Search + sortable columns** over the list from `action=list-invites` (filtered to the allowed role slugs).
|
|
23
|
+
|
|
24
|
+
**Downstream effects**
|
|
25
|
+
|
|
26
|
+
- `create-invite` inserts a stub `people` row carrying `invite_token` (16 chars) and `invite_expires_at` (7 days); the register flow consumes the token via `complete-registration` → `complete_registration_placement` RPC.
|
|
27
|
+
- Only `system_admin` (or an `*_admin` role on the account) can create invites; creating an invite for `role_slug: system_admin` is refused unless the caller is already `system_admin`.
|
|
28
|
+
- `revoke-invite` marks the person `inactive`, killing the unclaimed link; `reinvite` mints a fresh token/link. `block-invite`/`deny` handle abusive requests.
|
|
29
|
+
- Registration and account-creation calls are rate-limited per person and per IP.
|
|
30
|
+
|
|
31
|
+
## Minimal configuration
|
|
32
|
+
|
|
33
|
+
Fill name + email → Generate → the invite URL lands on your clipboard; send it out of band. The invitee registers within 7 days or you reinvite.
|
|
34
|
+
|
|
35
|
+
## Full configuration
|
|
36
|
+
|
|
37
|
+
The backing API (`/.netlify/functions/invites?action=`) also covers the public registration path and member management:
|
|
38
|
+
|
|
39
|
+
- `check-first-user`, `check-domain`, `validate-token`, `complete-registration` — anonymous signup flow
|
|
40
|
+
- `create-invite`, `list-invites`, `revoke-invite`, `reinvite`, `block-invite`
|
|
41
|
+
- `approve`, `deny` — access-request review
|
|
42
|
+
- `list-members`, `update-member`, `deactivate-member` — existing members
|
|
43
|
+
|
|
44
|
+
## Example
|
|
45
|
+
|
|
46
|
+
An admin generates a `system_admin` invite for a new hire, pastes the link into chat. Until they register, the row shows `pending`; if the link is lost, Reinvite mints a new URL and invalidates the old token; if they never join, Revoke marks the stub `inactive`.
|
|
47
|
+
|
|
48
|
+
## Core files
|
|
49
|
+
|
|
50
|
+
- `.framework/src/pages/admin/InvitesPage.tsx` — this page (scopes InviteManager to `system_admin`)
|
|
51
|
+
- `.framework/src/components/shared/InviteManager.tsx` — invite list/form/actions (reusable in other apps)
|
|
52
|
+
- `.framework/functions/invites.ts` — API (all actions above)
|
|
53
|
+
- Table: `public.people` (invite stub rows carry `invite_token`/`invite_expires_at`)
|
|
54
|
+
|
|
55
|
+
## Agent notes
|
|
56
|
+
|
|
57
|
+
- The invite URL is shown/copied **once** at create/reinvite — the API never returns it again; a lost link means Reinvite, not retrieve.
|
|
58
|
+
- Invites expire after 7 days (`invite_expires_at`); expired rows show `expired` until reinvited or revoked.
|
|
59
|
+
- This admin page is hardcoded to `system_admin` invites; app-level member invites use the same `InviteManager` with a different `allowedRoles` set.
|
|
60
|
+
- Stub invite rows are real `people` rows — they are excluded from normal principal resolution until registration completes.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Knowledge Base
|
|
3
|
+
audience: both
|
|
4
|
+
kind: entity
|
|
5
|
+
verified_against: e13127d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/src/pages/admin/kb/KBAdminPage.tsx
|
|
9
|
+
- .framework/src/components/kb/KBArticleList.tsx
|
|
10
|
+
- .framework/src/components/kb/KBArticleEditor.tsx
|
|
11
|
+
- .framework/src/pages/admin/EmbeddingsPage.tsx
|
|
12
|
+
- .framework/src/pages/admin/EmbeddingDetailPage.tsx
|
|
13
|
+
- .framework/functions/kb.ts
|
|
14
|
+
- .framework/functions/embeddings.ts
|
|
15
|
+
- .framework/functions/admin-data.ts
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## What this is for
|
|
19
|
+
|
|
20
|
+
The **KB** section (`/spine-framework/admin/kb/*`) covers three surfaces that share this doc:
|
|
21
|
+
|
|
22
|
+
- `kb/:typeSlug` — the article admin list/editor (`KBAdminPage`) for the KB item types `kb-spine-admin`, `kb-spine-internal`, `kb-spine-public`, `kb-account`. Articles are `items` records created/edited via `admin-data` and published via `kb`.
|
|
23
|
+
- `kb/embeddings` — the embedding record browser (`EmbeddingsPage`): every generated embedding row with its approval state.
|
|
24
|
+
- `kb/embeddings/:id` — single embedding detail (`EmbeddingDetailPage`): content, vector type, chunk index, model, approval/revocation timestamps, optional raw vector.
|
|
25
|
+
|
|
26
|
+
## Fields and settings
|
|
27
|
+
|
|
28
|
+
**Articles** (`KBArticleList` / `KBArticleEditor`)
|
|
29
|
+
|
|
30
|
+
- **Type slug tabs** — the four KB type slugs above; articles are `items` records of that type.
|
|
31
|
+
- **List** — title, status, created date; search hits `/api/kb?action=search` (sources `['kb']`).
|
|
32
|
+
- **Editor** — schema-driven form from the type's `design_schema` (main fields + metadata sidebar); Save writes `admin-data` create/update; **Publish** additionally calls `/api/kb?action=publish`; Delete uses `admin-data` remove.
|
|
33
|
+
- **Modes** — `view` (read-only), `edit`, `new` via route (`:id`, `:id/edit`, `new`).
|
|
34
|
+
|
|
35
|
+
**Embeddings** (`EmbeddingsPage` / `EmbeddingDetailPage`)
|
|
36
|
+
|
|
37
|
+
- **List** — content/document/model search + sortable columns; status badge: `Revoked` (revoked_at set) > `Approved` (approved_at set) > `Pending`.
|
|
38
|
+
- **Detail** — document_id, vector_type, chunk_index, model_id, token count, content preview, approved/revoked timestamps, and a show-vector toggle for the raw embedding.
|
|
39
|
+
|
|
40
|
+
**Downstream effects**
|
|
41
|
+
|
|
42
|
+
- Articles are plain `items` records — they inherit the type's `record_permissions` and `validation_schema`; `kb?action=publish` handles the publishing step (ingest/indexing) on top.
|
|
43
|
+
- Embeddings are generated per type `design_schema.vector_types` (default `['semantic','structure']`) and feed `match_embeddings`/semantic search used by agents and `search_knowledge` pipeline stages.
|
|
44
|
+
- `embeddings` actions: list/get/create/update + `generate`, `revoke`, `approve`, `stats`, `batch-create`, `delete-document`, `cleanup`, `search-similar`, `search-semantic`. `revoke` stamps `revoked_at` (excluded from search); `approve` stamps `approved_at` and clears revocation.
|
|
45
|
+
|
|
46
|
+
## Minimal configuration
|
|
47
|
+
|
|
48
|
+
Pick a KB type slug tab → New → fill the schema form → Save → Publish. The article is now searchable via `kb?action=search`.
|
|
49
|
+
|
|
50
|
+
## Full configuration
|
|
51
|
+
|
|
52
|
+
```jsonc
|
|
53
|
+
// article write — POST /api/admin-data?entity=items
|
|
54
|
+
{ "type_id": "kb-account", "title": "Refund policy", "data": { "body": "..." } }
|
|
55
|
+
// publish — POST /api/kb?action=publish { "id": "<article uuid>" }
|
|
56
|
+
// regenerate vectors — POST /api/embeddings?action=generate { "item_id": "<article uuid>" }
|
|
57
|
+
// curate — POST /api/embeddings?action=approve|revoke { "id": "<embedding uuid>" }
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Example
|
|
61
|
+
|
|
62
|
+
A support team writes refund-policy articles under `kb-account`, publishes them, then checks `kb/embeddings` — the new chunks show `Pending` until reviewed; approving the good ones and revoking a stale `semantic` chunk changes what `search_knowledge` returns to the reply-drafting agent.
|
|
63
|
+
|
|
64
|
+
## Core files
|
|
65
|
+
|
|
66
|
+
- `.framework/src/pages/admin/kb/KBAdminPage.tsx` — article page shell (list vs editor by mode)
|
|
67
|
+
- `.framework/src/components/kb/KBArticleList.tsx` / `KBArticleEditor.tsx` — list + schema-driven editor, publish call
|
|
68
|
+
- `.framework/src/pages/admin/EmbeddingsPage.tsx` / `EmbeddingDetailPage.tsx` — embedding browser/detail
|
|
69
|
+
- `.framework/functions/kb.ts` — `search`, `publish`, and KB pipeline actions
|
|
70
|
+
- `.framework/functions/embeddings.ts` — embedding CRUD + generate/approve/revoke/search
|
|
71
|
+
- `.framework/functions/admin-data.ts` — the underlying record CRUD
|
|
72
|
+
- Tables: `public.items` (articles), `public.embeddings` (vectors)
|
|
73
|
+
|
|
74
|
+
## Agent notes
|
|
75
|
+
|
|
76
|
+
- `instructionKeyFor` maps every `kb/*` route to this doc — there is no separate `embeddings` page key.
|
|
77
|
+
- Articles are records, not files — edit them through this UI or `admin-data`, never direct table writes that skip validation/triggers.
|
|
78
|
+
- `Pending` embeddings still count toward `stats`; only `revoked_at` removes a vector from similarity search.
|
|
79
|
+
- Deleting an article does not cascade to its embedding rows — use `embeddings?action=delete-document` to clean up vectors for a removed document.
|
|
80
|
+
- Re-publish after large edits: publishing re-ingests; stale chunks otherwise linger until `generate`/`cleanup` runs.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Logs
|
|
3
|
+
audience: both
|
|
4
|
+
kind: entity
|
|
5
|
+
verified_against: e13127d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/src/pages/admin/LogsPage.tsx
|
|
9
|
+
- .framework/functions/logs.ts
|
|
10
|
+
- .framework/functions/_shared/logging.ts
|
|
11
|
+
- .framework/functions/system-cron.ts
|
|
12
|
+
- .framework/migrations/069_log_stats_window.sql
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## What this is for
|
|
16
|
+
|
|
17
|
+
**Logs** (`/spine-framework/admin/observability/logs`) is the account's audit-trail and event-history viewer — every `emitLog`/`emitAudit`/`emitSpan`/`emitActivity` write lands in the `logs` table and shows up here. Use it to trace a request end-to-end (via `trace_id`), answer "who did what when", and debug trigger/pipeline/delivery failures.
|
|
18
|
+
|
|
19
|
+
## Fields and settings
|
|
20
|
+
|
|
21
|
+
- **Stats cards** — Total Events, Event Types, Errors, User Actions over the current filter window (`/api/logs?action=stats`).
|
|
22
|
+
- **Search** — free text over message/actor fields; debounced 300ms.
|
|
23
|
+
- **Level** — `event_type` of the log row (error/audit/span/activity categories surface as selectable values from the stats payload).
|
|
24
|
+
- **Source / Source Type** — `action` and `target_type` of the row (e.g. `type.updated`, `item`).
|
|
25
|
+
- **Source ID / Actor ID / Trace ID** — UUID/prefix filters: `target_id`, `actor_id`, `trace_id`.
|
|
26
|
+
- **From / To dates** — `date_from`/`date_to`; defaults to the last 24h. Stats default to a 7-day window when no `date_from` is given.
|
|
27
|
+
- **Row click** — expands the row to show `details`, `metadata`, span info (`span_name`, `span_id`, `parent_span_id`, `duration_ms`, `outcome`).
|
|
28
|
+
- **Trace button** — clicking a `trace_id` filters the whole grid to that trace (all spans of one request).
|
|
29
|
+
- Paged at 100 rows per page; sortable columns (Time, Trace, Level, Source, Source Type, Source ID, Person, Message).
|
|
30
|
+
|
|
31
|
+
**Downstream effects**
|
|
32
|
+
|
|
33
|
+
- What gets written is gated by `system_settings` key `logging`: levels `off | timing | warnings | errors | all` (default `['timing','errors']`). Rows at unconfigured levels are dropped at write time — they never reach this page.
|
|
34
|
+
- Retention is `cleanup` with `days_to_keep` (default 30); `system-cron` runs `cleanup_old_logs` at 00:00 UTC. Debugging a week-old issue? Check before it ages out.
|
|
35
|
+
- `logs` is also a queryable `admin-data` entity, so pipelines and agents can read the same rows.
|
|
36
|
+
- The Observability dashboard and Security page both read this table (`threshold.breached` events, security signals).
|
|
37
|
+
|
|
38
|
+
## Minimal configuration
|
|
39
|
+
|
|
40
|
+
Nothing to configure on the page itself. To see useful data, ensure the account's `system_settings.logging.levels` includes the level you need (e.g. `errors`), then filter:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
Level = error · From = <incident date> · Trace ID = <trace from a failed request>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Full configuration
|
|
47
|
+
|
|
48
|
+
Combine all filters for a forensic query — e.g. every log row for one record by one person in a window:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
query = "ticket"
|
|
52
|
+
target_type = "items" · source_id = "<record uuid>"
|
|
53
|
+
actor_id = "<person uuid>" · trace_id = "<trace uuid>"
|
|
54
|
+
date_from = 2026-10-01 · date_to = 2026-10-30
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
API equivalents: `/api/logs?action=account` (paged rows), `action=stats`, plus `target`, `person`, `search`, `cleanup` actions server-side.
|
|
58
|
+
|
|
59
|
+
## Example
|
|
60
|
+
|
|
61
|
+
A user reports a failed save at 14:03. Open Logs, set From to that day, paste the `trace_id` from the error toast into Trace ID — the grid now shows the request's full span tree (`api.request` → `db.update` → `trigger.fired`), and the red `error` row's expanded `details` names the failing stage.
|
|
62
|
+
|
|
63
|
+
## Core files
|
|
64
|
+
|
|
65
|
+
- `.framework/src/pages/admin/LogsPage.tsx` — this page
|
|
66
|
+
- `.framework/functions/logs.ts` — API (`account`, `target`, `person`, `stats`, `search`, `cleanup` actions)
|
|
67
|
+
- `.framework/functions/_shared/logging.ts` — `emitLog`, level resolution from `system_settings`
|
|
68
|
+
- `.framework/functions/_shared/audit.ts` / `.framework/functions/_shared/tracing.ts` — `emitAudit`/`emitSpan` writers, W3C trace context
|
|
69
|
+
- `.framework/functions/system-cron.ts` — daily `cleanup_old_logs` at 00:00 UTC
|
|
70
|
+
- Table: `public.logs`
|
|
71
|
+
|
|
72
|
+
## Agent notes
|
|
73
|
+
|
|
74
|
+
- Empty page with active traffic almost always means log **levels** filter writes, not that nothing happened — check `system_settings.logging` first.
|
|
75
|
+
- `trace_id` is the fastest debugging primitive: every function request shares one, so one click reconstructs the whole call including trigger dispatch.
|
|
76
|
+
- System admins query across accounts via `adminDb`; everyone else sees only their account's rows.
|
|
77
|
+
- Cleanup is destructive and daily — export anything worth keeping before the 30-day default window closes.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Notifications
|
|
3
|
+
audience: both
|
|
4
|
+
kind: entity
|
|
5
|
+
verified_against: b3b3ae0
|
|
6
|
+
verified_on: 2026-11-01
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/migrations/050_notifications.sql
|
|
9
|
+
- .framework/functions/_shared/notify.ts
|
|
10
|
+
- .framework/functions/_shared/pipeline-runner.ts
|
|
11
|
+
- .framework/functions/_shared/agent-runner.ts
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Notifications
|
|
15
|
+
|
|
16
|
+
## What this is for
|
|
17
|
+
|
|
18
|
+
A **notification** is an in-app message to a specific person — "a pipeline
|
|
19
|
+
wants your attention on this record." `public.notifications` (migration 050)
|
|
20
|
+
is written by pipeline `send_notification` stages and the agent-runner's
|
|
21
|
+
notification action, and read by the signed-in recipient. It is the
|
|
22
|
+
*inbound* counterpart to [Deliveries](deliveries.md), which tracks outbound
|
|
23
|
+
webhook/integration sends.
|
|
24
|
+
|
|
25
|
+
## Fields and settings
|
|
26
|
+
|
|
27
|
+
| Column | Notes |
|
|
28
|
+
|---|---|
|
|
29
|
+
| `person_id` | required — the recipient (`people.id`). RLS scopes reads to `person_id = current_actor_id()`; users see only their own. |
|
|
30
|
+
| `account_id` | required — tenant scope; service role writes across accounts. |
|
|
31
|
+
| `source_type` / `source_id` | what produced it — default `'pipeline_execution'`; `source_id` is a text request/execution id (not always a uuid). |
|
|
32
|
+
| `target_type` / `target_id` | the record the notification concerns — the deep link. |
|
|
33
|
+
| `message` | required text body. |
|
|
34
|
+
| `data` | jsonb — arbitrary payload (e.g. rendered title, action hints). |
|
|
35
|
+
| `is_read`, `read_at` | read state; indexed `(account_id, person_id, is_read, created_at DESC)` for the unread inbox query. |
|
|
36
|
+
|
|
37
|
+
**Recipient resolution**
|
|
38
|
+
|
|
39
|
+
Pipeline/agent stages accept recipients as person UUIDs or `role:<slug>`
|
|
40
|
+
entries. `resolveNotificationRecipients`
|
|
41
|
+
(`.framework/functions/_shared/notify.ts`) expands `role:slug` → all
|
|
42
|
+
`people.id` in the account holding that role; unknown entries warn and are
|
|
43
|
+
skipped. A role matching zero people produces a valid empty insert — not an
|
|
44
|
+
error.
|
|
45
|
+
|
|
46
|
+
## Minimal configuration
|
|
47
|
+
|
|
48
|
+
Written by the platform — a valid row needs `account_id`, `person_id`,
|
|
49
|
+
`message` (from the stage's recipients + message config).
|
|
50
|
+
|
|
51
|
+
## Full configuration
|
|
52
|
+
|
|
53
|
+
Adds `source_type`/`source_id` attribution, `target_type`/`target_id`
|
|
54
|
+
deep-linking, and a `data` payload.
|
|
55
|
+
|
|
56
|
+
## Example
|
|
57
|
+
|
|
58
|
+
A pipeline `send_notification` stage with `recipients: ["role:support_admin"]`
|
|
59
|
+
inserts one row per support_admin in the account; each sees it in their
|
|
60
|
+
inbox, scoped to their `person_id` by RLS.
|
|
61
|
+
|
|
62
|
+
## Core files
|
|
63
|
+
|
|
64
|
+
- `.framework/migrations/050_notifications.sql` — table, indexes, RLS
|
|
65
|
+
- `.framework/functions/_shared/notify.ts` — `role:` → `person_id` resolver
|
|
66
|
+
- `.framework/functions/_shared/pipeline-runner.ts` — `send_notification` stage
|
|
67
|
+
- `.framework/functions/_shared/agent-runner.ts` — agent notification action
|
|
68
|
+
|
|
69
|
+
## Agent notes
|
|
70
|
+
|
|
71
|
+
- Never write literal `role:*` strings into `person_id` — it's a uuid
|
|
72
|
+
column; only resolved `people.id` values may be inserted. Both writers
|
|
73
|
+
share the resolver — use it.
|
|
74
|
+
- `notifications` ≠ `deliveries`: notifications are in-app inbox rows;
|
|
75
|
+
deliveries are outbound webhook/integration attempts.
|
|
76
|
+
- RLS allows `UPDATE` where `person_id = current_actor_id()` — recipients
|
|
77
|
+
can mark their own rows read; they cannot read each other's.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Observability Dashboard
|
|
3
|
+
audience: both
|
|
4
|
+
kind: entity
|
|
5
|
+
verified_against: e13127d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/src/pages/admin/ObservabilityDashboard.tsx
|
|
9
|
+
- .framework/functions/observability.ts
|
|
10
|
+
- .framework/functions/logs.ts
|
|
11
|
+
- .framework/functions/_shared/logging.ts
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## What this is for
|
|
15
|
+
|
|
16
|
+
The **Observability Dashboard** (`/spine-framework/admin/observability`, also `/observability/dashboard`) is the account's health console: event volume, error rate, latency percentiles, per-pipeline run stats, top actors, and recent threshold-alert breaches — all computed from the `logs` table and refreshed on an interval.
|
|
17
|
+
|
|
18
|
+
## Fields and settings
|
|
19
|
+
|
|
20
|
+
- **Time range** — Last Hour / Last 24 Hours / Last 7 Days (`from`/`to` sent to every query; the volume chart buckets by minute/hour/day respectively).
|
|
21
|
+
- **Refresh interval** — auto-refetch every 10s / 30s / 1m, plus a manual Refresh Now button.
|
|
22
|
+
- **Stats cards** — Total Events, Error Rate (%), P95 Latency (ms, red above 2000ms), Pipelines with runs.
|
|
23
|
+
- **Charts** — Event Volume line chart (bucketed counts), error rate, latency percentiles (p50/p95/p99), per-pipeline success/failure/avg-duration, Top Actors (limit 5).
|
|
24
|
+
- **Recent alerts** — last 5 `threshold.breached` log events in the window (metric, threshold, actual value, time).
|
|
25
|
+
|
|
26
|
+
**Downstream effects**
|
|
27
|
+
|
|
28
|
+
- Everything derives from `public.logs` rows written by `emitLog`/`emitAudit`/`emitSpan` — if log levels drop a category (`system_settings.logging.levels`), it disappears from these charts too.
|
|
29
|
+
- Queries are account-scoped for normal users; `system_admin` principals get cross-account reads (`adminDb`).
|
|
30
|
+
- The metrics here are exactly what threshold alerts compare against (`error_rate`, `latency_percentiles`, `pipeline_stats` actions).
|
|
31
|
+
- This page is read-only — it writes nothing.
|
|
32
|
+
|
|
33
|
+
## Minimal configuration
|
|
34
|
+
|
|
35
|
+
Open the page — defaults (24h range, 30s refresh) are sane for monitoring. There is nothing to configure.
|
|
36
|
+
|
|
37
|
+
## Full configuration
|
|
38
|
+
|
|
39
|
+
The backing API exposes more than the charts use:
|
|
40
|
+
|
|
41
|
+
- `GET /api/observability?action=event_volume&from=&to=&bucket=minute|hour|day`
|
|
42
|
+
- `action=error_rate` · `action=latency_percentiles` · `action=pipeline_stats` · `action=top_actors&limit=` · `action=ai_support_metrics` · `action=cleanup` (POST)
|
|
43
|
+
|
|
44
|
+
## Example
|
|
45
|
+
|
|
46
|
+
After deploying a new pipeline, set the range to Last Hour and refresh to 10s: Event Volume climbs with each run, the pipeline stats panel shows success vs failure counts and average duration, and a spike in the error-rate card correlates to the `error`-level rows you can drill into under Logs.
|
|
47
|
+
|
|
48
|
+
## Core files
|
|
49
|
+
|
|
50
|
+
- `.framework/src/pages/admin/ObservabilityDashboard.tsx` — this page (recharts)
|
|
51
|
+
- `.framework/functions/observability.ts` — metrics API (all `action=` queries above)
|
|
52
|
+
- `.framework/functions/logs.ts` — the `threshold.breached` alert feed (`action=account&event=`)
|
|
53
|
+
- `.framework/functions/_shared/logging.ts` — the emit path and level gating that decides what reaches `logs`
|
|
54
|
+
- Table: `public.logs` (source of all charts)
|
|
55
|
+
|
|
56
|
+
## Agent notes
|
|
57
|
+
|
|
58
|
+
- Flat charts with healthy traffic = log-level filtering, not broken metrics — the dashboard can only chart what `system_settings.logging.levels` allowed to be written.
|
|
59
|
+
- `system_admin` sees cross-account aggregates here; account members see only their own account.
|
|
60
|
+
- The 10s refresh is client-side polling — each tick re-runs all five metric queries; don't leave it open on a metered connection.
|
|
61
|
+
- Breach alerts shown here are read from `logs`, not from the alert records — see Threshold Alerts for the rules themselves.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Netlify Deployment
|
|
3
|
+
audience: both
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: e13127d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- netlify.toml
|
|
9
|
+
- .framework/public/_redirects
|
|
10
|
+
- scripts/assemble.sh
|
|
11
|
+
- scripts/assemble-frontend.sh
|
|
12
|
+
- scripts/assemble-functions.sh
|
|
13
|
+
- config/vite.config.ts
|
|
14
|
+
- .env.example
|
|
15
|
+
- .framework/functions/system.ts
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# Netlify Deployment
|
|
19
|
+
|
|
20
|
+
Spine deploys as a single Netlify site: a Vite-built SPA in `dist/` plus a
|
|
21
|
+
flat directory of functions in `.assembled/netlify/functions`, backed by a
|
|
22
|
+
Supabase (Postgres) project. Both halves are produced by the same build
|
|
23
|
+
command — there is no separate function deploy step.
|
|
24
|
+
|
|
25
|
+
## `netlify.toml` anatomy
|
|
26
|
+
|
|
27
|
+
The root `netlify.toml` is partly framework-owned (regenerated on
|
|
28
|
+
`npx spine-framework upgrade`, preserving `[functions."custom_*"]` sections):
|
|
29
|
+
|
|
30
|
+
| Block | Contents |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `[build]` | `command = "npm run assemble && npm run build"`, `publish = "dist"`, `functions = ".assembled/netlify/functions"` |
|
|
33
|
+
| `[build.environment]` | `SECRETS_SCAN_OMIT_KEYS` whitelists the intentionally-public vars (`VITE_SUPABASE_URL`, `VITE_SUPABASE_ANON_KEY`, `APP_BASE_URL`, `SITE_URL`, …) so Netlify's secrets scanner does not flag them; `SECRETS_SCAN_OMIT_PATHS` covers `.framework/.xenv*` |
|
|
34
|
+
| `[functions]` | `directory = ".assembled/netlify/functions"`, `included_files` for `_shared/**`, `node_bundler = "esbuild"`, `external_node_modules = ["typescript"]` (the esbuild printer panic workaround — typescript.js ships unbundled) |
|
|
35
|
+
| `[dev]` | `netlify dev` runs `scripts/netlify-dev-wrapper.sh`, site on `8888`, Vite `targetPort` `3001`, `startTimeout 300` |
|
|
36
|
+
| `[functions."<name>"]` | per-function config: `trigger-scheduler` gets `schedule = "* * * * *"`; `cil-ingest`, `cil-embeddings`, `cil-callmap`, `cil-agent`, `ai-agents`, `kb` get `timeout` overrides |
|
|
37
|
+
| `[[redirects]]` | `/* → /index.html` with status `200` (SPA fallback) |
|
|
38
|
+
| `[[headers]]` | `X-Frame-Options DENY`, `nosniff`, HSTS, `Referrer-Policy`, `Permissions-Policy`, and a CSP that allows `*.supabase.co` (https+wss) and `challenges.cloudflare.com` (Turnstile) |
|
|
39
|
+
|
|
40
|
+
Dispatched trigger pipelines run as Netlify **background functions**
|
|
41
|
+
(`pipeline-runner-background`): the dispatcher gets a `202` immediately and
|
|
42
|
+
each run gets the fixed 15-minute background budget — which is why no
|
|
43
|
+
`timeout` entry exists for it.
|
|
44
|
+
|
|
45
|
+
## How `.assembled/` is produced
|
|
46
|
+
|
|
47
|
+
`npm run assemble` (`scripts/assemble.sh`) runs before every dev start
|
|
48
|
+
(`predev:netlify`) and every build (`prebuild`). It wipes stale `dist/`,
|
|
49
|
+
then:
|
|
50
|
+
|
|
51
|
+
1. `scripts/assemble-functions.sh` flattens `.framework/functions` plus
|
|
52
|
+
`custom/functions` and `custom/apps/*/functions` into
|
|
53
|
+
`.assembled/netlify/functions`. Basename collisions are a **hard error**
|
|
54
|
+
naming both sources — Netlify needs one flat dir, so last-wins overwrite
|
|
55
|
+
is forbidden. `_quarantine` and `*-test.ts`/`*.test.ts` are excluded.
|
|
56
|
+
2. `scripts/assemble-frontend.sh` copies `.framework/src` as the base layer,
|
|
57
|
+
each `custom/apps/<slug>` verbatim to `.assembled/src/<slug>/`, fixes up
|
|
58
|
+
`index.html`, merges `.framework/public` + app `public/` dirs into the
|
|
59
|
+
project `public/` (same-path collisions are a hard error), copies
|
|
60
|
+
`_redirects` into `.assembled/src/` for `netlify dev`, and stages the
|
|
61
|
+
project `spine.config.json` into `public/`.
|
|
62
|
+
|
|
63
|
+
`config/vite.config.ts` then builds with `root: .assembled/src`,
|
|
64
|
+
`publicDir: public/`, `build.outDir: dist/` — matching `publish = "dist"`.
|
|
65
|
+
Never edit `.assembled/`; it is regenerated freely.
|
|
66
|
+
|
|
67
|
+
## The `/api/*` URL space
|
|
68
|
+
|
|
69
|
+
Requests to `/api/*` are rewritten to `/.netlify/functions/:splat`:
|
|
70
|
+
|
|
71
|
+
- **Production:** the published `_redirects` file — sourced from
|
|
72
|
+
`.framework/public/_redirects`, merged into `public/` at assemble time —
|
|
73
|
+
carries `/api/* → /.netlify/functions/:splat 200`, plus
|
|
74
|
+
`/spine.config.json` passthrough and the `/* → /index.html` fallback.
|
|
75
|
+
- **Dev:** the Vite proxy in `config/vite.config.ts` maps `/api/*` to
|
|
76
|
+
`localhost:8888` with the same rewrite.
|
|
77
|
+
|
|
78
|
+
Because the SPA fallback is `/* → /index.html 200`, **any unmatched URL
|
|
79
|
+
returns HTTP 200 with HTML** — including typos under `/api/`. A probe that
|
|
80
|
+
gets HTML instead of JSON is hitting the SPA fallback, not a function.
|
|
81
|
+
|
|
82
|
+
## Environment variables in production
|
|
83
|
+
|
|
84
|
+
Set these in the Netlify site environment (not in `netlify.toml`).
|
|
85
|
+
The full annotated list is [Environment Variables](../reference/env-vars.md);
|
|
86
|
+
|
|
87
|
+
| Variable | Needed for |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `VITE_SUPABASE_URL`, `VITE_SUPABASE_ANON_KEY` | browser bundle (public by design) |
|
|
90
|
+
| `SUPABASE_URL`, `SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY` | all functions — service-role key is server-only, never `VITE_`-prefixed |
|
|
91
|
+
| `INTERNAL_INVOKE_SECRET` | trigger-dispatched pipelines — unset means `queued` rows that never run |
|
|
92
|
+
| `APP_BASE_URL` | invite links, auth redirects, `base_url` allowlist |
|
|
93
|
+
| `SCHEDULER_API_KEY` | key-gated `system-cron`/`trigger-scheduler` invocation |
|
|
94
|
+
| `SITE_URL` | CORS `Access-Control-Allow-Origin`; falls back to Netlify's auto-set `URL` |
|
|
95
|
+
| `RESEND_API_KEY`, `EMAIL_FROM` | outbound email (invites, notification stages) |
|
|
96
|
+
| `OPENAI_API_KEY` | AI stage types, agents, embeddings |
|
|
97
|
+
| `NETLIFY_ACCESS_TOKEN`, `NETLIFY_SITE_ID` | optional — push banned IPs to the edge firewall (security function skips silently without them) |
|
|
98
|
+
|
|
99
|
+
`SUPABASE_DB_PASSWORD` is **not** a function env var — it is used locally by
|
|
100
|
+
`npx spine-framework migrate` to connect to Postgres directly.
|
|
101
|
+
|
|
102
|
+
## Deploy checklist
|
|
103
|
+
|
|
104
|
+
- [ ] Supabase project exists; `SUPABASE_*` + `VITE_SUPABASE_*` vars set in Netlify.
|
|
105
|
+
- [ ] `INTERNAL_INVOKE_SECRET` generated (`openssl rand -hex 32`) if triggers are used; `APP_BASE_URL`/`SITE_URL` set to the production origin.
|
|
106
|
+
- [ ] Migrations applied: `npx spine-framework migrate` against the production DB (see [Migrations](migrations.md)).
|
|
107
|
+
- [ ] Deploy via git push or `netlify deploy --prod` — the build command runs `assemble` then `vite build` automatically.
|
|
108
|
+
- [ ] Post-deploy smoke: `/api/system?action=health` and `?action=manifest` answer; first registration grants `system_admin` (see [Install and first run](../guide/01-install-and-first-run.md)).
|
|
109
|
+
- [ ] If timers/triggers are used: `trigger-scheduler` ticks appear in function logs within a minute; a test record write produces a `trigger.fired` log row.
|
|
110
|
+
|
|
111
|
+
## Related
|
|
112
|
+
|
|
113
|
+
- [Migrations](migrations.md) · [Upgrade](upgrade.md) · [Troubleshooting](troubleshooting.md)
|
|
114
|
+
- [Environment Variables](../reference/env-vars.md) · [CLI Reference](../reference/cli.md)
|
|
115
|
+
- [Anatomy of an installed project](../guide/02-project-map.md) — sealed vs customer trees
|