spine-framework 0.4.4 → 0.5.0-rc.4
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/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-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/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/admin-data.ts +39 -28
- package/.framework/functions/ai-agents.ts +10 -6
- package/.framework/functions/api-keys.ts +301 -122
- package/.framework/functions/apps.ts +43 -26
- 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 +145 -76
- 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 +277 -147
- 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/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/hooks/useEntityList.ts +12 -9
- package/.framework/src/hooks/useListSchema.ts +108 -58
- package/.framework/src/instructions/README.md +57 -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 +162 -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/invites.md +60 -0
- package/.framework/src/instructions/kb.md +80 -0
- package/.framework/src/instructions/logs.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 +148 -0
- package/.framework/src/instructions/operations/troubleshooting.md +170 -0
- package/.framework/src/instructions/operations/upgrade.md +116 -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 +150 -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/functions/account-nodes.md +75 -0
- package/.framework/src/instructions/reference/functions/admin-data.md +195 -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-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/package-and-install-an-app.md +127 -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 +82 -0
- package/.framework/src/instructions/triggers.md +69 -0
- package/.framework/src/instructions/types.md +93 -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/templates/agent/AGENTS.md +150 -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 +72 -0
- package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
- package/.framework/templates/agent/skills/spine-new-app/SKILL.md +67 -0
- package/.framework/templates/agent/skills/spine-new-function/SKILL.md +65 -0
- package/.framework/templates/agent/skills/spine-new-type/SKILL.md +65 -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 +392 -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/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/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,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: item-progress
|
|
3
|
+
audience: both
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/item-progress.ts
|
|
9
|
+
- .framework/functions/_shared/middleware.ts
|
|
10
|
+
- .framework/functions/_shared/audit.ts
|
|
11
|
+
- .framework/public/_redirects
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# item-progress
|
|
15
|
+
|
|
16
|
+
## What it does
|
|
17
|
+
|
|
18
|
+
Tracks a person's progress through a pipeline on a specific item —
|
|
19
|
+
one `item_progress` row per `(person_id, item_id)` pair. Enforces
|
|
20
|
+
forward-only status transitions defined by the row's type
|
|
21
|
+
(`design_schema.pipeline`, default `['not_started', 'in_progress',
|
|
22
|
+
'completed']`), stamps `started_at`/`completed_at`, and counts attempts.
|
|
23
|
+
|
|
24
|
+
## Route and methods
|
|
25
|
+
|
|
26
|
+
`/.netlify/functions/item-progress`, reachable as `/api/item-progress`
|
|
27
|
+
(`/api/*` → `/.netlify/functions/:splat`). Dispatch is by method
|
|
28
|
+
(`ctx.query.method`; `?method=` can override):
|
|
29
|
+
|
|
30
|
+
| Call | Action |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `GET /api/item-progress` | `list` |
|
|
33
|
+
| `GET /api/item-progress?id=<uuid>` | `get` |
|
|
34
|
+
| `POST /api/item-progress` | `upsert` |
|
|
35
|
+
| `PATCH /api/item-progress?id=<uuid>` | `update` |
|
|
36
|
+
|
|
37
|
+
Any other method → `Method <method> not supported`.
|
|
38
|
+
|
|
39
|
+
## Authentication
|
|
40
|
+
|
|
41
|
+
Standard `createHandler`: `x-api-key`, internal secret, or
|
|
42
|
+
`Bearer <jwt>`; anonymous is rejected with 401. All table access goes
|
|
43
|
+
through `ctx.db` (RLS-scoped for humans); type lookups and the
|
|
44
|
+
item/person title fetches use the service role. There are no additional
|
|
45
|
+
role checks.
|
|
46
|
+
|
|
47
|
+
## Request contract
|
|
48
|
+
|
|
49
|
+
**list** (query): `person_id`, `item_id`, `item_ids` (comma-separated),
|
|
50
|
+
`status`, `limit` (default `100`), `offset` (default `0`). Only
|
|
51
|
+
`is_active = true` rows, ordered `updated_at` desc.
|
|
52
|
+
|
|
53
|
+
**get** (query): `id`.
|
|
54
|
+
|
|
55
|
+
**upsert** (body): `person_id`, `item_id`, `account_id`, and `type_id`
|
|
56
|
+
*or* `type_slug` are required. Optional: `status`, `score`, `data`
|
|
57
|
+
(merged into `record.data`), `title`, `description`, `app_id`, `force`.
|
|
58
|
+
|
|
59
|
+
**update** (body `id` or query `id`): partial row fields plus `force`.
|
|
60
|
+
|
|
61
|
+
Transition rules: `status` may only move to the same or a later stage of
|
|
62
|
+
the type's pipeline (`toIdx >= fromIdx`). Statuses not present in the
|
|
63
|
+
pipeline pass unchecked. `force: true` bypasses the check. Backward moves
|
|
64
|
+
without `force` throw.
|
|
65
|
+
|
|
66
|
+
## Response contract
|
|
67
|
+
|
|
68
|
+
Standard `{ data, error, meta }` envelope.
|
|
69
|
+
|
|
70
|
+
- `list` → array of `item_progress` rows.
|
|
71
|
+
- `get` → one row.
|
|
72
|
+
- `upsert` → the upserted row (Postgres `ON CONFLICT (person_id, item_id)`
|
|
73
|
+
— repeat calls update the same row). Auto-fills `title`
|
|
74
|
+
(`"<item title> — <person name>"`) and `description`
|
|
75
|
+
(`"In Progress · score 90 · 2 attempts"`) when not given.
|
|
76
|
+
- `update` → the updated row.
|
|
77
|
+
|
|
78
|
+
## Side effects
|
|
79
|
+
|
|
80
|
+
- Writes `item_progress` rows; `data.attempts` increments on each status
|
|
81
|
+
change, `data.started_at`/`data.completed_at` stamp on first entry to
|
|
82
|
+
`in_progress`/`completed`. `created_by`/`updated_by` track the principal.
|
|
83
|
+
- Emits `item_progress.upserted` / `item_progress.updated` log entries via
|
|
84
|
+
`emitLog` with before/after payloads.
|
|
85
|
+
- Reads `items`, `people`, `types` for title composition and pipeline
|
|
86
|
+
resolution.
|
|
87
|
+
|
|
88
|
+
## Errors
|
|
89
|
+
|
|
90
|
+
| Condition | HTTP | Message |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| Missing `id` | 500 | `Progress record ID is required` |
|
|
93
|
+
| Missing required upsert fields | 500 | `person_id, item_id, type_id (or type_slug), and account_id are required` |
|
|
94
|
+
| Backward/invalid transition | 500 | `Invalid status transition: <from> → <to>. Use force: true to override.` |
|
|
95
|
+
| Row not found | 500 | `Progress record not found` |
|
|
96
|
+
| Unsupported method | 500 | `Method <method> not supported` |
|
|
97
|
+
|
|
98
|
+
## Agent notes
|
|
99
|
+
|
|
100
|
+
- There is no delete action; to retire a progress row, `PATCH`
|
|
101
|
+
`{ "is_active": false }` (list filters it out).
|
|
102
|
+
- `type_slug` is resolved with kind `item_progress` — use the progress
|
|
103
|
+
type's slug, not the item's type slug.
|
|
104
|
+
- Because `data` is merged (not replaced) on both upsert and update,
|
|
105
|
+
remove a key by writing an explicit `null`, not by omitting it.
|
|
106
|
+
- The unique key is `(person_id, item_id)` — one progress row per person
|
|
107
|
+
per item; concurrent upserts conflict-resolve onto the same row.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: kb
|
|
3
|
+
audience: both
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/kb.ts
|
|
9
|
+
- .framework/functions/_shared/kb.ts
|
|
10
|
+
- .framework/functions/_shared/llm.ts
|
|
11
|
+
- .framework/functions/_shared/middleware.ts
|
|
12
|
+
- .framework/functions/_shared/permissions.ts
|
|
13
|
+
- .framework/functions/_shared/pipeline-runner.ts
|
|
14
|
+
- .framework/migrations/054_kb_ingest_lease.sql
|
|
15
|
+
- netlify.toml
|
|
16
|
+
- public/_redirects
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# kb
|
|
20
|
+
|
|
21
|
+
## What it does
|
|
22
|
+
|
|
23
|
+
`kb` (`.framework/functions/kb.ts`) is the knowledge-base surface: a thin
|
|
24
|
+
`?action=` dispatcher over the shared implementation in
|
|
25
|
+
`.framework/functions/_shared/kb.ts`. It maintains `embeddings` vector rows
|
|
26
|
+
for published knowledge items and serves semantic search over them, gated by
|
|
27
|
+
the calling app's knowledge envelope.
|
|
28
|
+
|
|
29
|
+
| Action | Handler | Purpose |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| `ingest` | `kbIngest` | Embed one `items` record's declared vector fields into `embeddings`. |
|
|
32
|
+
| `revoke` | `kbRevoke` | Revoke + delete a document's embeddings and clear its ingest lease. |
|
|
33
|
+
| `reindex` | `kbReindex` | Re-ingest every `published` item of a type (batched, 10 in flight). |
|
|
34
|
+
| `search` | `kbSearch` | Vector search via `match_embeddings`, best-match-per-document. Default action. |
|
|
35
|
+
| `publish` | `kbPublish` | PATCH `status: 'published'` through admin-data, then ingest. |
|
|
36
|
+
|
|
37
|
+
## Route and methods
|
|
38
|
+
|
|
39
|
+
- `/api/kb?action=<action>` — `public/_redirects` maps `/api/*` to
|
|
40
|
+
`/.netlify/functions/:splat`. The action comes from `ctx.query.action`
|
|
41
|
+
only — it must be a query parameter; the HTTP method itself is not
|
|
42
|
+
checked, and the body carries the parameters.
|
|
43
|
+
- The function is configured with a 120s timeout in `netlify.toml` (reindex
|
|
44
|
+
sweeps a whole corpus).
|
|
45
|
+
|
|
46
|
+
## Authentication
|
|
47
|
+
|
|
48
|
+
`createHandler` resolves a principal and rejects anonymous requests with 401.
|
|
49
|
+
There is no role gate on the actions themselves; authorization is enforced
|
|
50
|
+
downstream:
|
|
51
|
+
|
|
52
|
+
- `kbSearch` runs `resolveKnowledgeScope` — deny-by-default unless the
|
|
53
|
+
caller's app declares `config.knowledge` — and each result is passed
|
|
54
|
+
through `sanitizeRecordData` before returning.
|
|
55
|
+
- `kbPublish` goes through admin-data `update` (a synthetic PATCH), so
|
|
56
|
+
field-level permissions and triggers apply.
|
|
57
|
+
- `kbIngest`/`kbRevoke`/`kbReindex` operate via `adminDb` — they rely on the
|
|
58
|
+
caller being trusted to name a document; `body.account_id` on ingest lets
|
|
59
|
+
an operator ingest a record owned by another account.
|
|
60
|
+
|
|
61
|
+
## Request contract
|
|
62
|
+
|
|
63
|
+
`ingest`: `{ id (required, items.id), account_id? }`
|
|
64
|
+
|
|
65
|
+
`revoke`: `{ id (required) }`
|
|
66
|
+
|
|
67
|
+
`reindex`: `{ type_slug (required), app_id? }` — `app_id` narrows the items
|
|
68
|
+
re-ingested.
|
|
69
|
+
|
|
70
|
+
`search`:
|
|
71
|
+
|
|
72
|
+
| Field | Default | Notes |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| `query` | — | Required. |
|
|
75
|
+
| `type_slugs` | `[]` | The caller's pick — intersected with the app envelope; picks outside it deny the call. |
|
|
76
|
+
| `sources` | `['kb']` | `'kb'` for knowledge items, `'code'` for code-chunk vectors (the `cil_*` item-type namespace). |
|
|
77
|
+
| `app_id` | `null` | Post-filter on `metadata.app_id`. |
|
|
78
|
+
| `vector_fields` | `[]` | Restrict to specific `metadata.vector_type` values. |
|
|
79
|
+
| `limit` / `offset` | `10` / `0` | Page over best-match documents. |
|
|
80
|
+
| `similarity_threshold` | `0.15` | Minimum cosine similarity. |
|
|
81
|
+
| `filters` | `{}` | Post-filter on the resolved `items` row. |
|
|
82
|
+
|
|
83
|
+
`publish`: `{ id (required) }`
|
|
84
|
+
|
|
85
|
+
## Response contract
|
|
86
|
+
|
|
87
|
+
- `ingest` → `{ ok, message, count?, in_progress? }`. `message` is one of
|
|
88
|
+
`embeddings generated`, `embeddings reused (unchanged content)`,
|
|
89
|
+
`record is not published; embeddings revoked if any`,
|
|
90
|
+
`embedding already in progress` (with `in_progress: true` — **not** a
|
|
91
|
+
success signal for stampers), or `no content to embed`.
|
|
92
|
+
- `revoke` → `{ ok, message: 'embeddings revoked', count }`, or
|
|
93
|
+
`{ ok, skipped: true, message: 'document is published — embeddings
|
|
94
|
+
retained', count: 0 }` — revoke re-checks current status at execution time
|
|
95
|
+
and refuses to strip a now-published document.
|
|
96
|
+
- `reindex` → `{ ok, count, errors?: [{ id, error }] }` — per-item failures
|
|
97
|
+
are collected, not fatal.
|
|
98
|
+
- `search` → `{ results, count, error? }`; results are
|
|
99
|
+
`{ id, document_id, type_slug, title, snippet, app_id, similarity, status,
|
|
100
|
+
created_at, source, data }` sorted by similarity. `error` carries
|
|
101
|
+
`knowledge_denied: <reason>` when the envelope refuses.
|
|
102
|
+
- `publish` → `{ record, ingest }` — the admin-data update result plus the
|
|
103
|
+
ingest result.
|
|
104
|
+
|
|
105
|
+
## Side effects
|
|
106
|
+
|
|
107
|
+
- `ingest`: reads the record's stamped `design_schema.functionality.kb` —
|
|
108
|
+
`vector_fields` (default `['title', 'content']`), `chunk_field` (default
|
|
109
|
+
`content`), `chunk_size` (800), `chunk_overlap` (100), `security_level`
|
|
110
|
+
(deny-by-default `internal`), `supersede_keys`. Field text is normalized
|
|
111
|
+
(rich-text HTML stripped, bare UUID tokens dropped) and hashed — a
|
|
112
|
+
`document_leases` row (migration `054`) dedupes unchanged re-embeds
|
|
113
|
+
(`reused`) and fences concurrent workers (`processing`). Prior embeddings
|
|
114
|
+
are cleared via `revoke_embeddings_for_item` + delete, chunks are embedded
|
|
115
|
+
in **one batched** `callOpenAIEmbeddings` call (`text-embedding-3-small`),
|
|
116
|
+
and new rows are inserted with `metadata.{type_slug, item_type, app_id,
|
|
117
|
+
vector_type, security_level, status}` and `approved_at` set. Older
|
|
118
|
+
`published` items sharing a `supersede_keys` value are archived with
|
|
119
|
+
`superseded_by`/`superseded_at` and their embeddings revoked.
|
|
120
|
+
- `revoke`: `revoke_embeddings_for_item` RPC + embeddings row delete +
|
|
121
|
+
unconditional `document_leases` delete (warn-only on failure).
|
|
122
|
+
- `reindex`: sweeps `published` items of the type in batches of 10, calling
|
|
123
|
+
`kbIngest` each — sequential ingest would exceed the function budget.
|
|
124
|
+
- `publish`: admin-data PATCH sets `status: 'published'` (permissions,
|
|
125
|
+
audit, triggers run), then `kbIngest` runs immediately in the same
|
|
126
|
+
request.
|
|
127
|
+
- `search`: one `callOpenAIEmbeddings` call for the query embedding plus
|
|
128
|
+
`match_embeddings` RPC per requested vector type.
|
|
129
|
+
|
|
130
|
+
## Errors
|
|
131
|
+
|
|
132
|
+
| Condition | Error |
|
|
133
|
+
|---|---|
|
|
134
|
+
| Missing inputs | `id is required` · `type_slug is required` · `query is required` |
|
|
135
|
+
| Unknown record | `record not found` |
|
|
136
|
+
| Unknown type | `type not found: <type_slug>` |
|
|
137
|
+
| Bad action | `unknown kb action: <action>` |
|
|
138
|
+
| RPC/insert failures | thrown — the lease is released with `failed` status |
|
|
139
|
+
|
|
140
|
+
## Agent notes
|
|
141
|
+
|
|
142
|
+
- **Ingest outcomes are tri-state.** `in_progress: true` means another
|
|
143
|
+
worker owns this content's embed — callers that stamp embedding state on
|
|
144
|
+
the record must not write success *or* failure; the owning worker's
|
|
145
|
+
completion reconciles the corpus. A fenced lease release (superseded
|
|
146
|
+
mid-flight) warns without throwing.
|
|
147
|
+
- **Search is deny-by-default.** No `config.knowledge` envelope on the app →
|
|
148
|
+
`knowledge_denied`. The envelope declares `account` (self, or
|
|
149
|
+
`self_and_descendants` via `get_accessible_accounts`), `platform`
|
|
150
|
+
(spine-system account, own `sources` allowlist), and project-scoped code
|
|
151
|
+
chunks — the last only when an explicit project id list survives the
|
|
152
|
+
envelope∩pick intersection. When the app declares `required_roles`, the
|
|
153
|
+
principal must hold one (system_admin bypasses).
|
|
154
|
+
- **`document_id` is polymorphic.** Regular records key `embeddings` by
|
|
155
|
+
their items UUID; code-chunk vectors key it by a `stable_id` string —
|
|
156
|
+
`kbSearch` splits the `items` lookup (`id` vs `data->>stable_id`) to
|
|
157
|
+
handle both.
|
|
158
|
+
- **Pipelines don't call this endpoint.** The `kb_ingest`/`kb_revoke` stage
|
|
159
|
+
handlers lazy-import `kbIngest`/`kbRevoke` directly and resolve `item_id`
|
|
160
|
+
from trigger data; `search_knowledge` uses its own text-search path over
|
|
161
|
+
`embeddings`, not `kbSearch`.
|
|
162
|
+
- `adminUpdate` is imported lazily to avoid a circular-import deadlock
|
|
163
|
+
through `pipeline-runner` — preserve that pattern if adding admin-data
|
|
164
|
+
calls.
|
|
165
|
+
- See [AI agents, prompt configs, and embeddings](../../concepts/ai-and-prompts.md)
|
|
166
|
+
for how these vectors feed agent retrieval.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: logs
|
|
3
|
+
audience: developers, agents
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/logs.ts
|
|
9
|
+
- .framework/functions/_shared/middleware.ts
|
|
10
|
+
- .framework/functions/_shared/principal.ts
|
|
11
|
+
- .framework/migrations/069_log_stats_window.sql
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# logs
|
|
15
|
+
|
|
16
|
+
## What it does
|
|
17
|
+
|
|
18
|
+
Read/write API over the `public.logs` table — the shared audit, span, and
|
|
19
|
+
event log every function writes into. Read actions cover account feeds,
|
|
20
|
+
per-target and per-person histories, aggregate stats, and message search;
|
|
21
|
+
the write path appends a single log row; `cleanup` prunes old rows.
|
|
22
|
+
|
|
23
|
+
## Route and methods
|
|
24
|
+
|
|
25
|
+
`/.netlify/functions/logs` — also `/api/logs` (`/api/*` →
|
|
26
|
+
`/.netlify/functions/:splat` in `.framework/public/_redirects`; same
|
|
27
|
+
rewrite in the dev proxy, `config/vite.config.ts`).
|
|
28
|
+
|
|
29
|
+
Dispatch by `?action=` + HTTP method:
|
|
30
|
+
|
|
31
|
+
| Call | Handler |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `GET?action=account` | `listAccount` — account-scoped feed |
|
|
34
|
+
| `GET?action=target` | `listTarget` — rows for one record |
|
|
35
|
+
| `GET?action=person` | `listPerson` — rows for one person |
|
|
36
|
+
| `GET?action=stats` | `getStats` — `get_log_stats` RPC |
|
|
37
|
+
| `GET?action=search` | `search` — `message ILIKE` match |
|
|
38
|
+
| `POST?action=cleanup` | `cleanup` — delete old rows |
|
|
39
|
+
| `POST` (no action) | `log` — insert one row |
|
|
40
|
+
|
|
41
|
+
Anything else throws `Invalid action or method`.
|
|
42
|
+
|
|
43
|
+
## Authentication
|
|
44
|
+
|
|
45
|
+
`createHandler` — any non-anonymous principal; anonymous → 401.
|
|
46
|
+
|
|
47
|
+
Scope varies per action:
|
|
48
|
+
|
|
49
|
+
- `listAccount` — `system_admin` principals read via `adminDb`
|
|
50
|
+
(cross-account); everyone else reads via `ctx.db` filtered to
|
|
51
|
+
`account_id`, and needs an account context.
|
|
52
|
+
- `listTarget`, `listPerson`, `search`, `getStats` — always `ctx.db`
|
|
53
|
+
pinned to `ctx.accountId`; all throw `Account context required` without
|
|
54
|
+
one. Note `ctx.db` is `adminDb` for machine principals, so the
|
|
55
|
+
`account_id` filter is the only scope guard there.
|
|
56
|
+
- `log`, `cleanup` — `ctx.db`, no role check inside the function.
|
|
57
|
+
|
|
58
|
+
## Request contract
|
|
59
|
+
|
|
60
|
+
Query parameters:
|
|
61
|
+
|
|
62
|
+
- `account`: `event` (exact `span_name`), `event_type` (exact `level`),
|
|
63
|
+
`target_type` (`source_type`), `source`, `source_id`, `actor_id`
|
|
64
|
+
(`created_by`), `query` (`message ILIKE %…%`), `date_from`, `date_to`,
|
|
65
|
+
`trace_id`, `limit` (100), `offset` (0).
|
|
66
|
+
- Special case: `event=threshold.breached` returns alert-shaped rows
|
|
67
|
+
`{ id, metric, threshold_value, actual_value, created_at }` taken from
|
|
68
|
+
`metadata`, plus `total` — not the standard row mapping.
|
|
69
|
+
- `target`: `target_type` + `target_id` (both required), `event_type`,
|
|
70
|
+
`limit` (100), `offset` (0).
|
|
71
|
+
- `person`: `person_id` (defaults to the caller's principal id),
|
|
72
|
+
`include_system` (`'true'` to include `level='system'` rows, excluded by
|
|
73
|
+
default), `limit` (50), `offset` (0).
|
|
74
|
+
- `stats`: `event_type`, `target_type`, `source`, `source_id`, `actor_id`,
|
|
75
|
+
`query`, `date_from`, `date_to` — all optional, mapped to the
|
|
76
|
+
`get_log_stats` RPC parameters.
|
|
77
|
+
- `search`: `query` (required), `event_type`, `target_type`, `limit` (50),
|
|
78
|
+
`offset` (0).
|
|
79
|
+
|
|
80
|
+
Body (`log` action only): `event_type` (required — written to `level`),
|
|
81
|
+
`target_type`, `target_id`, `action` (written to `source`), `message`,
|
|
82
|
+
`details` (written to `context`), `metadata`.
|
|
83
|
+
|
|
84
|
+
Body (`cleanup`): `days_to_keep` (default 30).
|
|
85
|
+
|
|
86
|
+
## Response contract
|
|
87
|
+
|
|
88
|
+
Standard envelope `{ data, error, meta }`.
|
|
89
|
+
|
|
90
|
+
- `account` → `{ data: [mapped rows], total }`.
|
|
91
|
+
- `target` / `person` / `search` → array of mapped rows.
|
|
92
|
+
- Rows are remapped by `mapLogRow`: `level → event_type`,
|
|
93
|
+
`person_id → actor_id`, `source_type → target_type`,
|
|
94
|
+
`source_id → target_id`, `source → action`, `context → details`, plus
|
|
95
|
+
`span_name`, `trace_id`, `span_id`, `parent_span_id`, `duration_ms`,
|
|
96
|
+
`outcome`, `event_category`, `kind`, `message`, `metadata`,
|
|
97
|
+
`created_at`.
|
|
98
|
+
- `stats` → first `get_log_stats` row `{ total, error_count,
|
|
99
|
+
user_action_count, event_type_count, levels, sources, source_types }`,
|
|
100
|
+
or a zeroed object when the RPC returns nothing.
|
|
101
|
+
- `log` → `{ log_id }`. `cleanup` → `{ deleted_count }`.
|
|
102
|
+
|
|
103
|
+
## Side effects
|
|
104
|
+
|
|
105
|
+
- `log` inserts one `logs` row stamped with `person_id` (caller),
|
|
106
|
+
`account_id`, and the mapped fields.
|
|
107
|
+
- `cleanup` deletes `logs` rows with `created_at` older than
|
|
108
|
+
`days_to_keep` — permanently.
|
|
109
|
+
- Read actions are pure queries. (The `get_log_stats` RPC is a read.)
|
|
110
|
+
|
|
111
|
+
## Errors
|
|
112
|
+
|
|
113
|
+
- `target_type and target_id are required`, `Person ID is required`,
|
|
114
|
+
`Search query is required`, `event_type is required` — validation.
|
|
115
|
+
- `Account context required` — non-admin callers without `ctx.accountId`.
|
|
116
|
+
- `Invalid action or method` — unmatched dispatch.
|
|
117
|
+
- `person`/`target` use caller-scoped `ctx.db` — a `person_id` outside the
|
|
118
|
+
caller's account simply returns an empty page, not an error.
|
|
119
|
+
|
|
120
|
+
## Agent notes
|
|
121
|
+
|
|
122
|
+
- **`get_log_stats` defaults to a 7-day window.** When `date_from` is not
|
|
123
|
+
supplied the RPC uses `now() - interval '7 days'`
|
|
124
|
+
(`.framework/migrations/069_log_stats_window.sql`) — "all-time" stats
|
|
125
|
+
are really trailing-week stats. Pass `date_from` explicitly for longer
|
|
126
|
+
windows. (Audit finding F-037.)
|
|
127
|
+
- `cleanup` has no admin check: it runs on `ctx.db`. Human JWTs get an
|
|
128
|
+
RLS-scoped client, but machine principals resolve to `adminDb` — a
|
|
129
|
+
machine call to `POST ?action=cleanup` deletes matching rows
|
|
130
|
+
**platform-wide**. Treat this action as operator-only.
|
|
131
|
+
- The same `account_id`-vs-`adminDb` asymmetry applies to `listTarget`,
|
|
132
|
+
`listPerson`, `search`, and `getStats`: the `account_id` filter is a
|
|
133
|
+
query predicate, not RLS, for machine callers — but it is always
|
|
134
|
+
applied, so cross-account reads still require omitting account context,
|
|
135
|
+
which those handlers forbid.
|
|
136
|
+
- `listAccount`'s `event=threshold.breached` branch returns a different
|
|
137
|
+
row shape — clients parsing log rows should branch on it.
|
|
138
|
+
- Field names in the response are the *mapped* names (`event_type`,
|
|
139
|
+
`actor_id`, `target_type`), not the column names (`level`, `person_id`,
|
|
140
|
+
`source_type`) — a common source of confusion when correlating with the
|
|
141
|
+
table directly.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: observability
|
|
3
|
+
audience: developers, agents
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/observability.ts
|
|
9
|
+
- .framework/functions/_shared/principal.ts
|
|
10
|
+
- .framework/functions/_shared/resolve-ids.ts
|
|
11
|
+
- .framework/functions/_shared/middleware.ts
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# observability
|
|
15
|
+
|
|
16
|
+
## What it does
|
|
17
|
+
|
|
18
|
+
Aggregation API over `public.logs` and `public.pipeline_executions` — the
|
|
19
|
+
metrics surface behind the observability dashboards: event volume, error
|
|
20
|
+
rate, latency percentiles, pipeline stats, top actors, AI-support metrics,
|
|
21
|
+
and a log-retention cleanup action.
|
|
22
|
+
|
|
23
|
+
## Route and methods
|
|
24
|
+
|
|
25
|
+
`/.netlify/functions/observability?action=<action>` — also `/api/…`
|
|
26
|
+
(`/api/*` → `/.netlify/functions/:splat` in
|
|
27
|
+
`.framework/public/_redirects`).
|
|
28
|
+
|
|
29
|
+
Dispatch is on `action` only — there are no HTTP-method checks. Valid
|
|
30
|
+
actions: `event_volume`, `error_rate`, `latency_percentiles`,
|
|
31
|
+
`pipeline_stats`, `top_actors`, `ai_support_metrics`, `cleanup`.
|
|
32
|
+
|
|
33
|
+
## Authentication
|
|
34
|
+
|
|
35
|
+
`createHandler` — non-anonymous principal required (401 otherwise).
|
|
36
|
+
|
|
37
|
+
`system_admin` principals query via `adminDb` across all accounts;
|
|
38
|
+
everyone else uses `ctx.db` and the handlers additionally filter
|
|
39
|
+
`account_id = ctx.accountId` — non-admins without account context get
|
|
40
|
+
`Account context required`. `cleanup` separately requires
|
|
41
|
+
`principal.roles` to include `system_admin`.
|
|
42
|
+
|
|
43
|
+
## Request contract
|
|
44
|
+
|
|
45
|
+
All inputs are query parameters:
|
|
46
|
+
|
|
47
|
+
| Action | Params | Returns |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| `event_volume` | `from`, `to` (required ISO dates); `bucket` = `hour`\|`day` (default `hour`) | `[{ bucket, count }]` sorted ascending — counts of `logs` rows bucketed by `created_at` |
|
|
50
|
+
| `error_rate` | `from`, `to` (required) | `{ total, errors, rate }` — rows with `outcome='failure'` or `level='error'` over total |
|
|
51
|
+
| `latency_percentiles` | `from`, `to` (required); `source` (default `request`, matched against `span_name`) | `{ p50, p90, p95, p99 }` over positive `duration_ms` values |
|
|
52
|
+
| `pipeline_stats` | `from`, `to` (required) | `[{ pipeline_id, success_count, failure_count, avg_duration_ms }]` over `pipeline_executions` (`completed`/`failed` statuses counted; avg over rows having both timestamps) |
|
|
53
|
+
| `top_actors` | `from`, `to` (required); `limit` (default 5) | `[{ principal_id, event_count }]` — counts of `logs.created_by`, `anonymous` for null |
|
|
54
|
+
| `ai_support_metrics` | `from`, `to` (optional; default last 30 days); requires account context | see below |
|
|
55
|
+
| `cleanup` | `days` (default 30) | `{ deleted_count }` via `cleanup_old_logs` RPC; system-admin only |
|
|
56
|
+
|
|
57
|
+
`ai_support_metrics` resolves the `ai_feedback_record` item type and
|
|
58
|
+
reads `items` for the context account: feedback rows (attempted count,
|
|
59
|
+
`customer_feedback='not_helpful'`, `kb_article_id`, `reuse_count`,
|
|
60
|
+
`kb_candidate`), resolved items (`status='resolved'` with
|
|
61
|
+
`data->>aim_escalation_reason='none'`), and escalations
|
|
62
|
+
(`status='human_assigned'`). Response: `{ period: {from,to},
|
|
63
|
+
ai_attempted, ai_resolved, human_escalations, thumbs_down_rate,
|
|
64
|
+
kb_articles_created, kb_reuse_count, knowledge_gaps }`. It uses `adminDb`
|
|
65
|
+
scoped to `ctx.accountId` and tolerates per-query failure via
|
|
66
|
+
`Promise.allSettled` — a failed sub-query contributes zero, not an error.
|
|
67
|
+
|
|
68
|
+
## Response contract
|
|
69
|
+
|
|
70
|
+
Standard envelope `{ data, error, meta }`. Payloads per action are listed
|
|
71
|
+
above; aggregation happens in memory inside the function (there are no
|
|
72
|
+
SQL rollups for the metrics endpoints — full matching rows are selected
|
|
73
|
+
then counted).
|
|
74
|
+
|
|
75
|
+
## Side effects
|
|
76
|
+
|
|
77
|
+
Read-only except `cleanup`, which deletes `logs` rows older than `days`
|
|
78
|
+
via the `cleanup_old_logs` RPC. (`system-cron` performs the same cleanup
|
|
79
|
+
automatically on the 00:00 UTC tick with a 30-day retention.)
|
|
80
|
+
|
|
81
|
+
## Errors
|
|
82
|
+
|
|
83
|
+
- `Account context required` — non-admin callers without `ctx.accountId`
|
|
84
|
+
(all actions), and `ai_support_metrics` for any caller.
|
|
85
|
+
- `from and to dates are required` — all metric actions except
|
|
86
|
+
`ai_support_metrics` and `cleanup`.
|
|
87
|
+
- `System admin required` — `cleanup` without the `system_admin` role.
|
|
88
|
+
- `Unknown action: <x>. Valid actions: …` — unmatched `action`.
|
|
89
|
+
|
|
90
|
+
## Agent notes
|
|
91
|
+
|
|
92
|
+
- These are in-memory aggregations, not RPC rollups: `event_volume`,
|
|
93
|
+
`error_rate`, `latency_percentiles`, and `top_actors` pull every
|
|
94
|
+
matching row's columns and count locally. Wide `from`/`to` windows over
|
|
95
|
+
a busy `logs` table are proportional in cost — keep ranges tight.
|
|
96
|
+
- `latency_percentiles` filters `span_name = source` (default `request`)
|
|
97
|
+
— the request spans emitted by `createHandler`. Passing `source=` for
|
|
98
|
+
other span names (e.g. `db.select`, `pipeline.run`) yields percentiles
|
|
99
|
+
for those operations.
|
|
100
|
+
- `top_actors` keys on `created_by`, which is null for machine-emitted
|
|
101
|
+
spans — those collapse into `anonymous`.
|
|
102
|
+
- `ai_support_metrics` depends on `ai_feedback_record` items and
|
|
103
|
+
`status='human_assigned'`/`'resolved'` conventions written by the AI
|
|
104
|
+
support flows; it returns zeros on installations without them rather
|
|
105
|
+
than erroring.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: pipeline-executions
|
|
3
|
+
audience: developers, agents
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/pipeline-executions.ts
|
|
9
|
+
- .framework/functions/_shared/middleware.ts
|
|
10
|
+
- .framework/functions/_shared/principal.ts
|
|
11
|
+
- .framework/functions/_shared/pipeline-runner.ts
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# pipeline-executions
|
|
15
|
+
|
|
16
|
+
## What it does
|
|
17
|
+
|
|
18
|
+
CRUD and lifecycle surface for `public.pipeline_executions` — the durable
|
|
19
|
+
ledger of pipeline runs. One row per run carries `pipeline_id`, `status`,
|
|
20
|
+
`trigger_data`, `result`, `error_message`, `started_at`, `completed_at`, and
|
|
21
|
+
`duration_ms`. The pipeline runner writes most transitions itself; this
|
|
22
|
+
endpoint is how operators and the admin UI read run history and how scripts
|
|
23
|
+
create, start, complete, cancel, or prune rows.
|
|
24
|
+
|
|
25
|
+
## Route and methods
|
|
26
|
+
|
|
27
|
+
`/.netlify/functions/pipeline-executions` — also reachable as
|
|
28
|
+
`/api/pipeline-executions` (`/api/*` → `/.netlify/functions/:splat` in
|
|
29
|
+
`.framework/public/_redirects`; same rewrite in the dev proxy,
|
|
30
|
+
`config/vite.config.ts`).
|
|
31
|
+
|
|
32
|
+
Dispatch is by `?action=` query parameter plus HTTP method (the middleware
|
|
33
|
+
copies `event.httpMethod` into `ctx.query.method`):
|
|
34
|
+
|
|
35
|
+
| Call | Handler |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `GET` (no action, no `id`) | `list` |
|
|
38
|
+
| `GET&id=` | `get` — single row by id |
|
|
39
|
+
| `GET?action=list` | `list` |
|
|
40
|
+
| `GET?action=running` | `getRunning` — `status = 'running'` rows |
|
|
41
|
+
| `GET?action=stats` | `getStats` — status counts |
|
|
42
|
+
| `POST` (no action) | `create` — insert a `pending` execution |
|
|
43
|
+
| `POST?action=cleanup` | `cleanup` — delete old rows |
|
|
44
|
+
| `PATCH?action=start` | `start` — set `running` + `started_at` |
|
|
45
|
+
| `PATCH?action=complete` | `complete` — set terminal status |
|
|
46
|
+
| `PATCH?action=cancel` | `cancel` — set `cancelled` |
|
|
47
|
+
|
|
48
|
+
Any other action/method combination throws `Invalid action or method`.
|
|
49
|
+
|
|
50
|
+
## Authentication
|
|
51
|
+
|
|
52
|
+
Wrapped in `createHandler`, so the request must resolve to a non-anonymous
|
|
53
|
+
principal — Bearer JWT (human), `x-api-key` (machine), or
|
|
54
|
+
`x-internal-secret` (internal) per `resolvePrincipal`. Anonymous requests
|
|
55
|
+
get 401 before the handler runs.
|
|
56
|
+
|
|
57
|
+
System admins (`roles` contains `system_admin`) read through `adminDb`
|
|
58
|
+
across all accounts. Everyone else reads through their scoped `ctx.db` and
|
|
59
|
+
must have an account context — `list`, `running`, and `stats` throw
|
|
60
|
+
`Account context required` without one.
|
|
61
|
+
|
|
62
|
+
## Request contract
|
|
63
|
+
|
|
64
|
+
Query parameters:
|
|
65
|
+
|
|
66
|
+
- `id` — execution UUID. `get` requires it; the middleware also copies it
|
|
67
|
+
into `body.id` for write actions.
|
|
68
|
+
- `pipeline_id` — filter for `list`, `running`, `stats`.
|
|
69
|
+
- `status` — filter for `list`.
|
|
70
|
+
- `date_from`, `date_to` — `created_at` bounds for `stats`.
|
|
71
|
+
- `limit` (default 100), `offset` (default 0) — `list` pagination.
|
|
72
|
+
|
|
73
|
+
Body fields:
|
|
74
|
+
|
|
75
|
+
- `create`: `pipeline_id` (required), `trigger_data` (optional object).
|
|
76
|
+
- `start` / `cancel`: `id` (required).
|
|
77
|
+
- `complete`: `id` (required), `output_data`, `error_message`. A non-empty
|
|
78
|
+
`error_message` makes the final status `failed`; otherwise `completed`.
|
|
79
|
+
- `cleanup`: `days_to_keep` (default 30), `status_filter` (optional) —
|
|
80
|
+
deletes rows with `created_at` older than the cutoff.
|
|
81
|
+
|
|
82
|
+
## Response contract
|
|
83
|
+
|
|
84
|
+
All successful returns are wrapped by the middleware in
|
|
85
|
+
`{ data, error: null, meta: { requestId, duration } }`.
|
|
86
|
+
|
|
87
|
+
- `list` / `running` — array of `pipeline_executions` rows, newest first
|
|
88
|
+
(`list` by `created_at`, `running` by `started_at`).
|
|
89
|
+
- `get` — one row.
|
|
90
|
+
- `stats` — `{ total, completed, failed, running }` counted in memory over
|
|
91
|
+
the filtered set.
|
|
92
|
+
- `create` — `{ execution_id }`.
|
|
93
|
+
- `start` / `complete` / `cancel` — `{ success: true }`.
|
|
94
|
+
- `cleanup` — `{ deleted_count }`.
|
|
95
|
+
|
|
96
|
+
## Side effects
|
|
97
|
+
|
|
98
|
+
- `create` inserts a row with `status: 'pending'`, stamped with
|
|
99
|
+
`created_by` and `account_id`.
|
|
100
|
+
- `start` / `complete` / `cancel` update status and timestamps; `complete`
|
|
101
|
+
also writes `result`, `error_message`, and `duration_ms` computed from
|
|
102
|
+
`started_at`.
|
|
103
|
+
- `cleanup` deletes rows permanently.
|
|
104
|
+
- Each write emits an audit log: `pipeline_execution.created`,
|
|
105
|
+
`pipeline_execution.started`, `pipeline_execution.completed`,
|
|
106
|
+
`pipeline_execution.cancelled`, `pipeline_executions.cleaned`.
|
|
107
|
+
|
|
108
|
+
## Errors
|
|
109
|
+
|
|
110
|
+
- `Execution ID is required` — `get`, `start`, `complete`, `cancel`
|
|
111
|
+
without `id`.
|
|
112
|
+
- `Pipeline ID is required` / `Account context required` — `create`
|
|
113
|
+
validation; account check on `list`, `running`, `stats` for non-admins.
|
|
114
|
+
- `Invalid action or method` — unmatched action/method pair.
|
|
115
|
+
- `get` uses `.single()` — a missing id surfaces as a database error,
|
|
116
|
+
not a clean 404.
|
|
117
|
+
- All thrown errors become structured 500 responses unless they carry a
|
|
118
|
+
`statusCode`.
|
|
119
|
+
|
|
120
|
+
## Agent notes
|
|
121
|
+
|
|
122
|
+
- `create` here stamps `status: 'pending'` — a different value from the
|
|
123
|
+
`queued` rows the trigger engine creates before dispatching to
|
|
124
|
+
`pipeline-runner-background`. Both are non-terminal.
|
|
125
|
+
- Rows stuck in `queued` or `running` mean dispatch or the runner died;
|
|
126
|
+
`sweepStaleExecutions` in `.framework/functions/_shared/pipeline-runner.ts`
|
|
127
|
+
marks `running`/`pending`/`queued` rows untouched for over 10 minutes as
|
|
128
|
+
`failed`.
|
|
129
|
+
- `cancel` only updates the row — nothing in this function interrupts an
|
|
130
|
+
in-flight run.
|
|
131
|
+
- `stats` loads all matching `id, status` rows and counts in memory; it
|
|
132
|
+
has no `limit` guard, so very large histories are expensive.
|