spine-framework 0.4.3 → 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.
Files changed (320) hide show
  1. package/.env.example +13 -3
  2. package/.framework/cli/commands/bootstrap.ts +392 -0
  3. package/.framework/cli/commands/dev.ts +91 -218
  4. package/.framework/cli/commands/doctor.ts +132 -26
  5. package/.framework/cli/commands/generate.ts +38 -298
  6. package/.framework/cli/commands/init.ts +20 -1
  7. package/.framework/cli/commands/migrate.ts +36 -30
  8. package/.framework/cli/commands/migrations.ts +16 -56
  9. package/.framework/cli/commands/upgrade.ts +35 -0
  10. package/.framework/cli/commands/validate-schema.ts +147 -0
  11. package/.framework/cli/context.ts +223 -110
  12. package/.framework/cli/index.ts +6 -0
  13. package/.framework/cli/lib/project-files.ts +241 -1
  14. package/.framework/functions/_shared/agent-runner.ts +1000 -81
  15. package/.framework/functions/_shared/cil.ts +2 -1
  16. package/.framework/functions/_shared/db.ts +3 -6
  17. package/.framework/functions/_shared/hash-api-key.ts +183 -79
  18. package/.framework/functions/_shared/kb.ts +61 -7
  19. package/.framework/functions/_shared/logging.ts +562 -224
  20. package/.framework/functions/_shared/message-sequence.ts +53 -0
  21. package/.framework/functions/_shared/middleware.ts +47 -44
  22. package/.framework/functions/_shared/pipeline-runner.ts +150 -37
  23. package/.framework/functions/_shared/principal.ts +9 -6
  24. package/.framework/functions/_shared/trigger-engine.ts +149 -40
  25. package/.framework/functions/admin-data.ts +48 -31
  26. package/.framework/functions/ai-agents.ts +103 -33
  27. package/.framework/functions/api-keys.ts +301 -122
  28. package/.framework/functions/apps.ts +43 -26
  29. package/.framework/functions/cil-ingest.ts +48 -15
  30. package/.framework/functions/debug-auth.ts +14 -8
  31. package/.framework/functions/deliveries.ts +39 -12
  32. package/.framework/functions/embeddings.ts +145 -76
  33. package/.framework/functions/integration-outbound.ts +67 -27
  34. package/.framework/functions/integrations.ts +254 -127
  35. package/.framework/functions/logs.ts +273 -133
  36. package/.framework/functions/observability.ts +277 -147
  37. package/.framework/functions/pipeline-executions.ts +1 -1
  38. package/.framework/functions/pipeline-runner-background.ts +120 -0
  39. package/.framework/functions/pipelines.ts +14 -9
  40. package/.framework/functions/prompt-configs.ts +7 -4
  41. package/.framework/functions/roles.ts +8 -5
  42. package/.framework/functions/security.ts +30 -17
  43. package/.framework/functions/system-cron.ts +250 -128
  44. package/.framework/functions/tests.ts +7 -6
  45. package/.framework/functions/timers.ts +310 -107
  46. package/.framework/functions/trigger-scheduler.ts +65 -5
  47. package/.framework/functions/triggers.ts +28 -13
  48. package/.framework/functions/types.ts +13 -10
  49. package/.framework/migrations/064_episode_leases.sql +196 -0
  50. package/.framework/migrations/065_execution_tables_updated_at.sql +20 -0
  51. package/.framework/migrations/066_episode_steal_thread_merge.sql +148 -0
  52. package/.framework/migrations/067_episode_lease_stale_steal.sql +93 -0
  53. package/.framework/migrations/068_cron_schedule_timer_rpcs.sql +251 -0
  54. package/.framework/migrations/069_log_stats_window.sql +54 -0
  55. package/.framework/migrations/070_app_type_schema_rpcs.sql +166 -0
  56. package/.framework/migrations/071_timer_interval_keys.sql +96 -0
  57. package/.framework/migrations/072_slug_autofill.sql +77 -0
  58. package/.framework/migrations/073_seed_member_roles.sql +26 -0
  59. package/.framework/schemas/spine.config.schema.json +62 -0
  60. package/.framework/src/App.tsx +3 -1
  61. package/.framework/src/apps/admin/index.tsx +1141 -775
  62. package/.framework/src/components/ErrorBoundary.tsx +136 -0
  63. package/.framework/src/components/admin/AdminListPage.tsx +81 -73
  64. package/.framework/src/components/admin/AdminSidebar.tsx +92 -81
  65. package/.framework/src/components/admin/EntitySelect.tsx +181 -0
  66. package/.framework/src/components/admin/PageInstructions.tsx +86 -127
  67. package/.framework/src/components/app-shell/GenericAppShell.tsx +3 -1
  68. package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
  69. package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
  70. package/.framework/src/components/auth/TurnstileWidget.tsx +9 -6
  71. package/.framework/src/components/kb/KBArticleEditor.tsx +49 -6
  72. package/.framework/src/components/layout/Layout.tsx +4 -2
  73. package/.framework/src/components/layout/Sidebar.tsx +304 -179
  74. package/.framework/src/components/runtime/DataDetailPage.tsx +67 -35
  75. package/.framework/src/components/runtime/DataHeader.tsx +96 -48
  76. package/.framework/src/components/runtime/DataListPage.tsx +3 -26
  77. package/.framework/src/components/runtime/DataStats.tsx +10 -10
  78. package/.framework/src/components/runtime/DataTable.tsx +12 -10
  79. package/.framework/src/components/ui/sheet.tsx +24 -26
  80. package/.framework/src/components/ui/textarea.tsx +74 -48
  81. package/.framework/src/hooks/useBranding.ts +47 -0
  82. package/.framework/src/hooks/useEntityList.ts +12 -9
  83. package/.framework/src/hooks/useListSchema.ts +108 -58
  84. package/.framework/src/instructions/README.md +57 -0
  85. package/.framework/src/instructions/ai-agents.md +69 -0
  86. package/.framework/src/instructions/alerts.md +80 -0
  87. package/.framework/src/instructions/api-keys.md +87 -0
  88. package/.framework/src/instructions/apps.md +77 -0
  89. package/.framework/src/instructions/concepts/ai-and-prompts.md +150 -0
  90. package/.framework/src/instructions/concepts/apps-and-renderers.md +162 -0
  91. package/.framework/src/instructions/concepts/auth-and-api-keys.md +133 -0
  92. package/.framework/src/instructions/concepts/automation.md +186 -0
  93. package/.framework/src/instructions/concepts/concurrency-and-idempotency.md +125 -0
  94. package/.framework/src/instructions/concepts/core-vs-custom.md +124 -0
  95. package/.framework/src/instructions/concepts/design-schema.md +152 -0
  96. package/.framework/src/instructions/concepts/integrations-and-webhooks.md +127 -0
  97. package/.framework/src/instructions/concepts/observability.md +116 -0
  98. package/.framework/src/instructions/concepts/permissions.md +150 -0
  99. package/.framework/src/instructions/concepts/runtime-records.md +111 -0
  100. package/.framework/src/instructions/concepts/scope-and-accounts.md +112 -0
  101. package/.framework/src/instructions/concepts/spine-way.md +172 -0
  102. package/.framework/src/instructions/deliveries.md +59 -0
  103. package/.framework/src/instructions/guide/00-orientation.md +106 -0
  104. package/.framework/src/instructions/guide/01-install-and-first-run.md +253 -0
  105. package/.framework/src/instructions/guide/02-project-map.md +147 -0
  106. package/.framework/src/instructions/guide/03-domain-model.md +223 -0
  107. package/.framework/src/instructions/guide/04-first-app.md +227 -0
  108. package/.framework/src/instructions/guide/05-glossary.md +183 -0
  109. package/.framework/src/instructions/integrations.md +96 -0
  110. package/.framework/src/instructions/internal/cil-markers.md +83 -0
  111. package/.framework/src/instructions/invites.md +60 -0
  112. package/.framework/src/instructions/kb.md +80 -0
  113. package/.framework/src/instructions/logs.md +77 -0
  114. package/.framework/src/instructions/observability.md +61 -0
  115. package/.framework/src/instructions/operations/deployment.md +115 -0
  116. package/.framework/src/instructions/operations/migrations.md +125 -0
  117. package/.framework/src/instructions/operations/security-checklist.md +148 -0
  118. package/.framework/src/instructions/operations/troubleshooting.md +170 -0
  119. package/.framework/src/instructions/operations/upgrade.md +116 -0
  120. package/.framework/src/instructions/pipeline-executions.md +62 -0
  121. package/.framework/src/instructions/pipelines.md +78 -0
  122. package/.framework/src/instructions/prompts.md +95 -0
  123. package/.framework/src/instructions/reference/api/admin-data.md +162 -0
  124. package/.framework/src/instructions/reference/api/auth.md +113 -0
  125. package/.framework/src/instructions/reference/api/errors-and-envelopes.md +113 -0
  126. package/.framework/src/instructions/reference/app-manifest.md +150 -0
  127. package/.framework/src/instructions/reference/cli.md +124 -0
  128. package/.framework/src/instructions/reference/database.md +132 -0
  129. package/.framework/src/instructions/reference/design-schema.md +280 -0
  130. package/.framework/src/instructions/reference/env-vars.md +133 -0
  131. package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
  132. package/.framework/src/instructions/reference/functions/admin-data.md +195 -0
  133. package/.framework/src/instructions/reference/functions/admin-funnel.md +90 -0
  134. package/.framework/src/instructions/reference/functions/admin-install-funnel.md +107 -0
  135. package/.framework/src/instructions/reference/functions/admin-signals.md +102 -0
  136. package/.framework/src/instructions/reference/functions/ai-agents.md +148 -0
  137. package/.framework/src/instructions/reference/functions/ai-interactions.md +156 -0
  138. package/.framework/src/instructions/reference/functions/api-keys.md +130 -0
  139. package/.framework/src/instructions/reference/functions/apps.md +140 -0
  140. package/.framework/src/instructions/reference/functions/auth.md +99 -0
  141. package/.framework/src/instructions/reference/functions/debug-auth.md +91 -0
  142. package/.framework/src/instructions/reference/functions/deliveries.md +110 -0
  143. package/.framework/src/instructions/reference/functions/embeddings.md +166 -0
  144. package/.framework/src/instructions/reference/functions/eval-harness.md +149 -0
  145. package/.framework/src/instructions/reference/functions/index.md +103 -0
  146. package/.framework/src/instructions/reference/functions/integration-outbound.md +200 -0
  147. package/.framework/src/instructions/reference/functions/integration-routes.md +151 -0
  148. package/.framework/src/instructions/reference/functions/integrations.md +130 -0
  149. package/.framework/src/instructions/reference/functions/invites.md +182 -0
  150. package/.framework/src/instructions/reference/functions/item-progress.md +107 -0
  151. package/.framework/src/instructions/reference/functions/kb.md +166 -0
  152. package/.framework/src/instructions/reference/functions/logs.md +141 -0
  153. package/.framework/src/instructions/reference/functions/observability.md +105 -0
  154. package/.framework/src/instructions/reference/functions/pipeline-executions.md +132 -0
  155. package/.framework/src/instructions/reference/functions/pipeline-runner-background.md +121 -0
  156. package/.framework/src/instructions/reference/functions/pipelines.md +136 -0
  157. package/.framework/src/instructions/reference/functions/prompt-configs.md +121 -0
  158. package/.framework/src/instructions/reference/functions/roles.md +121 -0
  159. package/.framework/src/instructions/reference/functions/security.md +140 -0
  160. package/.framework/src/instructions/reference/functions/system-cron.md +136 -0
  161. package/.framework/src/instructions/reference/functions/system.md +106 -0
  162. package/.framework/src/instructions/reference/functions/tests.md +97 -0
  163. package/.framework/src/instructions/reference/functions/timers.md +117 -0
  164. package/.framework/src/instructions/reference/functions/trigger-scheduler.md +102 -0
  165. package/.framework/src/instructions/reference/functions/triggers.md +140 -0
  166. package/.framework/src/instructions/reference/functions/types.md +146 -0
  167. package/.framework/src/instructions/reference/seeds.md +126 -0
  168. package/.framework/src/instructions/reference/shared-modules.md +127 -0
  169. package/.framework/src/instructions/reference/spine-config.md +114 -0
  170. package/.framework/src/instructions/roles.md +80 -0
  171. package/.framework/src/instructions/runtime-detail.md +70 -0
  172. package/.framework/src/instructions/runtime-list.md +77 -0
  173. package/.framework/src/instructions/security.md +67 -0
  174. package/.framework/src/instructions/tasks/add-a-pipeline.md +110 -0
  175. package/.framework/src/instructions/tasks/add-a-role.md +96 -0
  176. package/.framework/src/instructions/tasks/add-a-timer.md +114 -0
  177. package/.framework/src/instructions/tasks/add-a-trigger.md +106 -0
  178. package/.framework/src/instructions/tasks/add-a-type.md +151 -0
  179. package/.framework/src/instructions/tasks/add-a-webhook.md +108 -0
  180. package/.framework/src/instructions/tasks/add-an-ai-agent.md +108 -0
  181. package/.framework/src/instructions/tasks/add-an-integration.md +124 -0
  182. package/.framework/src/instructions/tasks/build-a-custom-page.md +118 -0
  183. package/.framework/src/instructions/tasks/package-and-install-an-app.md +127 -0
  184. package/.framework/src/instructions/tasks/test-your-code.md +108 -0
  185. package/.framework/src/instructions/tasks/write-a-function.md +132 -0
  186. package/.framework/src/instructions/tasks/write-a-migration.md +110 -0
  187. package/.framework/src/instructions/testing.md +59 -0
  188. package/.framework/src/instructions/timers.md +82 -0
  189. package/.framework/src/instructions/triggers.md +69 -0
  190. package/.framework/src/instructions/types.md +93 -0
  191. package/.framework/src/lib/branding.ts +128 -0
  192. package/.framework/src/pages/NoAppPage.tsx +3 -1
  193. package/.framework/src/pages/NotFoundPage.tsx +62 -31
  194. package/.framework/src/pages/SetupPage.tsx +5 -2
  195. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +117 -106
  196. package/.framework/src/pages/admin/AIAgentsPage.tsx +86 -48
  197. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +153 -152
  198. package/.framework/src/pages/admin/APIKeysPage.tsx +91 -90
  199. package/.framework/src/pages/admin/AlertsConfigPage.tsx +29 -18
  200. package/.framework/src/pages/admin/AppDetailPage.tsx +6 -60
  201. package/.framework/src/pages/admin/AppsPage.tsx +104 -66
  202. package/.framework/src/pages/admin/DeliveriesPage.tsx +5 -5
  203. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +5 -23
  204. package/.framework/src/pages/admin/EmbeddingsPage.tsx +5 -26
  205. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +7 -7
  206. package/.framework/src/pages/admin/IntegrationsPage.tsx +85 -43
  207. package/.framework/src/pages/admin/LogsPage.tsx +144 -76
  208. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +223 -140
  209. package/.framework/src/pages/admin/PipelineDetailPage.tsx +134 -113
  210. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +108 -45
  211. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -115
  212. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +96 -98
  213. package/.framework/src/pages/admin/PromptConfigsPage.tsx +101 -59
  214. package/.framework/src/pages/admin/RoleDetailPage.tsx +90 -79
  215. package/.framework/src/pages/admin/RolesPage.tsx +86 -43
  216. package/.framework/src/pages/admin/SecurityPage.tsx +1 -1
  217. package/.framework/src/pages/admin/TestingDashboard.tsx +16 -28
  218. package/.framework/src/pages/admin/TimerDetailPage.tsx +117 -100
  219. package/.framework/src/pages/admin/TimersPage.tsx +90 -62
  220. package/.framework/src/pages/admin/TriggerDetailPage.tsx +90 -83
  221. package/.framework/src/pages/admin/TriggersPage.tsx +124 -99
  222. package/.framework/src/pages/admin/TypeDetailPage.tsx +152 -138
  223. package/.framework/src/pages/admin/TypesPage.tsx +90 -48
  224. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +17 -25
  225. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +17 -27
  226. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +72 -40
  227. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +321 -129
  228. package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +18 -29
  229. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +26 -38
  230. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +34 -46
  231. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +189 -103
  232. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +246 -136
  233. package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +11 -22
  234. package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +9 -20
  235. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +26 -38
  236. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +7 -26
  237. package/.framework/src/pages/auth/AuthConfirmPage.tsx +3 -1
  238. package/.framework/src/pages/auth/ForgotPasswordPage.tsx +3 -1
  239. package/.framework/src/pages/auth/LoginPage.tsx +4 -2
  240. package/.framework/src/pages/auth/RegisterPage.tsx +3 -1
  241. package/.framework/templates/agent/AGENTS.md +150 -0
  242. package/.framework/templates/agent/skills/spine-add-ai-agent/SKILL.md +58 -0
  243. package/.framework/templates/agent/skills/spine-add-automation/SKILL.md +62 -0
  244. package/.framework/templates/agent/skills/spine-add-integration/SKILL.md +63 -0
  245. package/.framework/templates/agent/skills/spine-architect/SKILL.md +86 -0
  246. package/.framework/templates/agent/skills/spine-install-app/SKILL.md +72 -0
  247. package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
  248. package/.framework/templates/agent/skills/spine-new-app/SKILL.md +67 -0
  249. package/.framework/templates/agent/skills/spine-new-function/SKILL.md +65 -0
  250. package/.framework/templates/agent/skills/spine-new-type/SKILL.md +65 -0
  251. package/.framework/templates/agent/skills/spine-schema/SKILL.md +77 -0
  252. package/.framework/templates/agent/skills/spine-test/SKILL.md +67 -0
  253. package/.framework/templates/agent/skills/spine-troubleshoot/SKILL.md +74 -0
  254. package/.framework/templates/agent/skills/spine-upgrade/SKILL.md +69 -0
  255. package/.framework/templates/project.json +5 -1
  256. package/.framework/tests/unit/account-scoping-gates.test.ts +392 -0
  257. package/.framework/tests/unit/agent-files.test.ts +355 -0
  258. package/.framework/tests/unit/agent-runner.test.ts +496 -47
  259. package/.framework/tests/unit/api-keys-create-scope-gate.test.ts +160 -0
  260. package/.framework/tests/unit/api-keys-omit-secret-fields.test.ts +38 -0
  261. package/.framework/tests/unit/bootstrap.test.ts +242 -0
  262. package/.framework/tests/unit/cli-context-api-key.test.ts +114 -0
  263. package/.framework/tests/unit/debug-auth.test.ts +38 -0
  264. package/.framework/tests/unit/deliveries-account-scoping.test.ts +185 -0
  265. package/.framework/tests/unit/episode-lease.test.ts +695 -0
  266. package/.framework/tests/unit/integration-outbound-credentials.test.ts +167 -0
  267. package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
  268. package/.framework/tests/unit/kb-ingest-lease.test.ts +19 -0
  269. package/.framework/tests/unit/logging-app-id.test.ts +75 -0
  270. package/.framework/tests/unit/pipeline-runner-background.test.ts +120 -0
  271. package/.framework/tests/unit/pipeline-runner.test.ts +153 -1
  272. package/.framework/tests/unit/principal-cache.test.ts +15 -0
  273. package/.framework/tests/unit/system-context-denial.test.ts +145 -0
  274. package/.framework/tests/unit/system-cron.test.ts +240 -0
  275. package/.framework/tests/unit/timers.test.ts +159 -0
  276. package/.framework/tests/unit/trigger-engine.test.ts +278 -91
  277. package/.framework/tests/unit/trigger-scheduler.test.ts +78 -0
  278. package/README.md +125 -166
  279. package/config/tsconfig.cli.json +2 -2
  280. package/netlify.toml +5 -0
  281. package/package.json +15 -4
  282. package/templates/project/package-lock.json +2 -2
  283. package/.framework/docs/admin-routes.md +0 -76
  284. package/.framework/docs/api-docs-progress.md +0 -38
  285. package/.framework/docs/api-governance.md +0 -146
  286. package/.framework/docs/api-testing-results.md +0 -212
  287. package/.framework/docs/assembly-launch-guide.md +0 -138
  288. package/.framework/docs/audit-results.md +0 -590
  289. package/.framework/docs/db-api-inventory.md +0 -95
  290. package/.framework/docs/gap-remediation-backlog.md +0 -103
  291. package/.framework/docs/ui-adoption-verification.md +0 -111
  292. package/.framework/docs/ui-api-coverage.md +0 -84
  293. package/.framework/docs/v2-compatibility-audit.md +0 -228
  294. package/.framework/mcp/index.ts +0 -133
  295. package/.framework/mcp/load-env.ts +0 -60
  296. package/.framework/mcp/shared/app-manifest.ts +0 -275
  297. package/.framework/mcp/shared/db.ts +0 -477
  298. package/.framework/mcp/shared/kb.ts +0 -118
  299. package/.framework/mcp/tools/architect.ts +0 -431
  300. package/.framework/mcp/tools/cil/context.ts +0 -345
  301. package/.framework/mcp/tools/cil/graph.ts +0 -242
  302. package/.framework/mcp/tools/cil/ingest.ts +0 -1217
  303. package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +0 -693
  304. package/.framework/mcp/tools/cil/pipeline.ts +0 -767
  305. package/.framework/mcp/tools/cil/project.ts +0 -791
  306. package/.framework/mcp/tools/cil/retrieval.ts +0 -336
  307. package/.framework/mcp/tools/cil/search-core.ts +0 -454
  308. package/.framework/mcp/tools/cil/search.ts +0 -97
  309. package/.framework/mcp/tools/cil/suggest.ts +0 -186
  310. package/.framework/mcp/tools/cil/validate.ts +0 -1369
  311. package/.framework/mcp/tools/kb/delete.ts +0 -139
  312. package/.framework/mcp/tools/kb/index.ts +0 -52
  313. package/.framework/mcp/tools/kb/ingest.ts +0 -84
  314. package/.framework/mcp/tools/kb/list-types.ts +0 -102
  315. package/.framework/mcp/tools/kb/publish.ts +0 -100
  316. package/.framework/mcp/tools/kb/reindex.ts +0 -86
  317. package/.framework/mcp/tools/kb/revoke.ts +0 -85
  318. package/.framework/mcp/tools/kb/search.ts +0 -101
  319. package/.framework/mcp/tools/kb/upsert.ts +0 -125
  320. package/.framework/mcp/tools/observability.ts +0 -510
@@ -192,7 +192,8 @@ export function computeContentHash(raw: string): string {
192
192
  !trimmed.match(/^\/\*\*?\s*CIL\[DOC-/) &&
193
193
  !trimmed.match(/^<!--\s*CIL\[(BEGIN|END)/) &&
194
194
  !trimmed.match(/^<!--\s*CIL\[DOC-/) &&
195
- !trimmed.match(/^#\s*CIL\[(BEGIN|END|DOC-)/)
195
+ !trimmed.match(/^#\s*CIL\[(BEGIN|END|DOC-)/) &&
196
+ !trimmed.match(/^--\s*CIL\[(BEGIN|END|DOC-)/)
196
197
  })
197
198
  const collapsed: string[] = []
198
199
  let lastBlank = false
@@ -3,7 +3,7 @@
3
3
  // CIL[DOC-RESPONSIBILITY responsibility="Owns the shared Supabase client exports and common query joins."]
4
4
  // CIL[DOC-CONTEXT module="functions/_shared/db" layer="data-access" related_files=".framework/functions/_shared/db.ts"]
5
5
  // CIL[DOC-CONTRACT inputs="[]" outputs="adminDb, getUserDb, DbResult, joins, constants" errors="[]" side_effects="[]"]
6
- // CIL[DOC-MICRO logic_summary="Imports the Supabase client and WebSocket transport, then exports configured admin/user clients, a result type, and join mappings." notable_decisions="Falls back to placeholder credentials when environment values are absent."]
6
+ // CIL[DOC-MICRO logic_summary="Imports the Supabase client and exports configured admin/user clients, a result type, and join mappings. WebSocket/ws realtime transport removed — server-side functions never subscribe to Supabase channels." notable_decisions="Falls back to placeholder credentials when environment values are absent. Realtime transport removed to reduce bundle size and eliminate unnecessary module-level initialization."]
7
7
  // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
8
8
  // CIL[DOC-STABILITY stability="stable"]
9
9
  // CIL[DOC-AUDIENCE audience="human"]
@@ -14,7 +14,6 @@
14
14
 
15
15
 
16
16
  import { createClient } from '@supabase/supabase-js'
17
- import WebSocket from 'ws'
18
17
  // CIL[DOC-VECTOR type="context" content="purpose='Supabase client and database relation join module'; No verified external callers; imports: none"]
19
18
  // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
20
19
  // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
@@ -241,12 +240,11 @@ const dbSchema: string = _env.DB_SCHEMA || 'public'
241
240
  // CIL[DOC-RESPONSIBILITY responsibility="Exports the admin-authorized Supabase client for backend use."]
242
241
  // CIL[DOC-CONTEXT module="functions/_shared/db" layer="data-access" related_files=".framework/functions/_shared/db.ts"]
243
242
  // CIL[DOC-CONTRACT inputs="[]" outputs="SupabaseClient adminDb" errors="[]" side_effects="[]"]
244
- // CIL[DOC-MICRO logic_summary="Creates a createClient instance using supabaseUrl and supabaseServiceKey, sets the schema and WebSocket transport." notable_decisions="Uses service role key for elevated privileges and WebSocket for realtime."]
243
+ // CIL[DOC-MICRO logic_summary="Creates a createClient instance using supabaseUrl and supabaseServiceKey and sets the schema. No realtime transport: server-side functions never subscribe to Supabase channels — omitting the ws transport shrinks every function bundle and eliminates an unnecessary module-level init." notable_decisions="Realtime transport removed: ws import added ~10KB to every bundle and could attempt a WebSocket handshake on module load. REST/PostgREST queries are unaffected."]
245
244
  // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
246
245
  // CIL[DOC-STABILITY stability="stable"]
247
246
  export const adminDb = createClient(supabaseUrl, supabaseServiceKey, {
248
247
  db: { schema: dbSchema },
249
- realtime: { transport: WebSocket as any }
250
248
  })
251
249
  // CIL[DOC-VECTOR type="context" content="purpose='Admin Supabase client'; No verified external callers; imports: none"]
252
250
  // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
@@ -262,14 +260,13 @@ export const adminDb = createClient(supabaseUrl, supabaseServiceKey, {
262
260
  // CIL[DOC-CONTEXT module="db" layer="functions" related_files=".framework/functions/_shared/db.ts"]
263
261
  // CIL[DOC-RESPONSIBILITY responsibility="Creates a Supabase client scoped to an end-user JWT."]
264
262
  // CIL[DOC-CONTRACT inputs="jwt: string" outputs="SupabaseClient" errors="[]" side_effects="[]"]
265
- // CIL[DOC-MICRO logic_summary="Returns a createClient instance using supabaseUrl and supabaseAnonKey, schema, WebSocket transport, and an Authorization header." notable_decisions="Uses the anon key plus Bearer token for row-level security."]
263
+ // CIL[DOC-MICRO logic_summary="Returns a createClient instance using supabaseUrl and supabaseAnonKey, schema, and an Authorization header." notable_decisions="Uses the anon key plus Bearer token for row-level security. Realtime transport omitted for same reasons as adminDb."]
266
264
  // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
267
265
  // CIL[DOC-STABILITY stability="stable"]
268
266
  export function getUserDb(jwt: string) {
269
267
  return createClient(supabaseUrl, supabaseAnonKey, {
270
268
  db: { schema: dbSchema },
271
269
  global: { headers: { Authorization: `Bearer ${jwt}` } },
272
- realtime: { transport: WebSocket as any }
273
270
  })
274
271
  }
275
272
  // CIL[END stable_id="~/.framework/functions/_shared/db.ts:getUserDb:function"]
@@ -1,81 +1,135 @@
1
1
  // CIL[BEGIN stable_id="~/.framework/functions/_shared/hash-api-key.ts:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/functions/_shared/hash-api-key.ts"]
2
- // CIL[DOC-MACRO purpose="Provides PBKDF2-based API key hashing and verification utilities"]
3
- // CIL[DOC-RESPONSIBILITY responsibility="Imports crypto primitives and exports hashApiKey and verifyApiKey"]
4
- // CIL[DOC-CONTEXT module="functions/_shared" layer="shared" related_files=".framework/functions/_shared/hash-api-key.ts"]
5
- // CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
6
- // CIL[DOC-MICRO logic_summary="Imports crypto functions used by hashing and verification" notable_decisions="[]"]
7
- // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
8
- // CIL[DOC-STABILITY stability="stable"]
9
- // CIL[DOC-AUDIENCE audience="developers"]
2
+ /**
3
+ * @module hash-api-key
4
+ * @audience both
5
+ * @layer shared-core
6
+ * @stability stable
7
+ *
8
+ * This module provides utilities for securely hashing API keys using cryptographic functions. It plays a crucial role in ensuring the integrity and confidentiality of sensitive data within the application. INVARIANT: The API keys must always be hashed before storage to prevent unauthorized access.
9
+ *
10
+ * @seeAlso crypto (provides cryptographic functions for hashing).
11
+ *
12
+ * @cil-generated
13
+ */
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
10
23
 
11
24
 
12
25
  import { randomBytes, pbkdf2Sync, timingSafeEqual } from 'crypto'
13
- // CIL[DOC-VECTOR type="context" content="purpose='Provides PBKDF2-based API key hashing and verification utilities'; No verified external callers; imports: none"]
14
- // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
15
- // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
16
- // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
17
- // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
18
- // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
19
26
  // CIL[END stable_id="~/.framework/functions/_shared/hash-api-key.ts:module_header:module_header"]
20
27
 
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
21
43
  // CIL[BEGIN stable_id="~/.framework/functions/_shared/hash-api-key.ts:ITERATIONS:constant" type="constant" identity="ITERATIONS" parent="~/.framework/functions/_shared/hash-api-key.ts"]
22
- // CIL[DOC-MACRO purpose="Defines the default PBKDF2 iteration count"]
23
- // CIL[DOC-RESPONSIBILITY responsibility="Stores the default number of PBKDF2 iterations for hashing"]
24
- // CIL[DOC-CONTEXT module="functions/_shared" layer="shared" related_files=".framework/functions/_shared/hash-api-key.ts"]
25
- // CIL[DOC-CONTRACT inputs="none" outputs="number" errors="none" side_effects="none"]
26
- // CIL[DOC-MICRO logic_summary="Constant default iteration count used by hashApiKey" notable_decisions="[]"]
27
- // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
28
- // CIL[DOC-STABILITY stability="stable"]
44
+ /**
45
+ * ITERATIONS defines the number of iterations used for hashing an API key, ensuring a high level of security. This constant is crucial for maintaining the strength of the hashing process.
46
+ *
47
+ * @sideEffects none
48
+ * @stability internal
49
+ * @cil-generated
50
+ */
29
51
  const ITERATIONS = 100000
30
- // CIL[DOC-VECTOR type="context" content="purpose='Defines the default PBKDF2 iteration count'; No verified external callers; imports: none"]
31
- // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
32
- // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
33
- // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
34
- // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
35
- // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
36
52
  // CIL[END stable_id="~/.framework/functions/_shared/hash-api-key.ts:ITERATIONS:constant"]
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
37
68
  // CIL[BEGIN stable_id="~/.framework/functions/_shared/hash-api-key.ts:KEY_LENGTH:constant" type="constant" identity="KEY_LENGTH" parent="~/.framework/functions/_shared/hash-api-key.ts"]
38
- // CIL[DOC-MACRO purpose="Defines the derived key length in bytes for PBKDF2"]
39
- // CIL[DOC-RESPONSIBILITY responsibility="Stores the PBKDF2 derived key length"]
40
- // CIL[DOC-CONTEXT module="functions/_shared" layer="shared" related_files=".framework/functions/_shared/hash-api-key.ts"]
41
- // CIL[DOC-CONTRACT inputs="none" outputs="number" errors="none" side_effects="none"]
42
- // CIL[DOC-MICRO logic_summary="Constant derived key length used by hashApiKey" notable_decisions="[]"]
43
- // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
44
- // CIL[DOC-STABILITY stability="stable"]
69
+ /**
70
+ * KEY_LENGTH defines the length of the API key used for hashing to ensure a consistent key size for security purposes.
71
+ *
72
+ * @sideEffects none
73
+ * @stability internal
74
+ * @cil-generated
75
+ */
45
76
  const KEY_LENGTH = 32
46
- // CIL[DOC-VECTOR type="context" content="purpose='Defines the derived key length in bytes for PBKDF2'; No verified external callers; imports: none"]
47
- // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
48
- // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
49
- // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
50
- // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
51
- // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
52
77
  // CIL[END stable_id="~/.framework/functions/_shared/hash-api-key.ts:KEY_LENGTH:constant"]
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+
53
93
  // CIL[BEGIN stable_id="~/.framework/functions/_shared/hash-api-key.ts:DIGEST:constant" type="constant" identity="DIGEST" parent="~/.framework/functions/_shared/hash-api-key.ts"]
54
- // CIL[DOC-MACRO purpose="Defines the digest algorithm for PBKDF2"]
55
- // CIL[DOC-RESPONSIBILITY responsibility="Stores the PBKDF2 digest algorithm"]
56
- // CIL[DOC-CONTEXT module="functions/_shared" layer="shared" related_files=".framework/functions/_shared/hash-api-key.ts"]
57
- // CIL[DOC-CONTRACT inputs="none" outputs="string" errors="none" side_effects="none"]
58
- // CIL[DOC-MICRO logic_summary="Constant digest algorithm used by hashApiKey and verifyApiKey" notable_decisions="[]"]
59
- // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
60
- // CIL[DOC-STABILITY stability="stable"]
94
+ /**
95
+ * DIGEST defines the hashing algorithm used for API key generation, specifically SHA-256. It provides a consistent and secure method for hashing within the module.
96
+ *
97
+ * @sideEffects none
98
+ * @stability internal
99
+ * @cil-generated
100
+ */
61
101
  const DIGEST = 'sha256'
62
- // CIL[DOC-VECTOR type="context" content="purpose='Defines the digest algorithm for PBKDF2'; No verified external callers; imports: none"]
63
- // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
64
- // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
65
- // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
66
- // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
67
- // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
68
102
  // CIL[END stable_id="~/.framework/functions/_shared/hash-api-key.ts:DIGEST:constant"]
69
103
 
70
104
 
105
+
106
+
107
+
108
+
109
+
110
+
111
+
112
+
113
+
114
+
115
+
116
+
117
+
118
+
119
+
71
120
  // CIL[BEGIN stable_id="~/.framework/functions/_shared/hash-api-key.ts:hashApiKey:function" type="function" identity="hashApiKey" parent="~/.framework/functions/_shared/hash-api-key.ts"]
72
- // CIL[DOC-MACRO purpose="Hash a raw API key using PBKDF2 and return a storable key record"]
73
- // CIL[DOC-RESPONSIBILITY responsibility="Derive a salted PBKDF2 hash, prefix the raw key, and return a hash string"]
74
- // CIL[DOC-CONTEXT module="functions/_shared" layer="shared" related_files=".framework/functions/_shared/hash-api-key.ts"]
75
- // CIL[DOC-CONTRACT inputs="rawKey: string, iterations = ITERATIONS" outputs="{ key_prefix: string, key_hash: string, rawKey: string }" errors="none" side_effects="Uses crypto.randomBytes and pbkdf2Sync to generate salt and hash"]
76
- // CIL[DOC-MICRO logic_summary="Extracts an 8-character prefix, generates a random salt, derives a 32-byte PBKDF2 hash, and encodes the result as pbkdf2$iterations$salt$hash" notable_decisions="[]"]
77
- // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
78
- // CIL[DOC-STABILITY stability="stable"]
121
+ /**
122
+ * Hashes a raw API key using PBKDF2 to enhance security and returns the key prefix along with the hashed key. This function is essential for securely storing API keys.
123
+ *
124
+ * @inputSpec
125
+ * - rawKey: string (required) — the raw API key to be hashed.
126
+ * - iterations: number (optional) — the number of iterations for the PBKDF2 algorithm, defaults to ITERATIONS.
127
+ * @outputSpec { key_prefix: string; key_hash: string; rawKey: string }
128
+ * @sideEffects none
129
+ * @stability internal
130
+ * @calls randomBytes, pbkdf2Sync
131
+ * @cil-generated
132
+ */
79
133
  export function hashApiKey(rawKey: string, iterations = ITERATIONS): { key_prefix: string; key_hash: string; rawKey: string } {
80
134
  const key_prefix = rawKey.slice(0, 8)
81
135
  const salt = randomBytes(16)
@@ -84,23 +138,37 @@ export function hashApiKey(rawKey: string, iterations = ITERATIONS): { key_prefi
84
138
 
85
139
  return { key_prefix, key_hash, rawKey }
86
140
  }
87
- // CIL[DOC-VECTOR type="context" content="purpose='Hash a raw API key using PBKDF2 and return a storable key record'; No verified external callers; imports: none"]
88
- // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
89
- // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
90
- // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
91
- // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
92
- // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
93
141
  // CIL[END stable_id="~/.framework/functions/_shared/hash-api-key.ts:hashApiKey:function"]
94
142
 
95
143
 
144
+
145
+
146
+
147
+
148
+
149
+
150
+
151
+
152
+
153
+
154
+
155
+
156
+
157
+
158
+
96
159
  // CIL[BEGIN stable_id="~/.framework/functions/_shared/hash-api-key.ts:verifyApiKey:function" type="function" identity="verifyApiKey" parent="~/.framework/functions/_shared/hash-api-key.ts"]
97
- // CIL[DOC-MACRO purpose="Verify a raw API key against a stored PBKDF2 hash"]
98
- // CIL[DOC-RESPONSIBILITY responsibility="Parse the stored hash, re-derive the key, and compare hashes timing-safely"]
99
- // CIL[DOC-CONTEXT module="functions/_shared" layer="shared" related_files=".framework/functions/_shared/hash-api-key.ts"]
100
- // CIL[DOC-CONTRACT inputs="rawKey: string, keyHash: string" outputs="boolean" errors="Returns false for invalid format, missing parts, non-positive iterations, base64 decode errors, or mismatched lengths" side_effects="Uses crypto.pbkdf2Sync and timingSafeEqual"]
101
- // CIL[DOC-MICRO logic_summary="Validates the keyHash format, extracts iterations and salt, derives the hash with the stored length, and compares with timingSafeEqual" notable_decisions="[]"]
102
- // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
103
- // CIL[DOC-STABILITY stability="stable"]
160
+ /**
161
+ * Verifies an API key against a stored hash to ensure its validity. This function is essential for securing access to resources by validating the provided key.
162
+ *
163
+ * @inputSpec
164
+ * - rawKey: string (required) — the API key to verify.
165
+ * - keyHash: string (required) — the hashed representation of the API key.
166
+ * @outputSpec boolean — returns true if the API key is valid, otherwise false.
167
+ * @sideEffects none
168
+ * @stability internal
169
+ * @calls pbkdf2Sync, timingSafeEqual
170
+ * @cil-generated
171
+ */
104
172
  export function verifyApiKey(rawKey: string, keyHash: string): boolean {
105
173
  if (!keyHash || !keyHash.startsWith('pbkdf2$')) {
106
174
  return false
@@ -131,10 +199,46 @@ export function verifyApiKey(rawKey: string, keyHash: string): boolean {
131
199
  return false
132
200
  }
133
201
  }
134
- // CIL[DOC-VECTOR type="context" content="purpose='Verify a raw API key against a stored PBKDF2 hash'; No verified external callers; imports: none"]
135
- // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
136
- // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
137
- // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
138
- // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
139
- // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
140
202
  // CIL[END stable_id="~/.framework/functions/_shared/hash-api-key.ts:verifyApiKey:function"]
203
+
204
+
205
+
206
+
207
+
208
+
209
+
210
+
211
+
212
+
213
+
214
+
215
+
216
+
217
+
218
+
219
+ // CIL[BEGIN stable_id="~/.framework/functions/_shared/hash-api-key.ts:omitSecretFields:function" type="function" identity="omitSecretFields" parent="~/.framework/functions/_shared/hash-api-key.ts"]
220
+ /**
221
+ * The omitSecretFields function removes sensitive fields from an object to prevent exposure of secret information. It is designed to sanitize data before further processing or transmission.
222
+ *
223
+ * @inputSpec
224
+ * row: Record<string, any> | null | undefined — the object from which secret fields will be omitted.
225
+ * @outputSpec Record<string, any> | null | undefined — a new object without the omitted fields, or the original input if it was not an object.
226
+ * @sideEffects none
227
+ * @stability internal
228
+ * @cil-generated
229
+ */
230
+ export function omitSecretFields(row: Record<string, any> | null | undefined) {
231
+ if (!row || typeof row !== 'object') return row
232
+ const copy = { ...row }
233
+ delete copy.key_value
234
+ delete copy.key_hash
235
+ return copy
236
+ }
237
+ // CIL[END stable_id="~/.framework/functions/_shared/hash-api-key.ts:omitSecretFields:function"]
238
+
239
+
240
+
241
+
242
+
243
+
244
+
@@ -218,6 +218,43 @@ function getFieldValue(record: any, field: string): string {
218
218
  }
219
219
  return ''
220
220
  }
221
+
222
+ const UUID_TOKEN_RE = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi
223
+ const HTML_TAG_RE = /<[^>]*>/g
224
+ const HTML_LOOKS_LIKE_RE = /<\/?[a-z][^>]*>/i
225
+
226
+ // CIL[BEGIN stable_id="~/.framework/functions/_shared/kb.ts:getembeddablefieldvalue:function" type="function" identity="getEmbeddableFieldValue" parent="~/.framework/functions/_shared/kb.ts"]
227
+ // CIL[DOC-MACRO purpose="Normalize a record field into embeddable plain text — strips rich-text HTML and bare UUID tokens"]
228
+ // CIL[DOC-RESPONSIBILITY responsibility="Produce the exact text that feeds both the content-hash lease and the embedder so neither markup nor identifier-only chunks enter the corpus"]
229
+ // CIL[DOC-CONTEXT module="functions/_shared" layer="kb" related_files=".framework/functions/_shared/kb.ts"]
230
+ // CIL[DOC-CONTRACT inputs="record: any, field: string, designSchema: any" outputs="string — normalized text, '' when nothing embeddable remains" errors="[]" side_effects="[]"]
231
+ // CIL[DOC-MICRO logic_summary="rich_text fields (or any value containing tags) run through an HTML-to-text pass; every field then drops bare UUID tokens — reference-id arrays like tag item ids collapse to empty and are skipped" notable_decisions="applied uniformly to all vector fields so the content hash dedupes on the same text the embedder sees"]
232
+ // CIL[DOC-CONFIDENCE pass="first" score="0.85" flags="none"]
233
+ // CIL[DOC-STABILITY stability="internal"]
234
+ function getEmbeddableFieldValue(record: any, field: string, designSchema: any): string {
235
+ let text = getFieldValue(record, field)
236
+ if (!text) return ''
237
+ const dataType = designSchema?.fields?.[field]?.data_type
238
+ if (dataType === 'rich_text' || HTML_LOOKS_LIKE_RE.test(text)) {
239
+ text = text
240
+ .replace(/<\/(p|div|h[1-6]|li|ul|ol|section|article|header|footer|blockquote|tr|table)>/gi, '\n')
241
+ .replace(/<br\s*\/?>/gi, '\n')
242
+ .replace(/<li[^>]*>/gi, '\n- ')
243
+ .replace(HTML_TAG_RE, ' ')
244
+ .replace(/&nbsp;/gi, ' ')
245
+ .replace(/&amp;/g, '&')
246
+ .replace(/&lt;/g, '<')
247
+ .replace(/&gt;/g, '>')
248
+ .replace(/&quot;/g, '"')
249
+ .replace(/&#0?39;|&apos;/g, "'")
250
+ }
251
+ return text
252
+ .replace(UUID_TOKEN_RE, ' ')
253
+ .replace(/[ \t]+/g, ' ')
254
+ .replace(/\n{3,}/g, '\n\n')
255
+ .trim()
256
+ }
257
+ // CIL[END stable_id="~/.framework/functions/_shared/kb.ts:getembeddablefieldvalue:function"]
221
258
  // CIL[DOC-VECTOR type="context" content="purpose='Retrieves a flattened string value from a record or its data object.'; No verified external callers; imports: .framework/functions/_shared/db.ts,.framework/functions/_shared/middleware.ts,.framework/functions/_shared/permissions.ts"]
222
259
  // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
223
260
  // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
@@ -596,7 +633,7 @@ async function clearEmbeddingsForItem(documentId: string): Promise<number> {
596
633
  // CIL[DOC-RESPONSIBILITY responsibility="Processes a published KB item, chunks vector fields, and persists embeddings under a content-hash document lease."]
597
634
  // CIL[DOC-CONTEXT module="functions/_shared" layer="kb" related_files=".framework/functions/_shared/db.ts,.framework/functions/_shared/kb.ts,.framework/functions/_shared/llm.ts,.framework/functions/_shared/middleware.ts,.framework/functions/_shared/permissions.ts,.framework/migrations/054_kb_ingest_lease.sql"]
598
635
  // CIL[DOC-CONTRACT inputs="ctx: CoreContext, body with id: string and optional account_id" outputs="ok: boolean; message: string; count?: number; in_progress?: boolean" errors="id is required; record not found; database errors" side_effects="revokes previous embeddings; inserts rows into embeddings table; acquires/releases a document_leases row; archives+revokes superseded documents"]
599
- // CIL[DOC-MICRO logic_summary="Fetches the item, validates status, hashes the declared vector-field values, acquires a document lease ('reused' skips unchanged work, 'processing' reports another worker owns it), then inside try/finally clears stale embeddings, batches chunks into one embeddings call, inserts, and runs supersede_keys-driven supersession before releasing the lease token." notable_decisions="Revokes existing embeddings via clearEmbeddingsForItem (not kbRevoke — that would delete the caller's own lease); supersession is driven by functionality.kb.supersede_keys declared data fields instead of a hardcoded type-slug gate; a fenced release (token mismatch) warns without throwing — the work was written but ownership was lost to a superseding acquire."]
636
+ // CIL[DOC-MICRO logic_summary="Fetches the item, validates status, hashes the normalized vector-field values (rich_text HTML stripped, bare UUID tokens dropped), acquires a document lease ('reused' skips unchanged work, 'processing' reports another worker owns it), then inside try/finally clears stale embeddings, batches chunks into one embeddings call, inserts, and runs supersede_keys-driven supersession before releasing the lease token." notable_decisions="Revokes existing embeddings via clearEmbeddingsForItem (not kbRevoke — that would delete the caller's own lease); supersession is driven by functionality.kb.supersede_keys declared data fields instead of a hardcoded type-slug gate; a fenced release (token mismatch) warns without throwing — the work was written but ownership was lost to a superseding acquire."]
600
637
  // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
601
638
  // CIL[DOC-STABILITY stability="internal"]
602
639
  // CIL[DOC-NOTE type="contract" content="Lease outcomes: 'reused' returns a live-embeddings count so callers may stamp freshness; 'processing' returns in_progress:true with NO success marker — callers that stamp embedding state must not stamp success or failure for it, the owning worker's completion reconciles the corpus."]
@@ -660,11 +697,12 @@ export async function kbIngest(ctx: CoreContext, body: { id: string; account_id?
660
697
  const securityLevel = resolveSecurityLevel(designSchema)
661
698
 
662
699
  // Content-hash lease (document_leases, migration 054): sha256 over the
663
- // declared vector-field values in order — the same text that feeds the
664
- // embedder. The lease dedupes unchanged re-embeds and fences concurrent
665
- // workers (publish trigger + detached ingest both arrive here).
700
+ // declared vector-field values in order — the same NORMALIZED text that
701
+ // feeds the embedder (HTML stripped, bare UUIDs dropped). The lease
702
+ // dedupes unchanged re-embeds and fences concurrent workers (publish
703
+ // trigger + detached ingest both arrive here).
666
704
  const contentHash = createHash('sha256')
667
- .update(vectorFields.map((field: string) => getFieldValue(record, field)).join('\n'))
705
+ .update(vectorFields.map((field: string) => getEmbeddableFieldValue(record, field, designSchema)).join('\n'))
668
706
  .digest('hex')
669
707
 
670
708
  const { data: lease, error: leaseError } = await adminDb.rpc('acquire_document_lease', {
@@ -703,7 +741,7 @@ export async function kbIngest(ctx: CoreContext, body: { id: string; account_id?
703
741
 
704
742
  const toEmbed: Array<{ field: string; chunk: string }> = []
705
743
  for (const field of vectorFields) {
706
- const raw = getFieldValue(record, field)
744
+ const raw = getEmbeddableFieldValue(record, field, designSchema)
707
745
  if (!raw.trim()) continue
708
746
 
709
747
  const isChunked = field === chunkField && raw.length > chunkSize
@@ -837,10 +875,26 @@ export async function kbIngest(ctx: CoreContext, body: { id: string; account_id?
837
875
  * @calls clearEmbeddingsForItem, adminDb.from('document_leases').delete()
838
876
  * @cil-generated
839
877
  */
840
- export async function kbRevoke(ctx: CoreContext, body: { id: string }): Promise<{ ok: boolean; message: string; count?: number }> {
878
+ export async function kbRevoke(ctx: CoreContext, body: { id: string }): Promise<{ ok: boolean; message: string; count?: number; skipped?: boolean }> {
841
879
  const { id } = body
842
880
  if (!id) throw new Error('id is required')
843
881
 
882
+ // Execution-time fence: a revoke queued by an intermediate
883
+ // item_updated event (e.g. auto_revoke_on_unpublish fired while a
884
+ // publish pipeline was mid-flight) executes long after the trigger
885
+ // condition was true. Re-check the document's CURRENT status — if it
886
+ // is published now, its embeddings are fresh and must be kept.
887
+ // Trigger-time evaluation is always stale by the time work runs.
888
+ const { data: doc } = await adminDb
889
+ .from('items')
890
+ .select('status, data')
891
+ .eq('id', id)
892
+ .maybeSingle()
893
+ const docStatus = doc?.status ?? doc?.data?.status
894
+ if (doc && docStatus === 'published') {
895
+ return { ok: true, skipped: true, message: 'document is published — embeddings retained', count: 0 }
896
+ }
897
+
844
898
  const count = await clearEmbeddingsForItem(id)
845
899
 
846
900
  // Clear any stored lease — a 'completed' lease would otherwise force