@mnstry/atelier 0.2.0-alpha.12 → 0.2.0-alpha.13

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 (516) hide show
  1. package/CHANGELOG.md +661 -0
  2. package/README.md +142 -6
  3. package/contracts/atelier-adoption-policy.v2.schema.json +55 -0
  4. package/contracts/atelier-architecture.v1.schema.json +258 -0
  5. package/contracts/atelier-boundary-delegations.v1.schema.json +161 -0
  6. package/contracts/atelier-boundary-owner-keys.v1.schema.json +98 -0
  7. package/contracts/atelier-build.v1.schema.json +1076 -0
  8. package/contracts/atelier-capability.v1.schema.json +1252 -0
  9. package/contracts/atelier-coordination.v1.schema.json +1050 -0
  10. package/contracts/atelier-evidence-navigation.v1.schema.json +603 -0
  11. package/contracts/atelier-harness.v1.schema.json +234 -0
  12. package/contracts/atelier-ingestion.v1.schema.json +806 -0
  13. package/contracts/atelier-inquiry.v1.schema.json +1772 -0
  14. package/contracts/atelier-instruction-adoption.v1.schema.json +490 -0
  15. package/contracts/atelier-interaction.v1.schema.json +553 -0
  16. package/contracts/atelier-judgment.v1.schema.json +690 -0
  17. package/contracts/atelier-knowledge.v1.schema.json +1512 -0
  18. package/contracts/atelier-learning.v1.schema.json +1095 -0
  19. package/contracts/atelier-migration.v3.schema.json +64 -0
  20. package/contracts/atelier-personal-workspace-manifest.v1.schema.json +87 -0
  21. package/contracts/atelier-personal-workspace-overlay.v1.schema.json +299 -0
  22. package/contracts/atelier-template-adoption.v1.schema.json +223 -0
  23. package/contracts/atelier-template-profile.v1.schema.json +1168 -0
  24. package/contracts/atelier-trackable.v1.schema.json +781 -0
  25. package/contracts/atelier-upgrade-plan.v3.schema.json +532 -0
  26. package/docs/architecture.md +89 -0
  27. package/docs/assurance-controls.md +2 -2
  28. package/docs/blocks/capability-stewardship.md +17 -0
  29. package/docs/blocks/claims.md +16 -3
  30. package/docs/blocks/inquiry-harnesses.md +19 -0
  31. package/docs/blocks/learning-harnesses.md +17 -0
  32. package/docs/capability-stewardship.md +269 -0
  33. package/docs/coordination.md +51 -0
  34. package/docs/decision-practices.md +111 -0
  35. package/docs/decisions.md +29 -0
  36. package/docs/evidence-navigation.md +237 -0
  37. package/docs/exact-upgrades.md +3 -2
  38. package/docs/ingestion.md +181 -0
  39. package/docs/inquiry-harnesses.md +211 -0
  40. package/docs/install.md +80 -3
  41. package/docs/intake-and-guides.md +5 -0
  42. package/docs/integration-contract-decisions.md +24 -2
  43. package/docs/interaction-and-reflection.md +72 -0
  44. package/docs/knowledge-graph.md +20 -0
  45. package/docs/knowledge-health/api.md +30 -0
  46. package/docs/knowledge-health/assessment/api.md +96 -0
  47. package/docs/knowledge-health/assessment/capabilities.md +40 -0
  48. package/docs/knowledge-health/assessment/evaluation.md +27 -0
  49. package/docs/knowledge-health/assessment/facilitator.md +30 -0
  50. package/docs/knowledge-health/assessment/quickstart.md +61 -0
  51. package/docs/knowledge-health/assessment/troubleshooting.md +23 -0
  52. package/docs/knowledge-health/devday/facilitator.md +16 -0
  53. package/docs/knowledge-health/devday/participant-response.md +22 -0
  54. package/docs/knowledge-health/devday/public-default.md +107 -0
  55. package/docs/knowledge-health/devday/quickstart.md +18 -0
  56. package/docs/knowledge-health/devday/support.md +22 -0
  57. package/docs/knowledge-health/exercise.md +17 -0
  58. package/docs/knowledge-health/facilitator.md +20 -0
  59. package/docs/knowledge-health/quickstart.md +94 -0
  60. package/docs/knowledge-health/readme.md +15 -0
  61. package/docs/knowledge-health/support.md +34 -0
  62. package/docs/knowledge-setup.md +248 -0
  63. package/docs/knowledge-workspace.md +264 -0
  64. package/docs/learning-harnesses.md +238 -0
  65. package/docs/learning.md +215 -0
  66. package/docs/local-services.md +181 -16
  67. package/docs/obsidian-contract.md +223 -15
  68. package/docs/obsidian-plugin.md +38 -4
  69. package/docs/obsidian.md +613 -51
  70. package/docs/optional-ingestion.md +83 -0
  71. package/docs/personal-workspace.md +257 -0
  72. package/docs/practical-judgment.md +92 -0
  73. package/docs/progressive-ingestion-acceptance.md +89 -0
  74. package/docs/progressive-ingestion.md +235 -0
  75. package/docs/project-command-inventory.md +1 -0
  76. package/docs/release-engineering.md +49 -0
  77. package/docs/repo-boundary-guard.md +94 -0
  78. package/docs/research-starter-quickstart.md +66 -0
  79. package/docs/research-starter.md +114 -0
  80. package/docs/semantic-ingestion.md +198 -0
  81. package/docs/semantic-operation.md +317 -0
  82. package/docs/skill-steward.md +6 -0
  83. package/docs/template-profile.md +164 -0
  84. package/docs/template-upgrades.md +114 -0
  85. package/docs/trackables.md +111 -0
  86. package/docs/upgrade.md +47 -2
  87. package/fixtures/atelier-architecture/binding/invalid/document.json +19 -0
  88. package/fixtures/atelier-architecture/binding/valid/document.json +19 -0
  89. package/fixtures/atelier-architecture/catalog/invalid/document.json +290 -0
  90. package/fixtures/atelier-architecture/catalog/valid/document.json +343 -0
  91. package/fixtures/atelier-build-contract/attempt/invalid/document.json +20 -0
  92. package/fixtures/atelier-build-contract/attempt/valid/document.json +19 -0
  93. package/fixtures/atelier-build-contract/candidate/invalid/document.json +24 -0
  94. package/fixtures/atelier-build-contract/candidate/valid/document.json +23 -0
  95. package/fixtures/atelier-build-contract/decision/invalid/document.json +31 -0
  96. package/fixtures/atelier-build-contract/decision/valid/document.json +30 -0
  97. package/fixtures/atelier-build-contract/delivery/invalid/document.json +18 -0
  98. package/fixtures/atelier-build-contract/delivery/valid/document.json +17 -0
  99. package/fixtures/atelier-build-contract/gate/invalid/document.json +22 -0
  100. package/fixtures/atelier-build-contract/gate/valid/document.json +21 -0
  101. package/fixtures/atelier-build-contract/ledger/invalid/document.json +270 -0
  102. package/fixtures/atelier-build-contract/ledger/valid/document.json +269 -0
  103. package/fixtures/atelier-build-contract/objective/invalid/document.json +73 -0
  104. package/fixtures/atelier-build-contract/objective/valid/document.json +72 -0
  105. package/fixtures/atelier-build-contract/progress/invalid/document.json +18 -0
  106. package/fixtures/atelier-build-contract/progress/valid/document.json +17 -0
  107. package/fixtures/atelier-capability-contract/adoption/invalid/document.json +71 -0
  108. package/fixtures/atelier-capability-contract/adoption/valid/document.json +70 -0
  109. package/fixtures/atelier-capability-contract/event/invalid/document.json +18 -0
  110. package/fixtures/atelier-capability-contract/event/valid/document.json +17 -0
  111. package/fixtures/atelier-capability-contract/journal/invalid/document.json +382 -0
  112. package/fixtures/atelier-capability-contract/journal/valid/document.json +381 -0
  113. package/fixtures/atelier-capability-contract/notice/invalid/document.json +8 -0
  114. package/fixtures/atelier-capability-contract/notice/valid/document.json +7 -0
  115. package/fixtures/atelier-capability-contract/package/invalid/document.json +59 -0
  116. package/fixtures/atelier-capability-contract/package/valid/document.json +58 -0
  117. package/fixtures/atelier-capability-contract/plan/invalid/document.json +107 -0
  118. package/fixtures/atelier-capability-contract/plan/valid/document.json +106 -0
  119. package/fixtures/atelier-capability-contract/release/invalid/document.json +85 -0
  120. package/fixtures/atelier-capability-contract/release/valid/document.json +84 -0
  121. package/fixtures/atelier-capability-contract/state/invalid/document.json +316 -0
  122. package/fixtures/atelier-capability-contract/state/valid/document.json +315 -0
  123. package/fixtures/atelier-coordination/dependency/invalid/document.json +37 -0
  124. package/fixtures/atelier-coordination/dependency/valid/document.json +36 -0
  125. package/fixtures/atelier-coordination/directive/invalid/document.json +25 -0
  126. package/fixtures/atelier-coordination/directive/valid/document.json +24 -0
  127. package/fixtures/atelier-coordination/disposition/invalid/document.json +34 -0
  128. package/fixtures/atelier-coordination/disposition/valid/document.json +33 -0
  129. package/fixtures/atelier-coordination/outcome/invalid/document.json +39 -0
  130. package/fixtures/atelier-coordination/outcome/valid/document.json +38 -0
  131. package/fixtures/atelier-coordination/recovery/invalid/document.json +34 -0
  132. package/fixtures/atelier-coordination/recovery/valid/document.json +33 -0
  133. package/fixtures/atelier-harness-contract/handoff/invalid/document.json +25 -0
  134. package/fixtures/atelier-harness-contract/handoff/valid/document.json +24 -0
  135. package/fixtures/atelier-ingestion/invalid/authority.json +19 -0
  136. package/fixtures/atelier-ingestion/semantic-baseline.json +26 -0
  137. package/fixtures/atelier-ingestion/semantic-operation.json +75 -0
  138. package/fixtures/atelier-ingestion/valid/plan-input.json +18 -0
  139. package/fixtures/atelier-inquiry-contract/assessment/invalid/document.json +41 -0
  140. package/fixtures/atelier-inquiry-contract/assessment/valid/document.json +40 -0
  141. package/fixtures/atelier-inquiry-contract/bundle/invalid/document.json +51 -0
  142. package/fixtures/atelier-inquiry-contract/bundle/valid/document.json +50 -0
  143. package/fixtures/atelier-inquiry-contract/campaign/invalid/document.json +17 -0
  144. package/fixtures/atelier-inquiry-contract/campaign/valid/document.json +16 -0
  145. package/fixtures/atelier-inquiry-contract/decision/invalid/document.json +22 -0
  146. package/fixtures/atelier-inquiry-contract/decision/valid/document.json +21 -0
  147. package/fixtures/atelier-inquiry-contract/feedback/invalid/document.json +19 -0
  148. package/fixtures/atelier-inquiry-contract/feedback/valid/document.json +18 -0
  149. package/fixtures/atelier-inquiry-contract/handoff/invalid/document.json +92 -0
  150. package/fixtures/atelier-inquiry-contract/handoff/valid/document.json +91 -0
  151. package/fixtures/atelier-inquiry-contract/hypothesis/invalid/document.json +20 -0
  152. package/fixtures/atelier-inquiry-contract/hypothesis/valid/document.json +19 -0
  153. package/fixtures/atelier-inquiry-contract/ledger/invalid/document.json +680 -0
  154. package/fixtures/atelier-inquiry-contract/ledger/valid/document.json +679 -0
  155. package/fixtures/atelier-inquiry-contract/legacy-assessment/invalid/document.json +19 -0
  156. package/fixtures/atelier-inquiry-contract/legacy-assessment/valid/document.json +18 -0
  157. package/fixtures/atelier-inquiry-contract/request/invalid/document.json +50 -0
  158. package/fixtures/atelier-inquiry-contract/request/valid/document.json +49 -0
  159. package/fixtures/atelier-inquiry-contract/source/invalid/document.json +18 -0
  160. package/fixtures/atelier-inquiry-contract/source/valid/document.json +17 -0
  161. package/fixtures/atelier-inquiry-contract/withdrawal/invalid/document.json +16 -0
  162. package/fixtures/atelier-inquiry-contract/withdrawal/valid/document.json +15 -0
  163. package/fixtures/atelier-instruction-adoption/context-receipt/invalid/document.json +18 -0
  164. package/fixtures/atelier-instruction-adoption/context-receipt/valid/document.json +17 -0
  165. package/fixtures/atelier-instruction-adoption/plan/invalid/document.json +63 -0
  166. package/fixtures/atelier-instruction-adoption/plan/valid/document.json +62 -0
  167. package/fixtures/atelier-instruction-adoption/receipt/invalid/document.json +22 -0
  168. package/fixtures/atelier-instruction-adoption/receipt/valid/document.json +21 -0
  169. package/fixtures/atelier-instruction-adoption/state/invalid/document.json +30 -0
  170. package/fixtures/atelier-instruction-adoption/state/valid/document.json +29 -0
  171. package/fixtures/atelier-interaction/act/invalid/document.json +25 -0
  172. package/fixtures/atelier-interaction/act/valid/document.json +24 -0
  173. package/fixtures/atelier-interaction/assessment/invalid/document.json +32 -0
  174. package/fixtures/atelier-interaction/assessment/valid/document.json +31 -0
  175. package/fixtures/atelier-interaction/observation/invalid/document.json +22 -0
  176. package/fixtures/atelier-interaction/observation/valid/document.json +21 -0
  177. package/fixtures/atelier-interaction/policy/invalid/document.json +20 -0
  178. package/fixtures/atelier-interaction/policy/valid/document.json +19 -0
  179. package/fixtures/atelier-judgment/case/invalid/document.json +76 -0
  180. package/fixtures/atelier-judgment/case/valid/document.json +75 -0
  181. package/fixtures/atelier-judgment/decision/invalid/document.json +38 -0
  182. package/fixtures/atelier-judgment/decision/valid/document.json +37 -0
  183. package/fixtures/atelier-judgment/native-decision.md +3 -0
  184. package/fixtures/atelier-judgment/practice-reconsideration/definition.json +92 -0
  185. package/fixtures/atelier-judgment/practice-reconsideration/scenario.json +435 -0
  186. package/fixtures/atelier-knowledge-contract/activation/invalid/document.json +26 -0
  187. package/fixtures/atelier-knowledge-contract/activation/valid/document.json +25 -0
  188. package/fixtures/atelier-knowledge-contract/contribution/invalid/document.json +34 -0
  189. package/fixtures/atelier-knowledge-contract/contribution/valid/document.json +33 -0
  190. package/fixtures/atelier-knowledge-contract/domain/invalid/document.json +41 -0
  191. package/fixtures/atelier-knowledge-contract/domain/valid/document.json +40 -0
  192. package/fixtures/atelier-knowledge-contract/domain-revision/invalid/document.json +46 -0
  193. package/fixtures/atelier-knowledge-contract/domain-revision/valid/document.json +45 -0
  194. package/fixtures/atelier-knowledge-contract/evaluation/invalid/document.json +21 -0
  195. package/fixtures/atelier-knowledge-contract/evaluation/valid/document.json +20 -0
  196. package/fixtures/atelier-knowledge-contract/ledger/invalid/document.json +318 -0
  197. package/fixtures/atelier-knowledge-contract/ledger/valid/document.json +317 -0
  198. package/fixtures/atelier-knowledge-contract/relation/invalid/document.json +25 -0
  199. package/fixtures/atelier-knowledge-contract/relation/valid/document.json +24 -0
  200. package/fixtures/atelier-knowledge-contract/review/invalid/document.json +18 -0
  201. package/fixtures/atelier-knowledge-contract/review/valid/document.json +17 -0
  202. package/fixtures/atelier-knowledge-contract/withdrawal/invalid/document.json +16 -0
  203. package/fixtures/atelier-knowledge-contract/withdrawal/valid/document.json +15 -0
  204. package/fixtures/atelier-learning/invalid/authority.json +14 -0
  205. package/fixtures/atelier-learning/valid/capture.json +13 -0
  206. package/fixtures/atelier-personal-workspace-contract/manifest/invalid/authority-key.json +17 -0
  207. package/fixtures/atelier-personal-workspace-contract/manifest/invalid/nonempty-ext.json +25 -0
  208. package/fixtures/atelier-personal-workspace-contract/manifest/invalid/wrong-schema.json +22 -0
  209. package/fixtures/atelier-personal-workspace-contract/manifest/valid/manifest.json +22 -0
  210. package/fixtures/atelier-personal-workspace-contract/overlay/invalid/authority-key.json +25 -0
  211. package/fixtures/atelier-personal-workspace-contract/overlay/invalid/unknown-top-level-field.json +27 -0
  212. package/fixtures/atelier-personal-workspace-contract/overlay/invalid/wrong-schema.json +24 -0
  213. package/fixtures/atelier-trackable/definition/invalid/document.json +15 -0
  214. package/fixtures/atelier-trackable/definition/valid/document.json +14 -0
  215. package/fixtures/atelier-trackable/request/invalid/document.json +23 -0
  216. package/fixtures/atelier-trackable/request/valid/document.json +22 -0
  217. package/fixtures/boundary-delegations/invalid/authority-key.v1.json +30 -0
  218. package/fixtures/boundary-delegations/invalid/push-operation.v1.json +28 -0
  219. package/fixtures/boundary-delegations/invalid/unsigned.v1.json +25 -0
  220. package/fixtures/boundary-delegations/valid/empty.v1.json +4 -0
  221. package/fixtures/boundary-delegations/valid/signed-operator.v1.json +30 -0
  222. package/fixtures/boundary-owner-keys/invalid/wrong-algorithm.v1.json +15 -0
  223. package/fixtures/boundary-owner-keys/invalid/wrong-curve.v1.json +15 -0
  224. package/fixtures/boundary-owner-keys/valid/one-owner.v1.json +17 -0
  225. package/fixtures/capability-packages/adoption.example.json +70 -0
  226. package/fixtures/capability-packages/evidence-review/capability-package.json +58 -0
  227. package/fixtures/capability-packages/evidence-review/capability-release.json +84 -0
  228. package/fixtures/capability-packages/evidence-review/evaluation.md +15 -0
  229. package/fixtures/capability-packages/evidence-review/skills/evidence-review/SKILL.md +22 -0
  230. package/fixtures/capability-packages/evidence-review/skills/evidence-review/references/ledger.md +7 -0
  231. package/fixtures/capability-packages/research/capability-package.json +88 -0
  232. package/fixtures/capability-packages/research/capability-release.json +134 -0
  233. package/fixtures/capability-packages/research/evaluation.md +15 -0
  234. package/fixtures/capability-packages/research/skills/research-integrate/SKILL.md +29 -0
  235. package/fixtures/capability-packages/research/skills/research-integrate/references/integration.md +10 -0
  236. package/fixtures/capability-packages/research/skills/research-plan/SKILL.md +27 -0
  237. package/fixtures/capability-packages/research/skills/research-plan/references/inquiries.md +26 -0
  238. package/fixtures/capability-packages/research/skills/research-synthesize/SKILL.md +23 -0
  239. package/fixtures/capability-packages/research/skills/research-synthesize/references/synthesis.md +12 -0
  240. package/fixtures/evidence-navigation/invalid/duplicate-operation.json +16 -0
  241. package/fixtures/evidence-navigation/invalid/instruction-kind.json +36 -0
  242. package/fixtures/evidence-navigation/invalid/invalid-digest.json +36 -0
  243. package/fixtures/evidence-navigation/invalid/invalid-time.json +36 -0
  244. package/fixtures/evidence-navigation/invalid/missing-attribution.json +35 -0
  245. package/fixtures/evidence-navigation/invalid/reserved-extension.json +19 -0
  246. package/fixtures/evidence-navigation/invalid/unattributed-acceptance.json +36 -0
  247. package/fixtures/evidence-navigation/invalid/uncoerced-version.json +16 -0
  248. package/fixtures/evidence-navigation/invalid/unknown-currency.json +17 -0
  249. package/fixtures/evidence-navigation/invalid/unknown-selector-field.json +37 -0
  250. package/fixtures/evidence-navigation/invalid/unsupported-operation.json +15 -0
  251. package/fixtures/evidence-navigation/valid/claim.json +36 -0
  252. package/fixtures/evidence-navigation/valid/host.json +16 -0
  253. package/fixtures/evidence-navigation/valid/profile.json +16 -0
  254. package/fixtures/evidence-navigation/valid/snapshot.json +17 -0
  255. package/fixtures/harness-packages/build-harness/capability-package.json +61 -0
  256. package/fixtures/harness-packages/build-harness/capability-release.json +82 -0
  257. package/fixtures/harness-packages/build-harness/evaluation.md +3 -0
  258. package/fixtures/harness-packages/build-harness/skills/atelier-build-harness/SKILL.md +59 -0
  259. package/fixtures/harness-packages/knowledge-harness/capability-package.json +61 -0
  260. package/fixtures/harness-packages/knowledge-harness/capability-release.json +82 -0
  261. package/fixtures/harness-packages/knowledge-harness/evaluation.md +3 -0
  262. package/fixtures/harness-packages/knowledge-harness/skills/atelier-knowledge-harness/SKILL.md +60 -0
  263. package/fixtures/harnesses/learning-cycle.json +780 -0
  264. package/fixtures/inquiry/workshop.json +675 -0
  265. package/fixtures/inquiry-packages/discovery-harness/capability-package.json +61 -0
  266. package/fixtures/inquiry-packages/discovery-harness/capability-release.json +82 -0
  267. package/fixtures/inquiry-packages/discovery-harness/evaluation.md +3 -0
  268. package/fixtures/inquiry-packages/discovery-harness/skills/atelier-discovery-harness/SKILL.md +48 -0
  269. package/fixtures/inquiry-packages/research-harness/capability-package.json +61 -0
  270. package/fixtures/inquiry-packages/research-harness/capability-release.json +82 -0
  271. package/fixtures/inquiry-packages/research-harness/evaluation.md +3 -0
  272. package/fixtures/inquiry-packages/research-harness/skills/atelier-research-harness/SKILL.md +43 -0
  273. package/fixtures/knowledge-health/calibration-suite.json +26 -0
  274. package/fixtures/knowledge-health/calibration-trial.json +37 -0
  275. package/fixtures/knowledge-health/corrected/checklist.md +18 -0
  276. package/fixtures/knowledge-health/devday/participant-record.json +19 -0
  277. package/fixtures/knowledge-health/expected-summary.json +27 -0
  278. package/fixtures/knowledge-health/workshop/README.md +20 -0
  279. package/fixtures/knowledge-health/workshop/atelier.project.json +7 -0
  280. package/fixtures/knowledge-health/workshop/knowledge-plan.json +65 -0
  281. package/fixtures/knowledge-health/workshop/records/checklist.md +18 -0
  282. package/fixtures/knowledge-health/workshop/records/workshop.md +18 -0
  283. package/fixtures/knowledge-health/workshop/repo-access.v1.json +5 -0
  284. package/fixtures/knowledge-health/workspace/atelier.project.json +18 -0
  285. package/fixtures/knowledge-health/workspace/knowledge-plan.json +74 -0
  286. package/fixtures/knowledge-health/workspace/records/checklist.md +18 -0
  287. package/fixtures/knowledge-health/workspace/records/pilot.md +16 -0
  288. package/fixtures/knowledge-health/workspace/repo-access.v1.json +9 -0
  289. package/fixtures/personal-workspace/overlay.json +24 -0
  290. package/fixtures/templates/local-library.v1.json +75 -0
  291. package/fixtures/templates/upgrade-contracts/adoption-policy.v2/invalid.json +16 -0
  292. package/fixtures/templates/upgrade-contracts/adoption-policy.v2/valid.json +13 -0
  293. package/fixtures/templates/upgrade-contracts/migration.v3/invalid.json +24 -0
  294. package/fixtures/templates/upgrade-contracts/migration.v3/valid.json +21 -0
  295. package/fixtures/templates/upgrade-contracts/template-adoption.v1/invalid.json +60 -0
  296. package/fixtures/templates/upgrade-contracts/template-adoption.v1/valid.json +57 -0
  297. package/fixtures/templates/upgrade-contracts/upgrade-plan.v3/invalid.json +85 -0
  298. package/fixtures/templates/upgrade-contracts/upgrade-plan.v3/valid.json +82 -0
  299. package/package.json +59 -5
  300. package/skills/claude/atelier-build-harness/SKILL.md +59 -0
  301. package/skills/claude/atelier-coordination/SKILL.md +32 -0
  302. package/skills/claude/atelier-discovery-harness/SKILL.md +48 -0
  303. package/skills/claude/atelier-guided-upgrade/SKILL.md +29 -0
  304. package/skills/claude/atelier-interaction/SKILL.md +34 -0
  305. package/skills/claude/atelier-knowledge-harness/SKILL.md +60 -0
  306. package/skills/claude/atelier-knowledge-setup/SKILL.md +72 -0
  307. package/skills/claude/atelier-practical-judgment/SKILL.md +64 -0
  308. package/skills/claude/atelier-research-harness/SKILL.md +43 -0
  309. package/skills/claude/atelier-skill-steward/SKILL.md +53 -0
  310. package/skills/claude/atelier-trackables/SKILL.md +40 -0
  311. package/skills/codex/atelier-build-harness/SKILL.md +59 -0
  312. package/skills/codex/atelier-coordination/SKILL.md +32 -0
  313. package/skills/codex/atelier-discovery-harness/SKILL.md +48 -0
  314. package/skills/codex/atelier-guided-upgrade/SKILL.md +29 -0
  315. package/skills/codex/atelier-interaction/SKILL.md +34 -0
  316. package/skills/codex/atelier-knowledge-harness/SKILL.md +60 -0
  317. package/skills/codex/atelier-knowledge-setup/SKILL.md +72 -0
  318. package/skills/codex/atelier-practical-judgment/SKILL.md +64 -0
  319. package/skills/codex/atelier-research-harness/SKILL.md +43 -0
  320. package/skills/codex/atelier-skill-steward/SKILL.md +53 -0
  321. package/skills/codex/atelier-trackables/SKILL.md +40 -0
  322. package/src/architecture/catalog.mjs +355 -0
  323. package/src/architecture/index.mjs +93 -0
  324. package/src/boundary/content-rules.mjs +4 -2
  325. package/src/boundary/delegation-command.mjs +158 -0
  326. package/src/boundary/delegation.mjs +396 -0
  327. package/src/boundary/policy.mjs +52 -10
  328. package/src/build/git.mjs +18 -0
  329. package/src/build/index.mjs +3 -0
  330. package/src/build/ledger.mjs +88 -0
  331. package/src/capabilities/adoption.mjs +381 -0
  332. package/src/capabilities/evidence.mjs +140 -0
  333. package/src/capabilities/files.mjs +124 -0
  334. package/src/capabilities/index.mjs +4 -0
  335. package/src/capabilities/instructions.mjs +250 -0
  336. package/src/capabilities/package.mjs +136 -0
  337. package/src/cli/command-failure.mjs +36 -0
  338. package/src/cli/execute-command.mjs +16 -2
  339. package/src/cli/run.mjs +65 -9
  340. package/src/coauthor/store.mjs +43 -2
  341. package/src/collaboration/proposals.mjs +189 -0
  342. package/src/commands/architecture.mjs +27 -0
  343. package/src/commands/boundary.mjs +3 -0
  344. package/src/commands/capability.mjs +73 -0
  345. package/src/commands/enroll.mjs +3 -0
  346. package/src/commands/harness.mjs +79 -0
  347. package/src/commands/ingest.mjs +39 -0
  348. package/src/commands/init.mjs +4 -1
  349. package/src/commands/inquiry.mjs +66 -0
  350. package/src/commands/knowledge-semantic.mjs +128 -0
  351. package/src/commands/knowledge.mjs +119 -0
  352. package/src/commands/learn.mjs +61 -0
  353. package/src/commands/obsidian.mjs +754 -63
  354. package/src/commands/practice.mjs +24 -0
  355. package/src/commands/setup.mjs +22 -2
  356. package/src/commands/trackable.mjs +19 -0
  357. package/src/contracts/corpus.mjs +30 -0
  358. package/src/coordination/index.mjs +110 -0
  359. package/src/evidence-navigation/contracts.mjs +60 -0
  360. package/src/evidence-navigation/evaluate.mjs +69 -0
  361. package/src/evidence-navigation/generation.mjs +22 -0
  362. package/src/evidence-navigation/index.mjs +2 -0
  363. package/src/evidence-navigation/local.mjs +167 -0
  364. package/src/evidence-navigation/schema.generated.mjs +604 -0
  365. package/src/evidence-navigation/types.mjs +12 -0
  366. package/src/export/atelier-export-contract.mjs +35 -8
  367. package/src/graph/enroll.mjs +248 -0
  368. package/src/graph/graph.mjs +17 -1
  369. package/src/graph/knowledge-graph.mjs +58 -8
  370. package/src/harnesses/contracts.mjs +41 -0
  371. package/src/harnesses/exchange.mjs +154 -0
  372. package/src/harnesses/feedback.mjs +16 -0
  373. package/src/harnesses/history.mjs +37 -0
  374. package/src/harnesses/index.mjs +4 -0
  375. package/src/harnesses/store.mjs +29 -0
  376. package/src/ingestion/contracts.mjs +41 -0
  377. package/src/ingestion/evaluation.mjs +119 -0
  378. package/src/ingestion/optional/composition/index.mjs +171 -0
  379. package/src/ingestion/optional/evaluation/index.mjs +219 -0
  380. package/src/ingestion/optional/graphify/composition-binding.mjs +60 -0
  381. package/src/ingestion/optional/graphify/composition_receiving.py +185 -0
  382. package/src/ingestion/optional/graphify/graphify_supplier.py +933 -0
  383. package/src/ingestion/optional/graphify/json_bridge.py +163 -0
  384. package/src/ingestion/optional/graphify/query_receiving.py +209 -0
  385. package/src/ingestion/optional/index.mjs +9 -0
  386. package/src/ingestion/optional/jev/bridge.mjs +166 -0
  387. package/src/ingestion/optional/jev/composition-binding.mjs +116 -0
  388. package/src/ingestion/optional/jev/judgments.mjs +174 -0
  389. package/src/ingestion/optional/jev/native-translation.mjs +32 -0
  390. package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/__init__.py +7 -0
  391. package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/adapter.py +966 -0
  392. package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/artifacts.py +101 -0
  393. package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/projection.py +146 -0
  394. package/src/ingestion/optional/lightrag/composition-binding.mjs +67 -0
  395. package/src/ingestion/optional/vanilla/composition-binding.mjs +102 -0
  396. package/src/ingestion/optional/vanilla/index.mjs +218 -0
  397. package/src/ingestion/optional/vanilla/native-normalization.mjs +187 -0
  398. package/src/ingestion/processors.mjs +218 -0
  399. package/src/ingestion/semantic.mjs +203 -0
  400. package/src/ingestion/store.mjs +372 -0
  401. package/src/inquiry/beliefs.mjs +32 -0
  402. package/src/inquiry/contracts.mjs +23 -0
  403. package/src/inquiry/index.mjs +5 -0
  404. package/src/inquiry/ledger.mjs +135 -0
  405. package/src/inquiry/lenses.mjs +15 -0
  406. package/src/inquiry/projection.mjs +77 -0
  407. package/src/inquiry/store.mjs +35 -0
  408. package/src/intake/read-scope.mjs +12 -0
  409. package/src/intake/store.mjs +69 -6
  410. package/src/interaction/index.mjs +91 -0
  411. package/src/judgment/index.mjs +82 -0
  412. package/src/judgment/practice-evaluation.mjs +70 -0
  413. package/src/judgment/practice.mjs +144 -0
  414. package/src/knowledge/context.mjs +217 -0
  415. package/src/knowledge/index.mjs +5 -0
  416. package/src/knowledge/ingestion.mjs +79 -0
  417. package/src/knowledge/intake.mjs +33 -0
  418. package/src/knowledge/ledger.mjs +110 -0
  419. package/src/knowledge/participatory/guidance.mjs +36 -0
  420. package/src/knowledge/participatory/guided-contribution.mjs +44 -0
  421. package/src/knowledge/participatory/index.mjs +2 -0
  422. package/src/knowledge/participatory/installed-workshop.mjs +108 -0
  423. package/src/knowledge/plan.mjs +67 -0
  424. package/src/knowledge/projection.mjs +43 -0
  425. package/src/knowledge/semantic-operation.mjs +567 -0
  426. package/src/knowledge/sessions.mjs +467 -0
  427. package/src/knowledge/workspace.mjs +295 -0
  428. package/src/knowledge-health/assessment.mjs +165 -0
  429. package/src/knowledge-health/capabilities.mjs +350 -0
  430. package/src/knowledge-health/context.mjs +62 -0
  431. package/src/knowledge-health/example.mjs +47 -0
  432. package/src/knowledge-health/index.mjs +10 -0
  433. package/src/knowledge-health/owner-join.mjs +88 -0
  434. package/src/knowledge-health/proof/common.mjs +24 -0
  435. package/src/knowledge-health/proof/installed-check.mjs +100 -0
  436. package/src/knowledge-health/proof/native-readback.mjs +37 -0
  437. package/src/knowledge-health/proof/rehearse.mjs +68 -0
  438. package/src/knowledge-health/proof/validate-participant.mjs +17 -0
  439. package/src/knowledge-health/proof/verify-native-capture.mjs +14 -0
  440. package/src/knowledge-health/proof/workshop-check.mjs +36 -0
  441. package/src/knowledge-health/rule-identity.mjs +44 -0
  442. package/src/knowledge-health/workshop-composition.mjs +19 -0
  443. package/src/knowledge-health/workshop-profile.mjs +176 -0
  444. package/src/knowledge-health/workshop.mjs +99 -0
  445. package/src/learning/adapters.mjs +68 -0
  446. package/src/learning/assistance.mjs +127 -0
  447. package/src/learning/contracts.mjs +65 -0
  448. package/src/learning/store.mjs +272 -0
  449. package/src/personal-workspace/index.mjs +391 -0
  450. package/src/personal-workspace/selection.mjs +385 -0
  451. package/src/project/config.mjs +8 -0
  452. package/src/project/durable-state.mjs +3 -1
  453. package/src/project/file-class.mjs +1 -1
  454. package/src/projection/obsidian/edits/apply.mjs +55 -16
  455. package/src/projection/obsidian/personal-workspace.mjs +436 -0
  456. package/src/projection/obsidian/proposals/adapter.mjs +47 -8
  457. package/src/projection/obsidian/proposals/observation.mjs +10 -2
  458. package/src/projection/obsidian/publication/publisher.mjs +142 -13
  459. package/src/projection/obsidian/publication/vault-list.mjs +4 -3
  460. package/src/projection/obsidian/recovery/index.mjs +4 -1
  461. package/src/projection/obsidian/recovery/store.mjs +160 -7
  462. package/src/projection/obsidian/selection-ui/contribution.mjs +6 -5
  463. package/src/readiness/readiness.mjs +10 -3
  464. package/src/reflection/index.mjs +56 -0
  465. package/src/runtime/obsidian/app-production-seams.mjs +48 -16
  466. package/src/runtime/obsidian/app-registration.mjs +175 -28
  467. package/src/runtime/obsidian/app-restart.mjs +154 -0
  468. package/src/runtime/obsidian/engine.mjs +163 -20
  469. package/src/runtime/obsidian/extension-points.mjs +1 -1
  470. package/src/runtime/obsidian/index.mjs +10 -7
  471. package/src/runtime/obsidian/launch-plan.mjs +7 -4
  472. package/src/runtime/obsidian/lifecycle.mjs +92 -12
  473. package/src/runtime/obsidian/login-item.mjs +332 -0
  474. package/src/runtime/obsidian/machine-settings.mjs +119 -12
  475. package/src/runtime/obsidian/opening.mjs +171 -37
  476. package/src/runtime/obsidian/pipeline.mjs +71 -3
  477. package/src/runtime/obsidian/plugin-channel.mjs +11 -3
  478. package/src/runtime/obsidian/plugin-choice.mjs +16 -4
  479. package/src/runtime/obsidian/plugin-drift.mjs +59 -11
  480. package/src/runtime/obsidian/plugin-presence.mjs +14 -1
  481. package/src/runtime/obsidian/process-identity.mjs +20 -10
  482. package/src/runtime/obsidian/project-views.mjs +282 -0
  483. package/src/runtime/obsidian/questions.mjs +48 -0
  484. package/src/runtime/obsidian/release-watch.mjs +56 -0
  485. package/src/runtime/obsidian/service-main.mjs +99 -16
  486. package/src/runtime/obsidian/service-manager-production.mjs +61 -0
  487. package/src/runtime/obsidian/service-managers.mjs +275 -0
  488. package/src/runtime/obsidian/service-record.mjs +87 -23
  489. package/src/runtime/obsidian/service.mjs +36 -3
  490. package/src/runtime/obsidian/startup-adapters.mjs +59 -21
  491. package/src/runtime/obsidian/unheld-evidence.mjs +84 -0
  492. package/src/runtime/obsidian/vault-location.mjs +300 -0
  493. package/src/runtime/obsidian/view-counts.mjs +34 -0
  494. package/src/server/local-sidecar.mjs +169 -2
  495. package/src/server/server.mjs +28 -2
  496. package/src/template-bindings/project.mjs +92 -0
  497. package/src/templates/profile.mjs +225 -0
  498. package/src/trackables/domain.mjs +175 -0
  499. package/src/trackables/store.mjs +114 -0
  500. package/src/ui/knowledge-page.mjs +1388 -0
  501. package/src/upgrade/explanation.mjs +1 -1
  502. package/src/upgrade/template-participant.mjs +171 -0
  503. package/src/upgrade/transaction.mjs +72 -25
  504. package/src/upgrade/upgrade.mjs +1 -0
  505. package/templates/external-project-workspace/pack-lifecycle.json +6 -1
  506. package/templates/knowledge-workspace/.gitattributes +2 -0
  507. package/templates/knowledge-workspace/AGENTS.md +27 -0
  508. package/templates/knowledge-workspace/README.md +44 -0
  509. package/templates/knowledge-workspace/atelier.project.json +22 -0
  510. package/templates/knowledge-workspace/gitignore +6 -0
  511. package/templates/knowledge-workspace/knowledge-plan.json +111 -0
  512. package/templates/knowledge-workspace/knowledge-plan.schema.json +281 -0
  513. package/templates/knowledge-workspace/records/forecast.md +22 -0
  514. package/templates/knowledge-workspace/records/inspection.md +25 -0
  515. package/templates/knowledge-workspace/records/telescope.md +24 -0
  516. package/templates/knowledge-workspace/repo-access.v1.json +9 -0
@@ -25,6 +25,7 @@ import {
25
25
  trustedMutationRequest,
26
26
  trustedReadRequest,
27
27
  } from './security.mjs'
28
+ import { canonicalize } from '../attestation/jcs.mjs'
28
29
  import { createProposalStore } from '../collaboration/proposals.mjs'
29
30
  import {
30
31
  renderProposalDetailPageHtml,
@@ -34,6 +35,9 @@ import {
34
35
  import { createReviewStore } from '../collaboration/review-store.mjs'
35
36
  import { loadBoundRun, currentRunEligibility } from '../readiness-protocols/evidence.mjs'
36
37
  import { renderReviewPage } from '../ui/review-page.mjs'
38
+ import { renderKnowledgePage } from '../ui/knowledge-page.mjs'
39
+ import { knowledgeDashboard, knowledgeQuestionContext, loadKnowledgeWorkspace } from '../knowledge/workspace.mjs'
40
+ import { createKnowledgeSessions } from '../knowledge/sessions.mjs'
37
41
 
38
42
  export const ATELIER_CONTEXT_SCHEMA = 'atelier-context@v1'
39
43
  export const ATELIER_PRESENCE_SCHEMA = 'atelier-presence@v1'
@@ -216,6 +220,28 @@ function listenFailure(error, port) {
216
220
  return error
217
221
  }
218
222
 
223
+ function knowledgeError(error) {
224
+ const message = error instanceof Error ? error.message : ''
225
+ if (message === 'coauthor requires a Git workspace with untracked, ignored .atelier-local/ state')
226
+ return 'coauthor requires a Git workspace with untracked, ignored local state. Initialize Git and ignore the .atelier-local directory.'
227
+ if (/^collaboration (ledger|event).*ceiling/.test(message))
228
+ return 'knowledge history capacity reached. Preserve the ledger and saved values for operator inspection; do not compact active session chains.'
229
+ if (message.startsWith('EEXIST: private state is locked'))
230
+ return 'knowledge writer is busy. Preserve the request and retry after the active writer finishes.'
231
+ const safe = [
232
+ 'workspace changed', 'stale revision', 'event id conflict', 'source changed',
233
+ 'coauthor session not found', 'coauthor requires', 'unknown knowledge',
234
+ 'invalid knowledge', 'request UUID', 'retry budget exhausted',
235
+ 'receipt and failure events are store-owned', 'event refused in',
236
+ 'coauthor plan must', 'knowledge session configuration mismatch',
237
+ 'knowledge session identity mismatch', 'knowledge session exceeds',
238
+ 'knowledge session list exceeds',
239
+ 'knowledge history unavailable', 'knowledge start marker mismatch',
240
+ ]
241
+ return safe.some(prefix => message.startsWith(prefix)) && !message.includes('/')
242
+ ? message : 'Knowledge operation unavailable. Preserve your draft and inspect the local workspace or active writer.'
243
+ }
244
+
219
245
  function doctorEnvelope() {
220
246
  return {
221
247
  ok: true,
@@ -231,6 +257,7 @@ export function createAtelierSidecarServer({
231
257
  workspaceRoot = process.cwd(),
232
258
  stateDir = workspaceRoot,
233
259
  reviewProject = null,
260
+ knowledgeProject = null,
234
261
  port = 0,
235
262
  presenceTtlMs = Number(process.env.ATELIER_PRESENCE_TTL_MS || 5 * 60 * 1000),
236
263
  } = {}) {
@@ -243,6 +270,7 @@ export function createAtelierSidecarServer({
243
270
  })
244
271
  const publication = loadPublishedWorkspaceManifest(root)
245
272
  const review = reviewProject ? createReviewStore(reviewProject) : null
273
+ const knowledge = knowledgeProject ? createKnowledgeSessions(knowledgeProject) : null
246
274
  const workspaceId = workspaceIdForRoot(root)
247
275
  const noncePath = path.join(stateDirReal, '.atelier-nonce')
248
276
  const presencePath = path.join(stateDirReal, '.atelier-presence.json')
@@ -253,6 +281,95 @@ export function createAtelierSidecarServer({
253
281
  workspaceId,
254
282
  })
255
283
 
284
+ // One copy-only declaration is retained from the workshop: this path, this
285
+ // action, from this view. The request stays `{ sessionId }`; everything
286
+ // retained is derived here from fresh owning readbacks, and the generic
287
+ // /api/proposals route keeps its published-HTML fence.
288
+ const WORKSHOP_HANDOFF_TARGET = Object.freeze({ path: 'records/checklist.md', action: 'copy.repoPath', viewId: 'knowledge-workshop' })
289
+ const sameJson = (left, right) => canonicalize(left) === canonicalize(right)
290
+ const refusedRetention = (error, status = 'refused') => ({ status: 409, body: { ok: false, error, retention: { status } } })
291
+ const retentionSummary = (record, outcome, sourceCurrent) => ({
292
+ status: 'retained',
293
+ outcome,
294
+ sourceCurrent,
295
+ proposalId: record.proposal.id,
296
+ readback: `/api/proposals/${record.proposal.id}`,
297
+ receipt: {
298
+ sessionId: record.retention.sessionId,
299
+ requestId: record.retention.requestId,
300
+ valueDigest: record.retention.valueDigest,
301
+ },
302
+ payloadDigest: record.retention.payloadDigest,
303
+ proposalStatus: record.proposal.status,
304
+ eventVersion: record.proposal.eventVersion,
305
+ })
306
+
307
+ function retainWorkshopHandoff(body) {
308
+ if (!body || Array.isArray(body) || Object.keys(body).join(',') !== 'sessionId' || typeof body.sessionId !== 'string') {
309
+ return refusedRetention('workshop handoff accepts a session id only')
310
+ }
311
+ // A fresh owning readback: this throws when the session, its ledger or a
312
+ // saved value is missing, redirected or altered.
313
+ const session = knowledge.read(body.sessionId)
314
+ const selected = session.record.flow === 'workshop-review'
315
+ ? session.state.saved.find((value) => value.fieldId === 'authored-revision')
316
+ : null
317
+ if (!selected) {
318
+ // No separately selected draft receipt: prepared only, as before. An
319
+ // incomplete Review or a response that selects nothing is refused by
320
+ // the existing preparation.
321
+ return { status: 200, body: { ...knowledge.workshopHandoff(body), retention: { status: 'not-retained', reason: 'no separately selected draft receipt' } } }
322
+ }
323
+ const own = session.owningReadbacks?.verified === true
324
+ ? session.owningReadbacks.immutableValues.find((value) => sameJson(value.receipt, selected.receipt))
325
+ : null
326
+ if (!own || own.text !== selected.text || selected.receipt.sessionId !== session.record.id) {
327
+ return refusedRetention('the selected draft receipt is not confirmed by its owning readback')
328
+ }
329
+ if (!session.current) {
330
+ // Reopen after a source change: read back what was retained, never append.
331
+ const known = proposals.lookupRetainedProposal(selected.receipt)
332
+ if (known.outcome === 'retained') {
333
+ return {
334
+ status: 200,
335
+ body: {
336
+ ok: true,
337
+ handoff: proposals.retainedHandoff(known.record),
338
+ sourceEditsApplied: false,
339
+ directWrite: false,
340
+ retention: retentionSummary(known.record, 'existing', false),
341
+ },
342
+ }
343
+ }
344
+ return known.outcome === 'not-found'
345
+ ? refusedRetention('the source changed before this draft was retained; nothing is retained for this receipt', 'not-retained')
346
+ : refusedRetention(known.error, known.outcome === 'conflict' ? 'refused' : 'unknown')
347
+ }
348
+ const prepared = knowledge.workshopHandoff(body)
349
+ const handoff = prepared.handoff
350
+ if (
351
+ !sameJson(handoff.proposal.selectedDraftReceipt, selected.receipt) || handoff.sessionId !== session.record.id ||
352
+ handoff.path !== WORKSHOP_HANDOFF_TARGET.path || handoff.action !== WORKSHOP_HANDOFF_TARGET.action ||
353
+ handoff.viewId !== WORKSHOP_HANDOFF_TARGET.viewId || handoff.directWrite !== false ||
354
+ handoff.applyEndpoint !== null || handoff.diff?.replacement !== selected.text
355
+ ) {
356
+ return refusedRetention('the prepared handoff does not match the selected draft readback')
357
+ }
358
+ // The original contribution is read back from its own session as well.
359
+ const original = knowledge.read(handoff.proposal.contributionSessionId)
360
+ const values = original.owningReadbacks?.verified === true ? original.owningReadbacks.immutableValues : null
361
+ if (
362
+ !values ||
363
+ !sameJson([...values.map((value) => value.receipt), selected.receipt], handoff.proposal.savedDraftReceipts) ||
364
+ values.find((value) => value.receipt.fieldId === 'original-words')?.text !== handoff.proposal.originalWords
365
+ ) {
366
+ return refusedRetention('the original contribution is not confirmed by its owning readback')
367
+ }
368
+ const result = proposals.retainProposal(handoff)
369
+ if (!result.ok) return refusedRetention(result.error, result.outcome === 'unknown' ? 'unknown' : 'refused')
370
+ return { status: 200, body: { ...prepared, retention: retentionSummary(result.record, result.outcome, true) } }
371
+ }
372
+
256
373
  function resolvePublishedPath({ rel, requireFile = false, requireHtml = false } = {}) {
257
374
  const resolved = resolveWorkspacePath({
258
375
  workspaceRoot: root,
@@ -395,8 +512,8 @@ export function createAtelierSidecarServer({
395
512
  // Browsers send Origin on POST; same-origin GET normally omits it. Keep
396
513
  // the strict origin gate for nonce disclosure instead of relaxing the
397
514
  // existing session-auth GET contract or accepting unclassified reads.
398
- if (review && url.pathname === '/api/review/session') {
399
- if (!trustedMutationRequest(req.headers) || !body || Array.isArray(body) || Object.keys(body).length) {
515
+ if ((review && url.pathname === '/api/review/session') || (knowledge && url.pathname === '/api/knowledge/session')) {
516
+ if (!trustedMutationRequest(req.headers) || !body || Array.isArray(body) || Object.keys(body).length || (url.pathname === '/api/knowledge/session' && url.search)) {
400
517
  json(res, 403, { ok: false, error: 'review session requires a same-origin empty request' })
401
518
  return
402
519
  }
@@ -411,6 +528,22 @@ export function createAtelierSidecarServer({
411
528
  return
412
529
  }
413
530
 
531
+ if (knowledge && url.pathname.startsWith('/api/knowledge/')) {
532
+ const operation = url.pathname.slice('/api/knowledge/'.length)
533
+ if (!['start', 'event', 'recover', 'lookup', 'workshop-handoff', 'workshop-review'].includes(operation) || url.search) {
534
+ json(res, 404, { ok: false, error: 'unknown knowledge action' }); return
535
+ }
536
+ try {
537
+ if (operation === 'workshop-handoff') {
538
+ const retained = retainWorkshopHandoff(body)
539
+ json(res, retained.status, retained.body)
540
+ } else {
541
+ json(res, 200, knowledge[operation === 'workshop-review' ? 'workshopReview' : operation](body))
542
+ }
543
+ } catch (error) { json(res, 409, { ok: false, error: knowledgeError(error) }) }
544
+ return
545
+ }
546
+
414
547
  if (review && url.pathname === '/api/review/contributions') {
415
548
  const result = review.contribute(body)
416
549
  json(res, result.status, result)
@@ -528,6 +661,38 @@ export function createAtelierSidecarServer({
528
661
  return
529
662
  }
530
663
 
664
+ if (knowledge && url.pathname === '/knowledge' && !url.search) {
665
+ const body = renderKnowledgePage()
666
+ res.writeHead(200, htmlDocumentHeaders(Buffer.byteLength(body)))
667
+ res.end(body)
668
+ return
669
+ }
670
+ if (knowledge && url.pathname.startsWith('/api/knowledge/')) {
671
+ try {
672
+ let result
673
+ const operation = url.pathname.slice('/api/knowledge/'.length)
674
+ const parameters = operation === 'context' ? ['id', 'mode'] : operation === 'read' ? ['id'] : []
675
+ if ([...url.searchParams.keys()].some(k => !parameters.includes(k)) || [...url.searchParams.keys()].length !== new Set(url.searchParams.keys()).size) throw new Error('invalid knowledge query')
676
+ if (operation === 'dashboard') {
677
+ const dashboard = knowledgeDashboard(loadKnowledgeWorkspace(knowledgeProject))
678
+ try {
679
+ const profile = knowledge.workshopProfile()
680
+ dashboard.flows = [profile.flow, ...dashboard.flows]
681
+ dashboard.workshop = { available: true, context: profile.context }
682
+ } catch {
683
+ dashboard.workshop = { available: false, reason: 'No current actionable public workshop finding.' }
684
+ }
685
+ result = { dashboard }
686
+ }
687
+ else if (operation === 'context') result = { context: knowledgeQuestionContext(loadKnowledgeWorkspace(knowledgeProject), url.searchParams.get('id'), url.searchParams.get('mode') || 'graph') }
688
+ else if (operation === 'sessions') result = knowledge.list()
689
+ else if (operation === 'read') result = knowledge.read(url.searchParams.get('id'))
690
+ else { json(res, 404, { ok: false, error: 'unknown knowledge action' }); return }
691
+ json(res, 200, { ok: true, ...result })
692
+ } catch (error) { json(res, 409, { ok: false, error: knowledgeError(error) }) }
693
+ return
694
+ }
695
+
531
696
  if (review && url.pathname === '/review') {
532
697
  const body = renderReviewPage()
533
698
  res.writeHead(200, htmlDocumentHeaders(Buffer.byteLength(body)))
@@ -626,6 +791,8 @@ export function createAtelierSidecarServer({
626
791
  proposal: record.proposal,
627
792
  diff: record.diff,
628
793
  copyable: record.copyable,
794
+ // A retained workshop handoff is read back whole; other proposals are unchanged.
795
+ ...(record.retention ? { payload: record.payload, retention: record.retention } : {}),
629
796
  } : { ok: false, error: result.error, diagnostics: result.diagnostics })
630
797
  return
631
798
  }
@@ -1,6 +1,27 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
1
3
  import { AtelierDiagnosticError, resolveProjectConfig } from '../project/config.mjs'
2
4
  import { createAtelierSidecarServer } from './local-sidecar.mjs'
3
5
 
6
+ // dev serves what build wrote and builds nothing itself. Before build, the
7
+ // output folder (or, after graph alone, its manifest) is absent, and the
8
+ // sidecar failed with a raw ENOENT carrying an absolute path or a redacted
9
+ // internal error. Name the missing steps instead, and name the folder only
10
+ // relative to the project config. A non-directory is left to the sidecar.
11
+ function assertProjectionBuilt(project) {
12
+ const stat = fs.statSync(project.outputRoot, { throwIfNoEntry: false })
13
+ if (stat && !stat.isDirectory()) return
14
+ const manifestMissing = stat && !fs.existsSync(path.join(project.outputRoot, 'atelier.manifest.json'))
15
+ if (stat && !manifestMissing) return
16
+ const rel = path.relative(project.configDir, project.outputRoot)
17
+ const contained = rel !== '' && rel !== '..' && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel)
18
+ const folder = contained ? `projection output folder ${rel}` : 'the projection output folder'
19
+ const state = manifestMissing ? 'has no atelier.manifest.json' : 'does not exist'
20
+ throw new AtelierDiagnosticError('projection-output-missing', `${folder} ${state}; this project has not been built yet`, {
21
+ hint: 'Run atelier graph, then atelier build, with the same --project path, then retry.',
22
+ })
23
+ }
24
+
4
25
  function parseServerArgs(argv = []) {
5
26
  const args = { projectArgs: [] }
6
27
  for (const arg of argv) {
@@ -30,11 +51,16 @@ function resolvePort(args, env = process.env) {
30
51
  export async function runServerCommand(argv = process.argv.slice(2)) {
31
52
  const args = parseServerArgs(argv)
32
53
  const project = resolveProjectConfig({ argv: args.projectArgs })
54
+ // An unusable --port or PORT is refused before the project's build state,
55
+ // so a mistyped argument is named even in a project not built yet.
56
+ const port = args.smoke ? 0 : resolvePort(args)
57
+ assertProjectionBuilt(project)
33
58
  const sidecar = createAtelierSidecarServer({
34
59
  workspaceRoot: project.outputRoot,
35
60
  stateDir: project.outputRoot,
36
61
  reviewProject: argv.includes('--review') ? project : null,
37
- port: args.smoke ? 0 : resolvePort(args),
62
+ knowledgeProject: argv.includes('--knowledge') ? project : null,
63
+ port,
38
64
  })
39
65
 
40
66
  if (args.smoke) {
@@ -53,5 +79,5 @@ export async function runServerCommand(argv = process.argv.slice(2)) {
53
79
  }
54
80
 
55
81
  const address = await sidecar.listen()
56
- console.log(`MNSTRY Atelier listening on http://127.0.0.1:${address.port}/`)
82
+ console.log(`MNSTRY Atelier listening on http://127.0.0.1:${address.port}/${argv.includes('--knowledge') ? 'knowledge' : ''}`)
57
83
  }
@@ -0,0 +1,92 @@
1
+ import fs from 'node:fs'
2
+ import { buildCanonicalGraph, createGraphFileCache } from '../graph/graph.mjs'
3
+ import { projectGraph, PROJECTION_TARGETS } from '../projection/policy.mjs'
4
+ import { renderReadOnlyDocument, validatePresentation } from '../ui/presentation/index.mjs'
5
+ import { templateReference, validateTemplateDefinition, validateTemplateBinding, validateTemplateHost } from '../templates/profile.mjs'
6
+
7
+ const packageVersion = JSON.parse(fs.readFileSync(new URL('../../package.json', import.meta.url), 'utf8')).version
8
+ const identity = value => templateReference('PayloadRef', 'identity', '1', value).digest.slice(7, 39)
9
+ const outcome = (ok, errors, rest = {}) => ({ ok, errors, authority: 'structural-only', executionAuthority: false, publicationAuthority: false, conformanceClaim: 'none', ...rest })
10
+
11
+ /** Read-only composition over a loaded project, not a graph or source writer.
12
+ * The request is bounded JSON text: {profile, projectRef, roleNodeIds, target,
13
+ * theme?}. This initial binding supports classified active Markdown resources,
14
+ * Resource bindings and CollectionView/StatusView. Other requirements
15
+ * refuse instead of being converted to a superficially similar surface.
16
+ */
17
+ export function createTemplateProjectView(project, requestJson) {
18
+ try {
19
+ if (typeof requestJson !== 'string' || Buffer.byteLength(requestJson) > 1048576) return outcome(false, ['invalid request'])
20
+ const request = JSON.parse(requestJson)
21
+ // The profile reference validator also enforces bounded plain JSON before
22
+ // the request is used; no caller-supplied accessors enter this adapter.
23
+ templateReference('PayloadRef', 'request', '1', request)
24
+ if (!request || Array.isArray(request) || Object.keys(request).some(key => !['profile', 'projectRef', 'roleNodeIds', 'target', 'theme'].includes(key))) return outcome(false, ['invalid request'])
25
+ const { profile, projectRef, roleNodeIds, target, theme = 'light' } = request
26
+ const definition = validateTemplateDefinition(profile)
27
+ if (!definition.ok) return outcome(false, definition.errors)
28
+ if (!Object.hasOwn(PROJECTION_TARGETS, target)) return outcome(false, ['unknown projection target'])
29
+ if (!roleNodeIds || Array.isArray(roleNodeIds) || typeof roleNodeIds !== 'object') return outcome(false, ['invalid role selection'])
30
+ if (profile.runtimeProfileRef !== null) return outcome(false, ['runtime profile requires its owning adapter'])
31
+ const templateRef = templateReference('TemplateRef', profile.id, profile.version, profile)
32
+ const host = validateTemplateHost(profile, {
33
+ schema: 'atelier-template-host@v1', templateRef, carrier: 'web', atelierVersion: packageVersion,
34
+ packRefs: [], semanticPrimitives: ['Resource'], surfacePrimitives: ['CollectionView', 'StatusView'],
35
+ runtimeProfileRef: null, optionalDecisions: [],
36
+ })
37
+ if (!host.ok) return outcome(false, host.errors)
38
+ const cache = createGraphFileCache()
39
+ const graph = buildCanonicalGraph(project, { fileCache: cache })
40
+ if (!graph.ok) return outcome(false, ['canonical graph invalid'])
41
+ const projected = projectGraph(graph, { target })
42
+ const visible = new Map(projected.nodes.filter(node => node.classification === 'classified' && node.status === 'active').map(node => [node.id, node]))
43
+ const records = [], sources = new Map(), roles = []
44
+ for (const [roleRef, ids] of Object.entries(roleNodeIds)) {
45
+ if (!profile.semanticRoles.some(role => role.id === roleRef) || !Array.isArray(ids) || !ids.length || ids.length > 256 || ids.some(id => typeof id !== 'string') || new Set(ids).size !== ids.length) return outcome(false, ['invalid role selection'])
46
+ if (profile.semanticRoles.find(role => role.id === roleRef).primitive !== 'Resource') return outcome(false, ['semantic role has no canonical binding'])
47
+ const refs = []
48
+ for (const id of ids) {
49
+ const node = visible.get(id)
50
+ // A node's audience cannot widen its repository's read boundary.
51
+ if (!node || !PROJECTION_TARGETS[target].has(node.repoAccess?.readBoundary)) return outcome(false, ['selected source unavailable for this view'])
52
+ const source = cache.files.get(`${node.repo}\u0000${node.path}`)
53
+ if (!source || !/^[a-f0-9]{64}$/.test(source.digest)) return outcome(false, ['source byte pin unavailable'])
54
+ if (!sources.has(id)) {
55
+ const document = { nodeId: id, repository: node.repo, path: node.path, rawDigest: `sha256:${source.digest}`, title: node.title, summary: node.summary }
56
+ const ref = templateReference('SourceRef', `source.${identity(id)}`, document.rawDigest, document)
57
+ sources.set(id, { ref, document }); records.push({ ref, document })
58
+ }
59
+ refs.push(sources.get(id).ref)
60
+ }
61
+ roles.push({ roleRef, sourceRefs: refs })
62
+ }
63
+ const binding = { schema: 'atelier-template-binding@v1', id: `binding.${identity(projectRef)}`, version: '1.0.0', templateRef, projectRef, roles }
64
+ const checked = validateTemplateBinding(profile, binding, records)
65
+ if (!checked.ok) return outcome(false, checked.errors)
66
+ const bindingRef = templateReference('BindingRef', binding.id, binding.version, binding)
67
+ const nodes = profile.surfaceRoles.map((view, index) => {
68
+ const id = `view-${index}`
69
+ if (view.primitive === 'StatusView') return { id, type: 'status', label: view.id, text: 'Read-only local projection. Runtime actions and publication are unavailable.', tone: 'neutral', details: host.diagnostics.map(item => ({ label: item.id, value: item.kind === 'semantic-role' ? 'Optional role unsupported; binding unavailable' : `${item.status}; deterministic or manual fallback` })), actions: [] }
70
+ const sourceIds = [...new Set(view.semanticRoleRefs.flatMap(role => roleNodeIds[role] ?? []))]
71
+ return { id, type: 'collection', label: view.id, items: sourceIds.map(sourceId => ({ id: `item-${identity(sourceId)}`, label: sources.get(sourceId).document.title, detail: sources.get(sourceId).document.summary })) }
72
+ })
73
+ nodes.unshift({ id: 'description', type: 'text', label: 'Inputs and limits', text: `Inputs: ${profile.inputs.join('; ')}. Limits: ${profile.limitations.join('; ')}` })
74
+ nodes.push({ id: 'identity', type: 'text', label: 'Template and binding identity', text: `Template: ${templateRef.id}@${templateRef.version}; ${templateRef.digest}. Binding: ${bindingRef.id}@${bindingRef.version}; ${bindingRef.digest}. Release: none (local preview). Structural references only; not verified authorship, release acceptance or rights.` })
75
+ // Provenance travels with readable exports, not only the API envelope.
76
+ // Use opaque refs and byte pins; never expose local repository paths here.
77
+ for (let offset = 0; offset < records.length; offset += 128) nodes.push({
78
+ id: `provenance-${offset}`, type: 'collection', label: 'Source provenance',
79
+ items: records.slice(offset, offset + 128).map(({ ref, document }) => ({ id: `source-${identity(ref.id)}`, label: document.title, detail: `SourceRef: ${ref.id}; ${ref.digest}. Source bytes: ${document.rawDigest}.` })),
80
+ })
81
+ const model = { schema: 'atelier.presentation/v1', version: '1.0.0', id: `template-${identity(profile.id)}`, title: profile.purpose, lang: 'en', direction: 'ltr', theme, density: 'comfortable', navigation: [], panes: [{ id: 'content', label: 'Template preview', role: 'primary', blocks: nodes.map(node => node.id) }], nodes }
82
+ if (validatePresentation(model).length) return outcome(false, ['presentation binding invalid'])
83
+ // Reuse the exact same non-interactive renderer for local web and document
84
+ // preview. This creates bytes only; it does not publish or erase old copies.
85
+ const html = renderReadOnlyDocument(model)
86
+ const textPreview = [model.title, ...nodes.flatMap(node => [node.label, ...(node.items ? node.items.flatMap(item => [item.label, item.detail]) : [node.text]), ...(node.details ?? []).map(item => `${item.label}: ${item.value}`)])].join('\n\n')
87
+ return outcome(true, [], { templateRef, bindingRef, binding, records, model, html, textPreview, capabilityDiagnostics: host.diagnostics, optionalDecisions: host.diagnostics.filter(item => item.kind !== 'semantic-role'), target, lifecycle: 'active-only', artifacts: { web: html, documents: html } })
88
+ } catch {
89
+ // Do not disclose source paths or source text through exception messages.
90
+ return outcome(false, ['template project composition refused'])
91
+ }
92
+ }
@@ -0,0 +1,225 @@
1
+ import fs from 'node:fs'
2
+ import { createHash } from 'node:crypto'
3
+ import { canonicalize } from '../attestation/jcs.mjs'
4
+ import { validateJsonSchema } from '../export/atelier-export-contract.mjs'
5
+ import { validateDecisionRequest, validateDecisionResult } from '../decisions/contracts.mjs'
6
+
7
+ const schema = JSON.parse(fs.readFileSync(new URL('../../contracts/atelier-template-profile.v1.schema.json', import.meta.url), 'utf8'))
8
+ const shapes = Object.fromEntries(Object.keys(schema.$defs).map(kind => [kind, {
9
+ $schema: schema.$schema, $defs: schema.$defs, $ref: `#/$defs/${kind}`,
10
+ }]))
11
+ const forbidden = new Set(['__proto__', 'prototype', 'constructor'])
12
+ const referenceKinds = new Set(Object.keys(schema.$defs).filter(kind => kind.endsWith('Ref')))
13
+
14
+ // Inspect descriptors before traversing untrusted values; no getters or toJSON.
15
+ // All public entry points snapshot once before relational or hash checks.
16
+ function snapshot(input) {
17
+ const ancestors = new Set()
18
+ let nodes = 0, bytes = 0
19
+ function copy(value, depth) {
20
+ if (++nodes > 20000 || depth > 24) throw new TypeError('template input limit')
21
+ if (value === null || typeof value === 'boolean' || typeof value === 'string' || typeof value === 'number' && Number.isFinite(value)) {
22
+ if (typeof value === 'string' && value.length > 1048576) throw new TypeError('template input limit')
23
+ bytes += Buffer.byteLength(JSON.stringify(value))
24
+ if (bytes > 1048576) throw new TypeError('template input limit')
25
+ return value
26
+ }
27
+ if (!value || typeof value !== 'object' || ancestors.has(value)) throw new TypeError('template input is not JSON')
28
+ const array = Array.isArray(value), proto = Object.getPrototypeOf(value)
29
+ if (array ? proto !== Array.prototype : proto !== Object.prototype && proto !== null) throw new TypeError('template input prototype')
30
+ const descriptors = Object.getOwnPropertyDescriptors(value), keys = Reflect.ownKeys(descriptors)
31
+ if (keys.some(key => typeof key !== 'string' || forbidden.has(key))) throw new TypeError('template input key')
32
+ ancestors.add(value)
33
+ const result = array ? [] : {}
34
+ if (array && (descriptors.length.value > 20000 || keys.length !== descriptors.length.value + 1)) throw new TypeError('template array shape')
35
+ for (const key of keys) {
36
+ if (array && key === 'length') continue
37
+ const d = descriptors[key]
38
+ if (!Object.hasOwn(d, 'value') || !d.enumerable || array && !/^(0|[1-9][0-9]*)$/.test(key)) throw new TypeError('template input descriptor')
39
+ bytes += Buffer.byteLength(key) + 4
40
+ if (bytes > 1048576) throw new TypeError('template input limit')
41
+ result[key] = copy(d.value, depth + 1)
42
+ }
43
+ if (array && result.length !== descriptors.length.value) throw new TypeError('template sparse array')
44
+ ancestors.delete(value)
45
+ return result
46
+ }
47
+ return copy(input, 0)
48
+ }
49
+
50
+ function shape(kind, value, errors) {
51
+ const invalid = validateJsonSchema(shapes[kind], value).length > 0
52
+ if (invalid) errors.push(`invalid ${kind} shape`)
53
+ return !invalid
54
+ }
55
+ function same(a, b) { return canonicalize(a) === canonicalize(b) }
56
+ function unique(items, key, errors, label) {
57
+ if (new Set(items.map(key)).size !== items.length) errors.push(`duplicate ${label}`)
58
+ }
59
+ function refFor(kind, id, version, document) {
60
+ if (!referenceKinds.has(kind)) throw new TypeError('unsupported reference kind')
61
+ const domain = { schema: 'atelier-template-digest@v1', kind, id, version, document }
62
+ const digest = `sha256:${createHash('sha256').update(canonicalize(domain)).digest('hex')}`
63
+ const ref = { kind, id, version, digest }
64
+ if (validateJsonSchema(shapes[kind], ref).length) throw new TypeError('invalid reference identity')
65
+ return ref
66
+ }
67
+
68
+ /** Content identity only; neither signature verification nor owner acceptance. */
69
+ export function templateReference(kind, id, version, document) {
70
+ const value = snapshot({ kind, id, version, document })
71
+ return refFor(value.kind, value.id, value.version, value.document)
72
+ }
73
+
74
+ function run(stage, input, inspect) {
75
+ const errors = [], diagnostics = []
76
+ try { inspect(snapshot(input), errors, diagnostics) }
77
+ catch { errors.push('expected bounded plain JSON and supported reference identity') }
78
+ return { ok: errors.length === 0, stage, errors: [...new Set(errors)], diagnostics,
79
+ authority: 'structural-only', executionAuthority: false, publicationAuthority: false }
80
+ }
81
+
82
+ function definition(value, errors) {
83
+ if (!shape('definition', value, errors)) return false
84
+ unique(value.semanticRoles, x => x.id, errors, 'semantic role')
85
+ unique(value.surfaceRoles, x => x.id, errors, 'surface role')
86
+ unique(value.carriers, x => x.id, errors, 'carrier')
87
+ unique(value.extensions, x => x.namespace, errors, 'extension namespace')
88
+ unique(value.optionalDecisions, x => x.id, errors, 'optional decision')
89
+ unique(value.compatibility.packRefs, x => x.id, errors, 'pack identity')
90
+ const roles = new Set(value.semanticRoles.map(x => x.id))
91
+ for (const view of value.surfaceRoles) {
92
+ if (view.semanticRoleRefs.some(id => !roles.has(id))) errors.push('dangling surface semantic role')
93
+ }
94
+ // No extension handlers are registered by this additive profile. Optional
95
+ // opaque payloads remain inert. Required semantics cannot be silently ignored.
96
+ if (value.extensions.some(x => x.required)) errors.push('required extension unsupported')
97
+ return errors.length === 0
98
+ }
99
+
100
+ export function validateTemplateDefinition(input) {
101
+ return run('definition', input, (value, errors) => definition(value, errors))
102
+ }
103
+
104
+ function inventory(records, errors) {
105
+ const entries = new Map(), identities = new Set()
106
+ if (!Array.isArray(records) || records.length > 256) {
107
+ errors.push('invalid reference inventory'); return entries
108
+ }
109
+ for (const record of records) {
110
+ if (!shape('record', record, errors)) continue
111
+ const { ref, document } = record
112
+ const key = canonicalize(ref), identity = canonicalize([ref.kind, ref.id, ref.version])
113
+ if (identities.has(identity)) errors.push('ambiguous reference inventory')
114
+ identities.add(identity)
115
+ if (!same(ref, refFor(ref.kind, ref.id, ref.version, document))) errors.push('reference digest mismatch')
116
+ entries.set(key, document)
117
+ }
118
+ return entries
119
+ }
120
+ function resolve(ref, entries, errors) {
121
+ const key = canonicalize(ref)
122
+ if (!entries.has(key)) errors.push('dangling exact reference')
123
+ return entries.get(key)
124
+ }
125
+ function extensions(value, entries, errors) {
126
+ for (const entry of value.extensions) {
127
+ resolve(entry.schemaRef, entries, errors)
128
+ resolve(entry.payloadRef, entries, errors)
129
+ }
130
+ }
131
+ function binding(profile, value, entries, errors) {
132
+ if (!shape('binding', value, errors)) return false
133
+ if (!same(value.templateRef, refFor('TemplateRef', profile.id, profile.version, profile))) errors.push('template binding mismatch')
134
+ unique(value.roles, x => x.roleRef, errors, 'role binding')
135
+ const roles = new Map(profile.semanticRoles.map(x => [x.id, x]))
136
+ for (const entry of value.roles) {
137
+ const role = roles.get(entry.roleRef)
138
+ if (!role) errors.push('unknown semantic role')
139
+ if (role?.cardinality === 'one' && entry.sourceRefs.length !== 1) errors.push('plural singular role binding')
140
+ for (const ref of entry.sourceRefs) resolve(ref, entries, errors)
141
+ }
142
+ for (const role of profile.semanticRoles) {
143
+ if (role.required && !value.roles.some(x => x.roleRef === role.id)) errors.push('required semantic role unbound')
144
+ }
145
+ extensions(profile, entries, errors)
146
+ return errors.length === 0
147
+ }
148
+
149
+ export function validateTemplateBinding(profile, projectBinding, records = []) {
150
+ return run('binding', { profile, projectBinding, records }, (value, errors) => {
151
+ if (!definition(value.profile, errors)) return
152
+ binding(value.profile, value.projectBinding, inventory(value.records, errors), errors)
153
+ })
154
+ }
155
+
156
+ export function validateTemplateRelease(profile, projectBinding, release, records = []) {
157
+ return run('release', { profile, projectBinding, release, records }, (value, errors) => {
158
+ if (!definition(value.profile, errors) || !shape('release', value.release, errors)) return
159
+ const entries = inventory(value.records, errors)
160
+ if (!binding(value.profile, value.projectBinding, entries, errors)) return
161
+ const templateRef = refFor('TemplateRef', value.profile.id, value.profile.version, value.profile)
162
+ const bindingRef = refFor('BindingRef', value.projectBinding.id, value.projectBinding.version, value.projectBinding)
163
+ if (!same(value.release.templateRef, templateRef) || !same(value.release.bindingRef, bindingRef)) errors.push('release binding mismatch')
164
+ const carriers = new Set(), sources = new Set(value.projectBinding.roles.flatMap(x => x.sourceRefs.map(canonicalize)))
165
+ for (const ref of value.release.projectionRefs) {
166
+ const projection = resolve(ref, entries, errors)
167
+ if (!shape('projection', projection, errors)) continue
168
+ if (projection.id !== ref.id || projection.version !== ref.version) errors.push('projection identity mismatch')
169
+ if (!same(projection.templateRef, templateRef) || !same(projection.bindingRef, bindingRef)) errors.push('projection binding mismatch')
170
+ if (carriers.has(projection.carrier)) errors.push('duplicate projection carrier')
171
+ carriers.add(projection.carrier)
172
+ if (!value.profile.carriers.some(x => x.id === projection.carrier)) errors.push('undeclared projection carrier')
173
+ for (const source of projection.sourceRefs) {
174
+ if (!sources.has(canonicalize(source))) errors.push('projection source outside project binding')
175
+ resolve(source, entries, errors)
176
+ }
177
+ for (const item of [...projection.policyRefs, ...projection.authorityDecisionRefs, projection.payloadRef, projection.capabilityRef]) resolve(item, entries, errors)
178
+ }
179
+ for (const carrier of value.profile.carriers) {
180
+ if (carrier.required && !carriers.has(carrier.id)) errors.push('required carrier missing')
181
+ }
182
+ })
183
+ }
184
+
185
+ /** Host declarations are structural observations, never runtime grants. */
186
+ export function validateTemplateHost(profile, host) {
187
+ return run('host', { profile, host }, (value, errors, diagnostics) => {
188
+ if (!definition(value.profile, errors) || !shape('host', value.host, errors)) return
189
+ const { profile: p, host: h } = value
190
+ if (!same(h.templateRef, refFor('TemplateRef', p.id, p.version, p))) errors.push('host template mismatch')
191
+ if (!p.carriers.some(x => x.id === h.carrier)) errors.push('undeclared host carrier')
192
+ if (!p.compatibility.atelierVersions.includes(h.atelierVersion)) errors.push('incompatible host version')
193
+ unique(h.packRefs, x => x.id, errors, 'host pack')
194
+ for (const pack of p.compatibility.packRefs) {
195
+ if (!h.packRefs.some(x => same(x, pack))) errors.push('required exact pack unavailable')
196
+ }
197
+ for (const role of p.semanticRoles) if (!h.semanticPrimitives.includes(role.primitive)) {
198
+ if (role.required) errors.push('unsupported semantic primitive')
199
+ else diagnostics.push({ kind: 'semantic-role', id: role.id, status: 'unsupported', bindingAllowed: false })
200
+ }
201
+ for (const role of p.surfaceRoles) if (!h.surfacePrimitives.includes(role.primitive)) errors.push('unsupported surface primitive')
202
+ if (!same(p.runtimeProfileRef, h.runtimeProfileRef)) errors.push('runtime profile missing or mismatched')
203
+ unique(h.optionalDecisions, x => x.id, errors, 'host decision')
204
+ for (const observation of h.optionalDecisions) {
205
+ if (!p.optionalDecisions.some(x => x.id === observation.id)) errors.push('undeclared optional decision')
206
+ if (observation.status === 'available' && observation.capabilityRef === null) errors.push('available decision needs exact capability reference')
207
+ if (observation.status === 'absent' && observation.capabilityRef !== null) errors.push('absent decision cannot claim a capability reference')
208
+ }
209
+ for (const request of p.optionalDecisions) {
210
+ const status = h.optionalDecisions.find(x => x.id === request.id)?.status ?? 'absent'
211
+ diagnostics.push({ id: request.id, status, fallback: 'deterministic-or-manual', invocationAuthorized: false })
212
+ }
213
+ })
214
+ }
215
+
216
+ /** Reuse provider-neutral decision semantics; never select or invoke a provider. */
217
+ export function validateTemplateDecision(profile, decisionId, request, result) {
218
+ return run('advisory-decision', { profile, decisionId, request, result }, (value, errors) => {
219
+ if (!definition(value.profile, errors)) return
220
+ if (!value.profile.optionalDecisions.some(x => x.id === value.decisionId)) errors.push('undeclared optional decision')
221
+ if (value.request?.task !== value.decisionId) errors.push('decision task binding mismatch')
222
+ if (!validateDecisionRequest(value.request).ok) errors.push('invalid decision request')
223
+ else if (!validateDecisionResult(value.request, value.result).ok) errors.push('invalid decision result')
224
+ })
225
+ }