@geraldmaron/construct 1.4.2 → 1.5.1

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 (519) hide show
  1. package/.env.example +36 -0
  2. package/README.md +41 -7
  3. package/bin/construct +1027 -64
  4. package/examples/distribution/sources/deck-one-pager.md +1 -1
  5. package/lib/acp/server.mjs +21 -7
  6. package/lib/adapters-sync.mjs +2 -1
  7. package/lib/audit-trail.mjs +185 -2
  8. package/lib/beads/auto-close.mjs +2 -1
  9. package/lib/beads/drift.mjs +2 -1
  10. package/lib/bridges/copilot-proxy.mjs +20 -5
  11. package/lib/certification/skill-inventory.mjs +10 -1
  12. package/lib/cli/approvals.mjs +147 -0
  13. package/lib/cli-commands.mjs +105 -14
  14. package/lib/comment-lint.mjs +127 -8
  15. package/lib/config/schema.mjs +6 -29
  16. package/lib/config/source-target-registry.mjs +70 -0
  17. package/lib/config/source-targets.mjs +179 -205
  18. package/lib/contracts/coverage.mjs +77 -0
  19. package/lib/contracts/validate.mjs +102 -13
  20. package/lib/contracts/violation-log.mjs +50 -4
  21. package/lib/db/migrate.mjs +69 -0
  22. package/lib/db/migrations/001_orchestration_runs.sql +9 -0
  23. package/lib/db/migrations/002_queue_provider.sql +50 -0
  24. package/lib/db/migrations/003_worker_registry.sql +17 -0
  25. package/lib/db/migrations/004_trace_events.sql +16 -0
  26. package/lib/db/migrations/005_shared_memory.sql +15 -0
  27. package/lib/db/migrations/006_orchestration_runs_tenant.sql +5 -0
  28. package/lib/decisions/enforced-baseline.json +0 -2
  29. package/lib/decisions/registry.mjs +3 -2
  30. package/lib/deployment/parity-contract.mjs +1 -1
  31. package/lib/deployment-mode.mjs +78 -2
  32. package/lib/diagram-export.mjs +10 -1
  33. package/lib/distill.mjs +5 -2
  34. package/lib/docs-verify.mjs +2 -1
  35. package/lib/doctor/cli.mjs +1 -0
  36. package/lib/doctor/command-on-path.mjs +24 -0
  37. package/lib/doctor/diagnosis.mjs +89 -0
  38. package/lib/doctor/embedding-health.mjs +50 -0
  39. package/lib/doctor/engine-health.mjs +93 -0
  40. package/lib/doctor/graph-validate.mjs +47 -0
  41. package/lib/doctor/index.mjs +18 -4
  42. package/lib/doctor/sidecar-providers.mjs +56 -0
  43. package/lib/doctor/watchers/consistency.mjs +93 -1
  44. package/lib/doctor/watchers/cx-budget.mjs +1 -1
  45. package/lib/doctor/watchers/graph-staleness.mjs +1 -1
  46. package/lib/doctor/watchers/mcp-protocol.mjs +17 -0
  47. package/lib/doctor/watchers/oracle-liveness.mjs +92 -0
  48. package/lib/doctor/watchers/orchestration-runs.mjs +108 -0
  49. package/lib/doctor/watchers/provider-breaker.mjs +78 -0
  50. package/lib/document-export.mjs +52 -27
  51. package/lib/document-extract/docling-client.mjs +9 -5
  52. package/lib/document-extract/docling-sidecar.py +30 -0
  53. package/lib/document-extract.mjs +1 -1
  54. package/lib/document-ingest.mjs +9 -0
  55. package/lib/embed/approval-queue.mjs +184 -88
  56. package/lib/embed/authority-guard.mjs +65 -2
  57. package/lib/embed/capability-jobs.mjs +365 -0
  58. package/lib/embed/capability-lifecycle.mjs +219 -0
  59. package/lib/embed/capability-loader.mjs +303 -0
  60. package/lib/embed/capability-runtime.mjs +58 -0
  61. package/lib/embed/cli.mjs +185 -8
  62. package/lib/embed/config.mjs +4 -0
  63. package/lib/embed/daemon.mjs +125 -6
  64. package/lib/embed/demand-fetch.mjs +190 -54
  65. package/lib/embed/inbox.mjs +12 -10
  66. package/lib/embed/presets/ops-triage.mjs +236 -0
  67. package/lib/embed/presets/pm-feedback.mjs +341 -0
  68. package/lib/embed/presets/tpm.mjs +401 -0
  69. package/lib/embed/providers/jira.mjs +72 -1
  70. package/lib/embed/providers/registry.mjs +140 -33
  71. package/lib/embedded-contract/capability.mjs +4 -0
  72. package/lib/embedded-contract/execution.mjs +1 -1
  73. package/lib/embedded-contract/model-resolve.mjs +53 -3
  74. package/lib/embedded-contract/workflow-defs.mjs +48 -73
  75. package/lib/embedded-contract/workflow-invoke.mjs +144 -4
  76. package/lib/embedded-contract/workflows/architecture-review.manifest.json +15 -0
  77. package/lib/embedded-contract/workflows/data-structure.manifest.json +14 -0
  78. package/lib/embedded-contract/workflows/evidence-ingest.manifest.json +14 -0
  79. package/lib/embedded-contract/workflows/memo-draft.manifest.json +14 -0
  80. package/lib/embedded-contract/workflows/operations-triage.manifest.json +18 -0
  81. package/lib/embedded-contract/workflows/operations.manifest.json +18 -0
  82. package/lib/embedded-contract/workflows/pm-feedback.manifest.json +18 -0
  83. package/lib/embedded-contract/workflows/prd-draft.manifest.json +15 -0
  84. package/lib/embedded-contract/workflows/proposal-review.manifest.json +15 -0
  85. package/lib/embedded-contract/workflows/research-synthesis.manifest.json +14 -0
  86. package/lib/embedded-contract/workflows/risk-review.manifest.json +15 -0
  87. package/lib/embedded-contract/workflows/structure-notes.manifest.json +14 -0
  88. package/lib/embedded-contract/workflows/transcript-process.manifest.json +14 -0
  89. package/lib/embedded-contract/workflows/triage.manifest.json +14 -0
  90. package/lib/env-config.mjs +50 -9
  91. package/lib/extensions/index.mjs +27 -0
  92. package/lib/extensions/loader.mjs +120 -0
  93. package/lib/extensions/manifest-schema.mjs +141 -0
  94. package/lib/extensions/manifests/anthropic.manifest.json +12 -0
  95. package/lib/extensions/manifests/atlassian-confluence.manifest.json +12 -0
  96. package/lib/extensions/manifests/atlassian-jira.manifest.json +53 -0
  97. package/lib/extensions/manifests/directory.manifest.json +12 -0
  98. package/lib/extensions/manifests/docling.manifest.json +37 -0
  99. package/lib/extensions/manifests/echo.manifest.json +8 -0
  100. package/lib/extensions/manifests/feedback.manifest.json +12 -0
  101. package/lib/extensions/manifests/github-copilot.manifest.json +12 -0
  102. package/lib/extensions/manifests/github.manifest.json +52 -0
  103. package/lib/extensions/manifests/linear.manifest.json +50 -0
  104. package/lib/extensions/manifests/local.manifest.json +12 -0
  105. package/lib/extensions/manifests/ollama.manifest.json +12 -0
  106. package/lib/extensions/manifests/openai.manifest.json +12 -0
  107. package/lib/extensions/manifests/openrouter-anthropic.manifest.json +12 -0
  108. package/lib/extensions/manifests/openrouter-deepseek.manifest.json +12 -0
  109. package/lib/extensions/manifests/openrouter-google.manifest.json +12 -0
  110. package/lib/extensions/manifests/openrouter-llama.manifest.json +12 -0
  111. package/lib/extensions/manifests/openrouter-qwen.manifest.json +12 -0
  112. package/lib/extensions/manifests/openrouter.manifest.json +12 -0
  113. package/lib/extensions/manifests/postgres.manifest.json +12 -0
  114. package/lib/extensions/manifests/salesforce.manifest.json +12 -0
  115. package/lib/extensions/manifests/slack.manifest.json +58 -0
  116. package/lib/extensions/manifests/whisper.manifest.json +36 -0
  117. package/lib/extensions/validate.mjs +175 -0
  118. package/lib/features.mjs +13 -9
  119. package/lib/flows/checkpoint.mjs +184 -0
  120. package/lib/flows/constants.mjs +27 -0
  121. package/lib/flows/define.mjs +146 -0
  122. package/lib/flows/engine.mjs +249 -0
  123. package/lib/flows/errors.mjs +19 -0
  124. package/lib/flows/index.mjs +27 -0
  125. package/lib/flows/joins.mjs +18 -0
  126. package/lib/flows/schema.mjs +90 -0
  127. package/lib/flows/state.mjs +31 -0
  128. package/lib/frameworks/loader.mjs +99 -0
  129. package/lib/frameworks/schema.mjs +157 -0
  130. package/lib/graph/build-from-corpus.mjs +67 -0
  131. package/lib/graph/build-from-embed.mjs +82 -0
  132. package/lib/graph/build-from-registry.mjs +164 -3
  133. package/lib/graph/cli.mjs +456 -12
  134. package/lib/graph/gap-queries.mjs +156 -0
  135. package/lib/graph/gaps.mjs +41 -0
  136. package/lib/graph/impacted.mjs +129 -0
  137. package/lib/graph/runtime-evidence.mjs +177 -0
  138. package/lib/graph/security-coverage.mjs +113 -0
  139. package/lib/graph/staleness.mjs +241 -10
  140. package/lib/graph/store.mjs +24 -7
  141. package/lib/graph/validate.mjs +161 -0
  142. package/lib/headhunt.mjs +9 -8
  143. package/lib/health-check.mjs +15 -7
  144. package/lib/hook-health.mjs +1 -1
  145. package/lib/hooks/agent-tracker.mjs +10 -4
  146. package/lib/hooks/edit-guard.mjs +3 -3
  147. package/lib/hooks/graph-impact-advisory.mjs +2 -2
  148. package/lib/hooks/guard-bash.mjs +41 -15
  149. package/lib/hooks/mcp-health-check.mjs +11 -4
  150. package/lib/hooks/model-fallback.mjs +50 -6
  151. package/lib/hooks/orchestration-dispatch-guard.mjs +14 -3
  152. package/lib/hooks/pre-compact.mjs +181 -173
  153. package/lib/hooks/rule-verifier.mjs +2 -1
  154. package/lib/hooks/session-reflect.mjs +2 -1
  155. package/lib/hooks/session-start.mjs +3 -3
  156. package/lib/host-capabilities.mjs +13 -2
  157. package/lib/host-disposition.mjs +19 -7
  158. package/lib/identity.mjs +92 -0
  159. package/lib/ingest/degraded-extract.mjs +96 -0
  160. package/lib/ingest/docling-remote.mjs +2 -1
  161. package/lib/ingest/provider-extract.mjs +7 -19
  162. package/lib/ingest/sidecar-providers.mjs +196 -0
  163. package/lib/ingest-tooling.mjs +11 -5
  164. package/lib/init-unified.mjs +57 -45
  165. package/lib/init-update.mjs +2 -1
  166. package/lib/install/legacy-global-cleanup.mjs +25 -0
  167. package/lib/intake/daemon.mjs +99 -8
  168. package/lib/intake/git-queue.mjs +110 -38
  169. package/lib/intake/queue-registry.mjs +50 -0
  170. package/lib/intake/queue.mjs +133 -17
  171. package/lib/intake/session-prelude.mjs +57 -8
  172. package/lib/integrations/intake-integrations.mjs +61 -111
  173. package/lib/intent-classifier.mjs +43 -5
  174. package/lib/libreoffice-export.mjs +8 -8
  175. package/lib/logging/rotate.mjs +5 -4
  176. package/lib/mcp/broker.mjs +332 -17
  177. package/lib/mcp/denied-store.mjs +95 -0
  178. package/lib/mcp/destructive-gate.mjs +30 -0
  179. package/lib/mcp/dispatch-envelope.mjs +199 -0
  180. package/lib/mcp/memory-bridge.mjs +2 -1
  181. package/lib/mcp/server.mjs +154 -1411
  182. package/lib/mcp/tool-definitions-memory.mjs +376 -0
  183. package/lib/mcp/tool-definitions-project.mjs +271 -0
  184. package/lib/mcp/tool-definitions-skills.mjs +311 -0
  185. package/lib/mcp/tool-definitions-workflow.mjs +398 -0
  186. package/lib/mcp/tool-definitions.mjs +25 -0
  187. package/lib/mcp/tool-registry.mjs +107 -0
  188. package/lib/mcp/tool-safety.mjs +1 -0
  189. package/lib/mcp/tools/orchestration-delegation-next.tool.mjs +68 -0
  190. package/lib/mcp/tools/orchestration-run.mjs +165 -25
  191. package/lib/mcp/tools/orchestration-task-result.tool.mjs +77 -0
  192. package/lib/mcp/tools/provider-write.mjs +187 -0
  193. package/lib/mcp/tools/scope.mjs +0 -3
  194. package/lib/mcp/tools/skills.mjs +1 -1
  195. package/lib/mcp/tools/storage.mjs +0 -8
  196. package/lib/mcp/transport/auth.mjs +238 -0
  197. package/lib/mcp/transport/http.mjs +136 -0
  198. package/lib/mcp/transport/mode.mjs +31 -0
  199. package/lib/mcp/transport/stdio.mjs +22 -0
  200. package/lib/mcp-catalog.json +4 -4
  201. package/lib/mcp-manager.mjs +34 -23
  202. package/lib/mcp-platform-config.mjs +31 -7
  203. package/lib/mode-capabilities.mjs +109 -0
  204. package/lib/model-router.mjs +42 -9
  205. package/lib/models/catalog.mjs +40 -1
  206. package/lib/net-guard.mjs +247 -0
  207. package/lib/observation-store.mjs +6 -2
  208. package/lib/ollama/provision-context.mjs +2 -1
  209. package/lib/opencode-config.mjs +22 -3
  210. package/lib/opencode-runtime-plugin.mjs +120 -2
  211. package/lib/oracle/actions.mjs +204 -10
  212. package/lib/oracle/cli.mjs +24 -3
  213. package/lib/oracle/dispatch.mjs +2 -1
  214. package/lib/oracle/execute.mjs +2 -2
  215. package/lib/oracle/gaps.mjs +3 -3
  216. package/lib/oracle/heartbeat.mjs +53 -0
  217. package/lib/oracle/read-model.mjs +69 -13
  218. package/lib/oracle/reconcile.mjs +3 -46
  219. package/lib/oracle/routing.mjs +37 -31
  220. package/lib/oracle/synthesize.mjs +1 -1
  221. package/lib/orchestration/classification.mjs +434 -0
  222. package/lib/orchestration/delegation-flow.mjs +132 -0
  223. package/lib/orchestration/flow-selection.mjs +418 -0
  224. package/lib/orchestration/gates.mjs +244 -0
  225. package/lib/orchestration/host-sampling.mjs +121 -0
  226. package/lib/orchestration/policy-constants.mjs +48 -0
  227. package/lib/orchestration/provider-outcome.mjs +197 -0
  228. package/lib/orchestration/readiness.mjs +114 -13
  229. package/lib/orchestration/run-store-postgres.mjs +32 -26
  230. package/lib/orchestration/run-store-sqlite.mjs +8 -14
  231. package/lib/orchestration/run-store.mjs +25 -12
  232. package/lib/orchestration/runtime.mjs +446 -39
  233. package/lib/orchestration/store.mjs +87 -12
  234. package/lib/orchestration/trace-store.mjs +125 -0
  235. package/lib/orchestration/web-capability.mjs +3 -2
  236. package/lib/orchestration/worker-runtime.mjs +162 -0
  237. package/lib/orchestration/worker.mjs +528 -89
  238. package/lib/orchestration-policy.mjs +67 -1111
  239. package/lib/packs/cli.mjs +144 -0
  240. package/lib/packs/core-pack.mjs +112 -0
  241. package/lib/packs/enablement.mjs +187 -0
  242. package/lib/packs/index.mjs +23 -0
  243. package/lib/packs/loader.mjs +176 -0
  244. package/lib/packs/manifest-schema.mjs +58 -0
  245. package/lib/packs/prompts.mjs +120 -0
  246. package/lib/packs/validate.mjs +208 -0
  247. package/lib/plugin-registry.mjs +4 -5
  248. package/lib/policy/audit-gate.mjs +42 -0
  249. package/lib/policy/consumption-budget.mjs +149 -0
  250. package/lib/policy/engine.mjs +81 -6
  251. package/lib/policy/role-authority.mjs +89 -0
  252. package/lib/prompt-composer.js +19 -3
  253. package/lib/providers/contract/adapters/confluence/governed-write.mjs +215 -0
  254. package/lib/providers/contract/adapters/confluence/manifest.json +17 -0
  255. package/lib/providers/contract/adapters/confluence/transport.mjs +135 -0
  256. package/lib/providers/contract/adapters/github/governed-write.mjs +121 -0
  257. package/lib/providers/contract/adapters/github/index.mjs +38 -3
  258. package/lib/providers/contract/adapters/jira/adf.mjs +151 -0
  259. package/lib/providers/contract/adapters/jira/createmeta.mjs +143 -0
  260. package/lib/providers/contract/adapters/jira/governed-write.mjs +182 -0
  261. package/lib/providers/contract/adapters/jira/manifest.json +17 -0
  262. package/lib/providers/contract/adapters/jira/transport.mjs +119 -0
  263. package/lib/providers/contract.mjs +207 -0
  264. package/lib/providers/credential-bootstrap.mjs +7 -4
  265. package/lib/providers/credential-sources.mjs +14 -2
  266. package/lib/providers/directory/index.mjs +273 -0
  267. package/lib/providers/feedback/index.mjs +392 -0
  268. package/lib/providers/filter-audit.mjs +68 -0
  269. package/lib/providers/filter-schema.mjs +54 -0
  270. package/lib/providers/github/index.mjs +31 -0
  271. package/lib/providers/instance-config.mjs +215 -0
  272. package/lib/providers/op-locate.mjs +96 -0
  273. package/lib/providers/op-run.mjs +64 -9
  274. package/lib/providers/registry.mjs +26 -3
  275. package/lib/providers/secret-audit-wiring.mjs +6 -0
  276. package/lib/providers/secret-resolver.mjs +240 -23
  277. package/lib/publish-template.mjs +6 -3
  278. package/lib/publish-tooling.mjs +4 -0
  279. package/lib/publish.mjs +32 -4
  280. package/lib/queue/pg-queue.mjs +395 -0
  281. package/lib/reflect.mjs +2 -1
  282. package/lib/registry/assemble.mjs +28 -2
  283. package/lib/registry/consolidation.mjs +8 -1
  284. package/lib/registry/custom-scaffold.mjs +200 -0
  285. package/lib/registry/custom-schema.mjs +137 -0
  286. package/lib/registry/loader.mjs +8 -3
  287. package/lib/registry/manifests/format-engines.default.json +15 -0
  288. package/lib/registry/manifests/surface-map.default.json +46 -0
  289. package/lib/registry/retired-paths.mjs +1 -0
  290. package/lib/registry/surface-map.mjs +100 -50
  291. package/lib/render-visual-check.mjs +240 -0
  292. package/lib/resources/budget.mjs +40 -17
  293. package/lib/roles/flavor-bindings.mjs +36 -14
  294. package/lib/roles/gateway.mjs +15 -8
  295. package/lib/roots.mjs +107 -0
  296. package/lib/runtime/uv-bootstrap.mjs +32 -6
  297. package/lib/runtime/whisper-bootstrap.mjs +11 -3
  298. package/lib/runtime-env.mjs +5 -2
  299. package/lib/scheduler/index.mjs +8 -20
  300. package/lib/schema-infer.mjs +31 -2
  301. package/lib/scopes/lifecycle.mjs +29 -25
  302. package/lib/scopes/loader.mjs +49 -12
  303. package/lib/scopes/teams.mjs +1 -1
  304. package/lib/security/ingest-boundary.mjs +80 -0
  305. package/lib/security/recall-wrapper.mjs +99 -0
  306. package/lib/security/trust.mjs +132 -0
  307. package/lib/service-manager.mjs +38 -19
  308. package/lib/setup.mjs +41 -23
  309. package/lib/skills-apply.mjs +4 -2
  310. package/lib/specialists/postconditions.mjs +2 -2
  311. package/lib/state-root.mjs +147 -0
  312. package/lib/status.mjs +597 -6
  313. package/lib/storage/admin.mjs +77 -5
  314. package/lib/storage/backend-registry.mjs +73 -0
  315. package/lib/storage/backend.mjs +63 -9
  316. package/lib/storage/embeddings-openai.mjs +12 -2
  317. package/lib/storage/hybrid-query.mjs +11 -3
  318. package/lib/storage/retrieval-hardening.mjs +384 -0
  319. package/lib/storage/shared-memory.mjs +158 -0
  320. package/lib/storage/vector-client.mjs +69 -2
  321. package/lib/team/health.mjs +72 -0
  322. package/lib/telemetry/backends/local.mjs +9 -3
  323. package/lib/telemetry/client.mjs +3 -7
  324. package/lib/template-registry.mjs +10 -12
  325. package/lib/tenant/context.mjs +82 -0
  326. package/lib/tenant/isolation.mjs +129 -0
  327. package/lib/test-corpus-inventory.mjs +104 -2
  328. package/lib/uninstall/uninstall.mjs +78 -12
  329. package/lib/validator.mjs +4 -6
  330. package/lib/worker/entrypoint.mjs +2 -1
  331. package/lib/worker/run.mjs +2 -2
  332. package/lib/worker/trace.mjs +5 -11
  333. package/lib/workflow-state.mjs +52 -8
  334. package/lib/workflows/liveness.mjs +203 -0
  335. package/lib/workflows/loader.mjs +177 -0
  336. package/lib/workflows/manifest-schema.mjs +71 -0
  337. package/lib/workflows/surface-parity.mjs +118 -0
  338. package/lib/workflows/validate.mjs +127 -0
  339. package/lib/writes/control-plane.mjs +140 -0
  340. package/lib/writes/envelope.mjs +206 -0
  341. package/lib/writes/sent-log.mjs +86 -0
  342. package/lib/writes/write-intent.mjs +109 -0
  343. package/package.json +16 -8
  344. package/personas/construct.md +12 -3
  345. package/registry/capabilities.json +143 -36
  346. package/rules/common/comments.md +1 -0
  347. package/schemas/project-config.schema.json +0 -12
  348. package/schemas/unified-registry.schema.json +2 -4
  349. package/scripts/sync-specialists.mjs +284 -81
  350. package/skills/docs/adr-workflow.md +1 -1
  351. package/skills/docs/codebase-research-workflow.md +3 -3
  352. package/skills/docs/prd-workflow.md +5 -6
  353. package/skills/docs/runbook-workflow.md +2 -2
  354. package/skills/docs/user-research-workflow.md +3 -3
  355. package/skills/operating/fleet-health-routing.md +77 -0
  356. package/skills/roles/ai-engineer.md +1 -1
  357. package/skills/roles/architect.md +0 -1
  358. package/skills/roles/business-strategist.md +1 -1
  359. package/skills/roles/data-analyst.telemetry.md +1 -1
  360. package/skills/roles/data-engineer.md +1 -1
  361. package/skills/roles/data-engineer.pipeline.md +1 -1
  362. package/skills/roles/data-engineer.vector-retrieval.md +1 -2
  363. package/skills/roles/data-engineer.warehouse.md +1 -1
  364. package/skills/roles/designer.accessibility.md +1 -1
  365. package/skills/roles/designer.md +0 -1
  366. package/skills/roles/devil-advocate.md +1 -1
  367. package/skills/roles/docs-keeper.md +1 -1
  368. package/skills/roles/evaluator.md +1 -1
  369. package/skills/roles/explorer.md +1 -1
  370. package/skills/roles/platform-engineer.md +1 -1
  371. package/skills/roles/qa.ai-eval.md +1 -2
  372. package/skills/roles/qa.api-contract.md +0 -1
  373. package/skills/roles/qa.data-pipeline.md +0 -1
  374. package/skills/roles/qa.md +0 -1
  375. package/skills/roles/qa.web-ui.md +0 -1
  376. package/skills/roles/release-manager.md +1 -1
  377. package/skills/roles/researcher.md +0 -2
  378. package/skills/roles/reviewer.md +0 -3
  379. package/skills/roles/security.ai.md +1 -1
  380. package/skills/roles/security.legal-compliance.md +1 -1
  381. package/skills/roles/security.md +0 -1
  382. package/skills/roles/security.privacy.md +0 -1
  383. package/skills/roles/security.supply-chain.md +1 -1
  384. package/skills/roles/sre.md +1 -1
  385. package/skills/roles/test-automation.md +1 -1
  386. package/skills/roles/trace-reviewer.md +1 -1
  387. package/skills/roles/ux-researcher.md +1 -1
  388. package/specialists/artifact-manifest.json +36 -36
  389. package/specialists/org/contracts/accessibility-to-qa.json +11 -3
  390. package/specialists/org/contracts/any-to-business-strategist.json +19 -7
  391. package/specialists/org/contracts/any-to-debugger.json +7 -1
  392. package/specialists/org/contracts/any-to-designer.json +7 -1
  393. package/specialists/org/contracts/any-to-docs-keeper.json +18 -4
  394. package/specialists/org/contracts/any-to-explorer.json +13 -4
  395. package/specialists/org/contracts/any-to-sre-incident.json +6 -2
  396. package/specialists/org/contracts/any-to-trace-reviewer.json +16 -4
  397. package/specialists/org/contracts/architect-to-ai-engineer.json +6 -2
  398. package/specialists/org/contracts/architect-to-data-engineer.json +17 -5
  399. package/specialists/org/contracts/architect-to-devil-advocate.json +11 -3
  400. package/specialists/org/contracts/architect-to-engineer.json +20 -4
  401. package/specialists/org/contracts/architect-to-evaluator.json +15 -5
  402. package/specialists/org/contracts/architect-to-legal-compliance.json +15 -5
  403. package/specialists/org/contracts/architect-to-operations.json +17 -4
  404. package/specialists/org/contracts/architect-to-platform-engineer.json +20 -4
  405. package/specialists/org/contracts/construct-to-orchestrator.json +10 -2
  406. package/specialists/org/contracts/data-analyst-to-product-manager.json +11 -2
  407. package/specialists/org/contracts/engineer-to-qa.json +20 -4
  408. package/specialists/org/contracts/engineer-to-reviewer.json +29 -5
  409. package/specialists/org/contracts/explorer-to-engineer.json +11 -3
  410. package/specialists/org/contracts/legal-compliance-to-release-manager.json +12 -4
  411. package/specialists/org/contracts/operations-to-user.json +53 -0
  412. package/specialists/org/contracts/operations-triage-output.json +53 -0
  413. package/specialists/org/contracts/pm-requirements-candidates.json +53 -0
  414. package/specialists/org/contracts/product-manager-to-architect.json +30 -10
  415. package/specialists/org/contracts/product-manager-to-data-analyst.json +13 -3
  416. package/specialists/org/contracts/product-manager-to-ux-researcher.json +15 -5
  417. package/specialists/org/contracts/qa-to-release-manager.json +11 -3
  418. package/specialists/org/contracts/researcher-to-architect.json +10 -2
  419. package/specialists/org/contracts/researcher-to-product-manager.json +16 -5
  420. package/specialists/org/contracts/reviewer-to-security.json +17 -3
  421. package/specialists/org/contracts/test-automation-to-engineer.json +11 -3
  422. package/specialists/org/contracts/trace-reviewer-to-sre.json +12 -4
  423. package/specialists/org/contracts/user-to-construct.json +11 -4
  424. package/specialists/org/frameworks/cx-architect-constraint-option-failure.md +66 -0
  425. package/specialists/org/frameworks/cx-engineer-feasibility-blast-radius.md +66 -0
  426. package/specialists/org/frameworks/cx-ops-dependency-sequencing.md +74 -0
  427. package/specialists/org/frameworks/cx-pm-value-tradeoff.md +66 -0
  428. package/specialists/org/frameworks/cx-qa-risk-based-coverage.md +69 -0
  429. package/specialists/org/groups/engineering-group.json +2 -6
  430. package/specialists/org/groups/governance-group.json +2 -3
  431. package/specialists/org/groups/operations-group.json +5 -8
  432. package/specialists/org/groups/product-group.json +4 -8
  433. package/specialists/org/groups/quality-group.json +3 -7
  434. package/specialists/org/groups/strategy-group.json +6 -9
  435. package/specialists/org/models.json.example +8 -0
  436. package/specialists/org/scopes/creative.json +6 -1
  437. package/specialists/org/scopes/operations.json +7 -1
  438. package/specialists/org/scopes/research.json +6 -1
  439. package/specialists/org/scopes/rnd.json +7 -1
  440. package/specialists/org/specialists/cx-architect.json +27 -8
  441. package/specialists/org/specialists/cx-designer.json +22 -8
  442. package/specialists/org/specialists/cx-engineer.json +71 -7
  443. package/specialists/org/specialists/cx-operations.json +89 -15
  444. package/specialists/org/specialists/cx-orchestrator.json +16 -4
  445. package/specialists/org/specialists/cx-product-manager.json +28 -9
  446. package/specialists/org/specialists/cx-qa.json +16 -6
  447. package/specialists/org/specialists/cx-researcher.json +40 -7
  448. package/specialists/org/specialists/cx-reviewer.json +61 -11
  449. package/specialists/org/specialists/cx-security.json +30 -10
  450. package/specialists/org/teams/design-team.json +3 -4
  451. package/specialists/org/teams/engineering-team.json +3 -10
  452. package/specialists/org/teams/governance-team.json +3 -5
  453. package/specialists/org/teams/operations-team.json +6 -12
  454. package/specialists/org/teams/product-management-team.json +2 -3
  455. package/specialists/org/teams/quality-team.json +4 -12
  456. package/specialists/org/teams/research-team.json +3 -3
  457. package/specialists/org/teams/strategy-team.json +7 -14
  458. package/specialists/prompts/cx-architect.md +20 -1
  459. package/specialists/prompts/cx-data-analyst.md +1 -1
  460. package/specialists/prompts/cx-designer.md +12 -4
  461. package/specialists/prompts/cx-engineer.md +15 -1
  462. package/specialists/prompts/cx-operations.md +14 -2
  463. package/specialists/prompts/cx-orchestrator.md +9 -5
  464. package/specialists/prompts/cx-product-manager.md +5 -1
  465. package/specialists/prompts/cx-qa.md +6 -2
  466. package/specialists/prompts/cx-researcher.md +25 -30
  467. package/specialists/prompts/cx-reviewer.md +19 -1
  468. package/specialists/prompts/cx-security.md +5 -1
  469. package/templates/distribution/construct-brand.typ +123 -59
  470. package/templates/distribution/construct-decision.typ +6 -3
  471. package/templates/distribution/construct-pdf.typ +11 -4
  472. package/templates/distribution/construct-prd.typ +6 -3
  473. package/templates/distribution/construct-research.typ +7 -4
  474. package/lib/providers/contract/adapters/git/index.mjs +0 -115
  475. package/lib/providers/contract/adapters/slack/index.mjs +0 -175
  476. package/specialists/org/contracts/business-strategist-to-product-manager.json +0 -39
  477. package/specialists/org/contracts/construct-to-rd-lead.json +0 -53
  478. package/specialists/org/contracts/data-engineer-to-platform-engineer.json +0 -36
  479. package/specialists/org/contracts/designer-to-accessibility.json +0 -36
  480. package/specialists/org/contracts/platform-engineer-to-engineer.json +0 -31
  481. package/specialists/org/contracts/qa-to-test-automation.json +0 -42
  482. package/specialists/org/contracts/rd-lead-to-architect.json +0 -38
  483. package/specialists/org/contracts/sre-to-release-manager.json +0 -36
  484. package/specialists/org/specialists/cx-accessibility.json +0 -69
  485. package/specialists/org/specialists/cx-ai-engineer.json +0 -75
  486. package/specialists/org/specialists/cx-business-strategist.json +0 -72
  487. package/specialists/org/specialists/cx-data-engineer.json +0 -71
  488. package/specialists/org/specialists/cx-devil-advocate.json +0 -71
  489. package/specialists/org/specialists/cx-docs-keeper.json +0 -82
  490. package/specialists/org/specialists/cx-evaluator.json +0 -69
  491. package/specialists/org/specialists/cx-explorer.json +0 -69
  492. package/specialists/org/specialists/cx-legal-compliance.json +0 -74
  493. package/specialists/org/specialists/cx-oracle.json +0 -46
  494. package/specialists/org/specialists/cx-platform-engineer.json +0 -80
  495. package/specialists/org/specialists/cx-rd-lead.json +0 -73
  496. package/specialists/org/specialists/cx-release-manager.json +0 -77
  497. package/specialists/org/specialists/cx-sre.json +0 -94
  498. package/specialists/org/specialists/cx-test-automation.json +0 -69
  499. package/specialists/org/specialists/cx-trace-reviewer.json +0 -69
  500. package/specialists/org/specialists/cx-ux-researcher.json +0 -68
  501. package/specialists/org/teams/accessibility-team.json +0 -39
  502. package/specialists/org/teams/ux-research-team.json +0 -39
  503. package/specialists/prompts/cx-accessibility.md +0 -55
  504. package/specialists/prompts/cx-ai-engineer.md +0 -124
  505. package/specialists/prompts/cx-business-strategist.md +0 -58
  506. package/specialists/prompts/cx-data-engineer.md +0 -55
  507. package/specialists/prompts/cx-devil-advocate.md +0 -59
  508. package/specialists/prompts/cx-docs-keeper.md +0 -164
  509. package/specialists/prompts/cx-evaluator.md +0 -49
  510. package/specialists/prompts/cx-explorer.md +0 -71
  511. package/specialists/prompts/cx-legal-compliance.md +0 -58
  512. package/specialists/prompts/cx-oracle.md +0 -98
  513. package/specialists/prompts/cx-platform-engineer.md +0 -97
  514. package/specialists/prompts/cx-rd-lead.md +0 -59
  515. package/specialists/prompts/cx-release-manager.md +0 -54
  516. package/specialists/prompts/cx-sre.md +0 -111
  517. package/specialists/prompts/cx-test-automation.md +0 -55
  518. package/specialists/prompts/cx-trace-reviewer.md +0 -101
  519. package/specialists/prompts/cx-ux-researcher.md +0 -53
@@ -10,7 +10,11 @@
10
10
  *
11
11
  * Lifecycle stages:
12
12
  * draft - in `.cx/scopes/draft-<id>/` with requirements.md + scope.json
13
- * active - in `specialists/org/scopes/<id>.json` (curated) or `.cx/scope.json` (custom)
13
+ * active - in `specialists/org/scopes/<id>.json` (curated), or a named
14
+ * custom profile under the construct-rf26.13 config-layer tiers
15
+ * (`~/.construct/org/scopes/<id>.json` or
16
+ * `<project>/.cx/org/scopes/<id>.json`), or the single-file
17
+ * `.cx/scope.json` escape hatch (custom, anonymous)
14
18
  * archived - in `archive/scopes/<id>/` with the final state and an
15
19
  * archive-note.md explaining why it was retired
16
20
  *
@@ -41,23 +45,22 @@ function archiveDir(cwd, id) {
41
45
 
42
46
  const REQUIREMENTS_BRIEF = (id, displayName) => `# Scope requirements: ${displayName} (${id})
43
47
 
44
- This brief is a discovery and design contract for a new Construct scope. Methodology lives in \`docs/guides/concepts/persona-research.md\` and lifecycle in \`docs/guides/concepts/scope-lifecycle.md\`. Treat each section as a task to dispatch to the named specialist. A draft scope is not ready to promote until every section has evidence backing it. The aim is research-grounded, not opinion-grounded.
48
+ This brief is a discovery and design contract for a new Construct scope. Methodology lives in \`docs/guides/concepts/persona-research.md\` and lifecycle in \`docs/guides/concepts/profile-lifecycle.md\`. Treat each section as a task to dispatch to the named specialist. A draft scope is not ready to promote until every section has evidence backing it. The aim is research-grounded, not opinion-grounded.
45
49
 
46
50
  Specialists used here are the existing Construct cx-* personas (already in the registry). They are dispatched the standard way; this doc just names which questions each one owns.
47
51
 
52
+ A scope selects flows and skill emphasis over the shared, fixed 12-role core roster (\`specialists/org/specialists/\`) — it does not invent new roles or departments. If this org genuinely needs a role absent from that roster, author it as a custom specialist via the construct-rf26.13 config layer (\`construct specialist create <id> --custom\`, docs/guides/cookbook/custom-specialists-and-teams.md) rather than declaring it inline here.
53
+
48
54
  This draft scaffolding produces:
49
- - \`scope.json\` — the structural definition (departments, roles, intake)
55
+ - \`scope.json\` — the structural definition (intake taxonomy, doc templates, skill emphasis)
50
56
  - \`requirements.md\` — this brief
51
- - \`personas/<role>.md\` — one persona artifact per role (filled during the discover phase)
52
- - \`departments/<dept>.md\` — one charter doc per department (filled during the frame phase)
53
-
54
- The persona and department files start as templates. Filling them from evidence is the actual work; the JSON is just a manifest.
57
+ - \`personas/<role>.md\` — optional, only when \`seedRoles\` names a role genuinely absent from the core roster (rare; most orgs route entirely through the existing 12)
55
58
 
56
- ## 1. Discovery (cx-ux-researcher)
59
+ ## 1. Discovery (cx-researcher)
57
60
 
58
61
  Goal: characterize the people, the work, and the outputs of this org type.
59
62
 
60
- - Who are the typical roles in this org? Name 4 to 8 with one-line responsibilities.
63
+ - Which of the 12 core roles (architect, reviewer, engineer, debugger, qa, security, operations, product-manager, data-analyst, designer, researcher, orchestrator) are primary for this org, and which are rarely if ever needed?
61
64
  - What is the dominant work loop? Describe in 5 to 8 stages.
62
65
  - What are the recurring signals that enter the loop (briefs, bug reports, customer messages, etc.)?
63
66
  - What are the canonical output artifacts (campaigns, runbooks, papers, etc.)?
@@ -67,23 +70,23 @@ Goal: characterize the people, the work, and the outputs of this org type.
67
70
 
68
71
  Goal: turn discovery into an intake taxonomy and a stage sequence.
69
72
 
70
- - Propose intake types (max 24). Each must be: distinct, observable, and routable to a primary owner.
73
+ - Propose intake types (max 24). Each must be: distinct, observable, and routable to a primary owner among the 12 core roles.
71
74
  - Propose stages (max 12). Order matters; each stage must answer "what changed?" not just "what happened next?".
72
- - For each intake type, name the primary owner role and the recommended chain (max 3 hops).
75
+ - For each intake type, name the primary owner role (one of the 12) and the recommended chain (max 3 hops).
73
76
  - For each stage, name the dominant artifact produced.
74
77
  - Acceptance: a representative real signal classifies into a single intake type with confidence > 0.6.
75
78
 
76
- ## 3. Architecture (cx-architect)
79
+ ## 3. Skill emphasis (cx-architect)
77
80
 
78
- Goal: define the role set and how it interlocks with the curated cx-* registry.
81
+ Goal: pick the flows and skill bundles this profile should emphasize from the shared registry — not a new role set.
79
82
 
80
- - List role ids the scope will reference (max 80, grouped into max 12 departments with max 20 roles per department). For each, indicate one of:
81
- reuse-existing (name the cx-* agent), create-new (specify scope), or compose-overlay (which base role + which flavor).
82
- - Identify cross-role handoffs. Where can ambiguity stall the loop? Name the orchestrator role.
83
- - Validate against the per-role cap (max 6 flavors per role per scope).
84
- - Acceptance: every declared role either exists in specialists/org or has a written scope statement.
83
+ - For each doc template the profile ships, confirm a docTemplates entry exists and name its owning role.
84
+ - List the \`defaultSkills\` (skill ids under \`skills/\`) this org's work loop leans on most — these become the profile's baseline skill entitlement (lib/skills/router.mjs), on top of whatever an individual specialist already carries.
85
+ - Set \`researchProfiles\` per intake type (external, user, codebase, market, compliance) and \`toneDefaults\` per doc template.
86
+ - Name the orchestrator's role in this loop (routing/dispatch is always cx-orchestrator; this just confirms nothing here bypasses it).
87
+ - Acceptance: every \`defaultSkills\` entry resolves to a real file under \`skills/\`; every doc template maps to an owning role among the 12.
85
88
 
86
- ## 4. Validation (cx-evaluator)
89
+ ## 4. Validation (cx-reviewer)
87
90
 
88
91
  Goal: prove the draft works on real signals before promotion.
89
92
 
@@ -93,13 +96,14 @@ Goal: prove the draft works on real signals before promotion.
93
96
 
94
97
  ## 5. Promotion (operator decision)
95
98
 
96
- Goal: move the draft into the active catalog.
99
+ Goal: move the draft into the active catalog, or leave it as a durable custom profile.
97
100
 
98
101
  - Curated path: hand-edit \`specialists/org/scopes/${id}.json\` from this draft, open a PR, run \`npm run lint:scopes\`.
99
- - Custom path: copy the draft scope.json to \`<project>/.cx/scope.json\` with \`"custom": true\`; \`construct scope set\` for switching between curated.
100
- - Either path requires the validation acceptance criteria above to be met.
102
+ - Custom path (named, reusable): copy the draft scope.json to \`<project>/.cx/org/scopes/${id}.json\` (project tier, git-tracked) or \`~/.construct/org/scopes/${id}.json\` (user tier, shared across projects) — the same builtin -> user -> project precedence construct-rf26.13 gives custom specialists and teams. Then \`construct scope set ${id}\` switches to it exactly like a curated scope.
103
+ - Custom path (anonymous, one-off): copy the draft scope.json to \`<project>/.cx/scope.json\` with \`"custom": true\` — the pre-rf26.13 escape hatch, still supported for a single ad hoc override.
104
+ - Any path requires the validation acceptance criteria above to be met.
101
105
 
102
- ## 6. Health monitoring (cx-evaluator + cx-trace-reviewer)
106
+ ## 6. Health monitoring (cx-reviewer)
103
107
 
104
108
  Goal: keep the scope honest after it ships.
105
109
 
@@ -107,7 +111,7 @@ Goal: keep the scope honest after it ships.
107
111
  - Any role with success-rate < 0.5 across 10+ runs triggers a review: is the role wrong, the prompt wrong, or the routing wrong?
108
112
  - Health data is the input for future scope revisions; never edit a scope without a health report first.
109
113
 
110
- ## 7. Archive (cx-docs-keeper + operator)
114
+ ## 7. Archive (cx-operations + operator)
111
115
 
112
116
  Goal: retire a scope cleanly without losing the learning.
113
117
 
@@ -397,7 +401,7 @@ export function scopeHealth(cwd, scopeId, { windowDays = 30 } = {}) {
397
401
 
398
402
  return {
399
403
  scope: scopeId,
400
- scopeExists: !!loadScope(scopeId),
404
+ scopeExists: !!loadScope(scopeId, { cwd }),
401
405
  windowDays,
402
406
  observationCount,
403
407
  roles: roleSummary,
@@ -1,12 +1,23 @@
1
1
  /**
2
2
  * lib/scopes/loader.mjs — Work-scope loader (intake lens + org enrichment).
3
3
  *
4
- * Curated scopes live in specialists/org/scopes/<id>.json. Custom overrides
5
- * use <cwd>/.cx/scope.json with custom: true. construct.config.json scope
6
- * field selects the active scope id (default rnd).
4
+ * Curated scopes live in specialists/org/scopes/<id>.json. Named custom
5
+ * scopes reuse the builtin -> user -> project precedence construct-rf26.13
6
+ * established for specialists/teams (lib/registry/assemble.mjs): an id also
7
+ * resolves from ~/.construct/org/scopes/<id>.json (shared across every
8
+ * project on the machine) and <cwd>/.cx/org/scopes/<id>.json (git-tracked,
9
+ * highest precedence), instead of a scope-specific mechanism. A later tier's
10
+ * fields shallow-merge over an earlier tier's on id collision.
7
11
  *
8
- * Teams and roles are not authored in scope files enrich.mjs attaches them
9
- * from specialists/org at load time.
12
+ * The single-file <cwd>/.cx/scope.json escape hatch (custom: true) still
13
+ * works for an anonymous, one-off override — it predates rf26.13 and stays
14
+ * for the "just this project, no id to remember" case — but a named, reusable
15
+ * custom profile should live under the tiered org/scopes/ layer so it can be
16
+ * switched to with `construct scope set <id>` like a curated one.
17
+ *
18
+ * construct.config.json's scope field selects the active curated/custom id
19
+ * (default rnd). Teams and roles are not authored in scope files — enrich.mjs
20
+ * attaches them from specialists/org at load time.
10
21
  */
11
22
 
12
23
  import { existsSync, readFileSync, readdirSync } from 'node:fs';
@@ -14,6 +25,7 @@ import { dirname, join, resolve } from 'node:path';
14
25
  import { fileURLToPath } from 'node:url';
15
26
 
16
27
  import { enrichScope } from './enrich.mjs';
28
+ import { homeDir } from '../paths.mjs';
17
29
 
18
30
  const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
19
31
  const REPO_ROOT = resolve(MODULE_DIR, '..', '..');
@@ -21,18 +33,43 @@ const SCOPES_DIR = join(REPO_ROOT, 'specialists', 'org', 'scopes');
21
33
 
22
34
  export const DEFAULT_SCOPE_ID = 'rnd';
23
35
 
24
- export function loadScope(id, opts = {}) {
25
- if (!id || typeof id !== 'string') return null;
26
- const path = join(SCOPES_DIR, `${id}.json`);
27
- if (!existsSync(path)) return null;
36
+ function userScopesDir() {
37
+ return join(homeDir(), '.construct', 'org', 'scopes');
38
+ }
39
+
40
+ function projectScopesDir(cwd) {
41
+ return join(cwd, '.cx', 'org', 'scopes');
42
+ }
43
+
44
+ function readScopeTier(dir, id) {
45
+ const p = join(dir, `${id}.json`);
46
+ if (!existsSync(p)) return null;
28
47
  try {
29
- const raw = JSON.parse(readFileSync(path, 'utf8'));
30
- return enrichScope(raw, opts);
48
+ return JSON.parse(readFileSync(p, 'utf8'));
31
49
  } catch {
32
50
  return null;
33
51
  }
34
52
  }
35
53
 
54
+ function resolveScopeRaw(id, cwd) {
55
+ const tiers = [SCOPES_DIR, userScopesDir(), projectScopesDir(cwd)];
56
+ let merged = null;
57
+ for (const dir of tiers) {
58
+ const raw = readScopeTier(dir, id);
59
+ if (!raw) continue;
60
+ merged = merged ? { ...merged, ...raw } : raw;
61
+ }
62
+ return merged;
63
+ }
64
+
65
+ export function loadScope(id, opts = {}) {
66
+ if (!id || typeof id !== 'string') return null;
67
+ const cwd = opts.cwd || process.cwd();
68
+ const raw = resolveScopeRaw(id, cwd);
69
+ if (!raw) return null;
70
+ return enrichScope(raw, opts);
71
+ }
72
+
36
73
  export function listScopes() {
37
74
  if (!existsSync(SCOPES_DIR)) return [];
38
75
  return readdirSync(SCOPES_DIR)
@@ -64,7 +101,7 @@ export function loadCustomScope(cwd) {
64
101
 
65
102
  export function resolveActiveScope(cwd, configScopeId = null) {
66
103
  const rootDir = REPO_ROOT;
67
- const enrichOpts = { rootDir };
104
+ const enrichOpts = { rootDir, cwd };
68
105
  if (configScopeId) {
69
106
  const s = loadScope(configScopeId, enrichOpts);
70
107
  if (s) return s;
@@ -10,7 +10,7 @@ const OPERATIONS_OBJECTIVE_RULES = [
10
10
  {
11
11
  teamId: 'operations-team',
12
12
  focus: 'reliability',
13
- attachTo: ['cx-sre', 'cx-docs-keeper'],
13
+ attachTo: ['cx-operations'],
14
14
  pattern: /\b(incident|outage|deploy|deployment|rollback|reliability|runbook|postmortem|sre|on-?call)\b/i,
15
15
  },
16
16
  {
@@ -0,0 +1,80 @@
1
+ /**
2
+ * lib/security/ingest-boundary.mjs — Source-kind → trust-level mapping at the
3
+ * ingest boundary.
4
+ *
5
+ * Maps named source kinds (provider type, pipeline stage, ingestion method)
6
+ * to canonical trust levels and stamps them onto records using the primitives
7
+ * in lib/security/trust.mjs.
8
+ *
9
+ * The module is additive — existing ingest, embed, or storage code is not modified.
10
+ * Callers that want labeling import `stampIngestBoundary` and
11
+ * apply it at the point where external content enters Construct's data layer.
12
+ *
13
+ * References: CX-AUDIT-LLMSEC-001, construct-9oi4.14.1
14
+ */
15
+
16
+ import { TRUST_LEVELS, stampTrust } from './trust.mjs';
17
+
18
+ // ---------------------------------------------------------------------------
19
+ // Source-kind registry
20
+ // ---------------------------------------------------------------------------
21
+
22
+ /**
23
+ * Mapping from well-known source kind strings to trust levels.
24
+ * All entries are lowercase; callers should normalise before lookup.
25
+ */
26
+ const SOURCE_KIND_MAP = {
27
+ // Built-in / internal
28
+ 'builtin-prompt': TRUST_LEVELS.TRUSTED_INTERNAL,
29
+ 'validated-pack': TRUST_LEVELS.TRUSTED_INTERNAL,
30
+
31
+ // Team-authored local content
32
+ 'team-authored': TRUST_LEVELS.TEAM_AUTHORED,
33
+ 'local-committed': TRUST_LEVELS.TEAM_AUTHORED,
34
+
35
+ // Authenticated external providers
36
+ 'github-issue': TRUST_LEVELS.EXTERNAL_AUTHENTICATED,
37
+ 'github-pr': TRUST_LEVELS.EXTERNAL_AUTHENTICATED,
38
+ 'github-comment': TRUST_LEVELS.EXTERNAL_AUTHENTICATED,
39
+ 'jira-ticket': TRUST_LEVELS.EXTERNAL_AUTHENTICATED,
40
+ 'jira-comment': TRUST_LEVELS.EXTERNAL_AUTHENTICATED,
41
+ 'confluence-page': TRUST_LEVELS.EXTERNAL_AUTHENTICATED,
42
+ 'linear-issue': TRUST_LEVELS.EXTERNAL_AUTHENTICATED,
43
+
44
+ // Unauthenticated / parsed / scraped content
45
+ 'docling-parsed': TRUST_LEVELS.EXTERNAL_UNAUTHENTICATED,
46
+ 'web-fetched': TRUST_LEVELS.EXTERNAL_UNAUTHENTICATED,
47
+ 'web-search-result': TRUST_LEVELS.EXTERNAL_UNAUTHENTICATED,
48
+ 'pdf-extracted': TRUST_LEVELS.EXTERNAL_UNAUTHENTICATED,
49
+ 'unknown': TRUST_LEVELS.EXTERNAL_UNAUTHENTICATED,
50
+ };
51
+
52
+ /**
53
+ * Resolve the trust level for a given source kind string.
54
+ * Unrecognised kinds default to EXTERNAL_UNAUTHENTICATED (fail-safe).
55
+ *
56
+ * @param {string} sourceKind
57
+ * @returns {string} TRUST_LEVELS value
58
+ */
59
+ export function resolveTrustLevel(sourceKind) {
60
+ const key = (sourceKind ?? 'unknown').toLowerCase();
61
+ return SOURCE_KIND_MAP[key] ?? TRUST_LEVELS.EXTERNAL_UNAUTHENTICATED;
62
+ }
63
+
64
+ /**
65
+ * Stamp a trust label onto a record at the ingest boundary.
66
+ *
67
+ * Maps `sourceKind` to the appropriate trust level and delegates to
68
+ * `stampTrust`. The resulting record has a `_trust` field that downstream
69
+ * consumers (recall wrappers, context assemblers) can inspect.
70
+ *
71
+ * @param {Record<string, unknown>} record The record being ingested.
72
+ * @param {string} sourceKind Well-known source kind string.
73
+ * @param {{ sourceRef?: string }} [options] Optional extra metadata.
74
+ * @returns {Record<string, unknown>} Stamped record (source record not mutated).
75
+ */
76
+ export function stampIngestBoundary(record, sourceKind, options = {}) {
77
+ const level = resolveTrustLevel(sourceKind);
78
+ const source = options.sourceRef ?? sourceKind ?? 'unknown';
79
+ return stampTrust(record, level, source);
80
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * lib/security/recall-wrapper.mjs — Trust-aware context assembly for recalled
3
+ * records.
4
+ *
5
+ * Before a set of recalled observations or documents is assembled into model
6
+ * context, callers pass them through `wrapForContextAssembly`. Records
7
+ * stamped as external (authenticated or unauthenticated) are wrapped with
8
+ * explicit untrusted delimiters. Internal and team-authored records pass
9
+ * through unchanged. Unstamped records are warned and treated as the most
10
+ * restrictive level: EXTERNAL_UNAUTHENTICATED.
11
+ *
12
+ * The module is additive — existing memory, recall, or context assembly code is not modified.
13
+ * Integration is left to follow-on beads (N2, N4).
14
+ *
15
+ * References: CX-AUDIT-LLMSEC-001, construct-9oi4.14.1
16
+ */
17
+
18
+ import { TRUST_LEVELS, recallTrustGrade, stampTrust, wrapUntrusted } from './trust.mjs';
19
+
20
+ // Trust levels that require the untrusted-delimiter treatment.
21
+ const UNTRUSTED_LEVELS = new Set([
22
+ TRUST_LEVELS.EXTERNAL_UNAUTHENTICATED,
23
+ TRUST_LEVELS.EXTERNAL_AUTHENTICATED,
24
+ ]);
25
+
26
+ /**
27
+ * Wrap a single recalled record for safe context assembly.
28
+ *
29
+ * - TRUSTED_INTERNAL / TEAM_AUTHORED → returned as-is.
30
+ * - EXTERNAL_AUTHENTICATED / EXTERNAL_UNAUTHENTICATED → content wrapped with
31
+ * `[UNTRUSTED:…]` delimiters in both the `content` field (if present) and
32
+ * as a `_wrappedContent` convenience field.
33
+ * - No `_trust` stamp → warned, treated as EXTERNAL_UNAUTHENTICATED.
34
+ *
35
+ * @param {Record<string, unknown>} record A recalled observation/document.
36
+ * @param {{ warn?: (msg: string) => void }} [opts]
37
+ * @returns {Record<string, unknown>} Wrapped or passthrough record.
38
+ */
39
+ export function wrapRecordForContext(record, opts = {}) {
40
+ const warn = opts.warn ?? ((msg) => console.warn('[recall-wrapper]', msg));
41
+ let trustMeta = recallTrustGrade(record);
42
+
43
+ if (trustMeta === null) {
44
+ warn(
45
+ `Recalled record has no _trust stamp — treating as ${TRUST_LEVELS.EXTERNAL_UNAUTHENTICATED}. ` +
46
+ `Record keys: ${Object.keys(record ?? {}).join(', ') || '(empty)'}`,
47
+ );
48
+ // Re-stamp defensively so downstream consumers always see _trust.
49
+ const stamped = stampTrust(
50
+ record,
51
+ TRUST_LEVELS.EXTERNAL_UNAUTHENTICATED,
52
+ 'unstamped-recall',
53
+ );
54
+ trustMeta = stamped._trust;
55
+ record = stamped;
56
+ }
57
+
58
+ if (!UNTRUSTED_LEVELS.has(trustMeta.level)) {
59
+ // TRUSTED_INTERNAL or TEAM_AUTHORED — pass through unchanged.
60
+ return record;
61
+ }
62
+
63
+ // External content: wrap any present content field and attach _wrappedContent.
64
+ const rawContent = record.content ?? record.text ?? record.body ?? null;
65
+ const wrapped = rawContent != null
66
+ ? wrapUntrusted(String(rawContent), trustMeta)
67
+ : null;
68
+
69
+ return {
70
+ ...record,
71
+ ...(wrapped != null ? { _wrappedContent: wrapped } : {}),
72
+ };
73
+ }
74
+
75
+ /**
76
+ * Wrap an array of recalled records for safe context assembly.
77
+ *
78
+ * Iterates each record through `wrapRecordForContext`. The returned array
79
+ * preserves original order. Non-object entries are returned as-is with a
80
+ * warning (defensive).
81
+ *
82
+ * @param {unknown[]} records Array of recalled observations or documents.
83
+ * @param {{ warn?: (msg: string) => void }} [opts]
84
+ * @returns {unknown[]} Array of wrapped/passthrough records.
85
+ */
86
+ export function wrapForContextAssembly(records, opts = {}) {
87
+ if (!Array.isArray(records)) {
88
+ throw new TypeError('wrapForContextAssembly: records must be an array');
89
+ }
90
+ const warn = opts.warn ?? ((msg) => console.warn('[recall-wrapper]', msg));
91
+
92
+ return records.map((record, idx) => {
93
+ if (record === null || typeof record !== 'object') {
94
+ warn(`Record at index ${idx} is not an object — skipping wrap (type: ${typeof record})`);
95
+ return record;
96
+ }
97
+ return wrapRecordForContext(record, { warn });
98
+ });
99
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * lib/security/trust.mjs — Trust taxonomy and stamping primitives.
3
+ *
4
+ * Provides a machine-checkable trust hierarchy for all content that flows
5
+ * through Construct: ingested documents, recalled observations, provider
6
+ * responses, and built-in prompts. Callers stamp records at ingestion time
7
+ * and downstream consumers check the stamp before assembling model context.
8
+ *
9
+ * Integration into callers (ingest/embed/storage paths) is handled by
10
+ * follow-on beads (N2, N4). This module is additive — it exports utilities
11
+ * only; no existing code is modified.
12
+ *
13
+ * References: CX-AUDIT-LLMSEC-001, OWASP LLM01 [S12][S13], construct-9oi4.14.1
14
+ */
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Trust level taxonomy (ordered lowest → highest trust)
18
+ // ---------------------------------------------------------------------------
19
+
20
+ /** @type {Record<string, string>} */
21
+ export const TRUST_LEVELS = {
22
+ /** Scraped or parsed content with no authorship verification (web pages,
23
+ * docling-extracted text, unknown origin). Highest risk of injection. */
24
+ EXTERNAL_UNAUTHENTICATED: 'external-unauthenticated',
25
+
26
+ /** Content from an authenticated external actor: a known GitHub user's
27
+ * issue-tracker comment, a named Jira reporter, a Confluence page with
28
+ * attributed authorship. Lower risk but still untrusted. */
29
+ EXTERNAL_AUTHENTICATED: 'external-authenticated',
30
+
31
+ /** Authored by a member of the Construct team or project (e.g. a local
32
+ * markdown file committed by the team). Trusted but not built-in. */
33
+ TEAM_AUTHORED: 'team-authored',
34
+
35
+ /** Built-in prompts, validated skill packs, and internal system content.
36
+ * Full trust — assembled directly into the instruction channel. */
37
+ TRUSTED_INTERNAL: 'trusted-internal',
38
+ };
39
+
40
+ // Ordered list for comparison (index = rank, higher index = higher trust).
41
+ const LEVEL_ORDER = [
42
+ TRUST_LEVELS.EXTERNAL_UNAUTHENTICATED,
43
+ TRUST_LEVELS.EXTERNAL_AUTHENTICATED,
44
+ TRUST_LEVELS.TEAM_AUTHORED,
45
+ TRUST_LEVELS.TRUSTED_INTERNAL,
46
+ ];
47
+
48
+ // ---------------------------------------------------------------------------
49
+ // Stamping
50
+ // ---------------------------------------------------------------------------
51
+
52
+ /**
53
+ * Stamp a trust label onto an observation or record object.
54
+ *
55
+ * @param {Record<string, unknown>} record Source object (not mutated).
56
+ * @param {string} level One of TRUST_LEVELS values.
57
+ * @param {string} source Human-readable source descriptor.
58
+ * @returns {Record<string, unknown>} New object with `_trust` metadata added.
59
+ */
60
+ export function stampTrust(record, level, source) {
61
+ if (!Object.values(TRUST_LEVELS).includes(level)) {
62
+ throw new TypeError(
63
+ `stampTrust: unknown trust level "${level}". ` +
64
+ `Valid values: ${Object.values(TRUST_LEVELS).join(', ')}`,
65
+ );
66
+ }
67
+ return {
68
+ ...record,
69
+ _trust: {
70
+ level,
71
+ source: String(source ?? 'unknown'),
72
+ stampedAt: Date.now(),
73
+ },
74
+ };
75
+ }
76
+
77
+ // ---------------------------------------------------------------------------
78
+ // Level comparison
79
+ // ---------------------------------------------------------------------------
80
+
81
+ /**
82
+ * Return true if `level` meets or exceeds `required` in the trust hierarchy.
83
+ *
84
+ * @param {string} level The trust level of the content being checked.
85
+ * @param {string} required The minimum acceptable trust level.
86
+ * @returns {boolean}
87
+ */
88
+ export function meetsMinTrustLevel(level, required) {
89
+ const levelIdx = LEVEL_ORDER.indexOf(level);
90
+ const requiredIdx = LEVEL_ORDER.indexOf(required);
91
+ if (levelIdx === -1 || requiredIdx === -1) return false;
92
+ return levelIdx >= requiredIdx;
93
+ }
94
+
95
+ // ---------------------------------------------------------------------------
96
+ // Context assembly helpers
97
+ // ---------------------------------------------------------------------------
98
+
99
+ /**
100
+ * Wrap external content with explicit untrusted delimiters so the model
101
+ * context assembler can visually and programmatically distinguish data from
102
+ * instructions.
103
+ *
104
+ * @param {string} content Raw text content.
105
+ * @param {{ level: string, source: string }} trustMeta Trust metadata.
106
+ * @returns {string} Delimited content string.
107
+ */
108
+ export function wrapUntrusted(content, trustMeta) {
109
+ const level = trustMeta?.level ?? TRUST_LEVELS.EXTERNAL_UNAUTHENTICATED;
110
+ const source = trustMeta?.source ?? 'unknown';
111
+ return `[UNTRUSTED:${level}:${source}]\n${content}\n[/UNTRUSTED]`;
112
+ }
113
+
114
+ // ---------------------------------------------------------------------------
115
+ // Recall re-grading
116
+ // ---------------------------------------------------------------------------
117
+
118
+ /**
119
+ * Re-grade a recalled record — returns the `_trust` meta object if present,
120
+ * or null if the record was stored without a trust stamp.
121
+ *
122
+ * Callers should treat a null return as EXTERNAL_UNAUTHENTICATED per policy.
123
+ *
124
+ * @param {Record<string, unknown>} record A recalled observation or document.
125
+ * @returns {{ level: string, source: string, stampedAt: number } | null}
126
+ */
127
+ export function recallTrustGrade(record) {
128
+ if (record && typeof record._trust === 'object' && record._trust !== null) {
129
+ return record._trust;
130
+ }
131
+ return null;
132
+ }