@mnstry/atelier 0.2.0-alpha.11 → 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 (560) hide show
  1. package/CHANGELOG.md +1005 -0
  2. package/README.md +149 -9
  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-obsidian-generation-manifest.v2.schema.json +372 -0
  21. package/contracts/atelier-personal-workspace-manifest.v1.schema.json +87 -0
  22. package/contracts/atelier-personal-workspace-overlay.v1.schema.json +299 -0
  23. package/contracts/atelier-template-adoption.v1.schema.json +223 -0
  24. package/contracts/atelier-template-profile.v1.schema.json +1168 -0
  25. package/contracts/atelier-trackable.v1.schema.json +781 -0
  26. package/contracts/atelier-upgrade-plan.v3.schema.json +532 -0
  27. package/contracts/compat-baseline.json +1 -1
  28. package/docs/architecture.md +89 -0
  29. package/docs/assurance-controls.md +4 -3
  30. package/docs/blocks/capability-stewardship.md +17 -0
  31. package/docs/blocks/claims.md +16 -3
  32. package/docs/blocks/inquiry-harnesses.md +19 -0
  33. package/docs/blocks/learning-harnesses.md +17 -0
  34. package/docs/capability-stewardship.md +269 -0
  35. package/docs/coordination.md +51 -0
  36. package/docs/decision-practices.md +111 -0
  37. package/docs/decisions.md +29 -0
  38. package/docs/evidence-navigation.md +237 -0
  39. package/docs/exact-upgrades.md +3 -2
  40. package/docs/ingestion.md +181 -0
  41. package/docs/inquiry-harnesses.md +211 -0
  42. package/docs/install.md +80 -3
  43. package/docs/intake-and-guides.md +5 -0
  44. package/docs/integration-contract-decisions.md +24 -2
  45. package/docs/interaction-and-reflection.md +72 -0
  46. package/docs/knowledge-graph.md +20 -0
  47. package/docs/knowledge-health/api.md +30 -0
  48. package/docs/knowledge-health/assessment/api.md +96 -0
  49. package/docs/knowledge-health/assessment/capabilities.md +40 -0
  50. package/docs/knowledge-health/assessment/evaluation.md +27 -0
  51. package/docs/knowledge-health/assessment/facilitator.md +30 -0
  52. package/docs/knowledge-health/assessment/quickstart.md +61 -0
  53. package/docs/knowledge-health/assessment/troubleshooting.md +23 -0
  54. package/docs/knowledge-health/devday/facilitator.md +16 -0
  55. package/docs/knowledge-health/devday/participant-response.md +22 -0
  56. package/docs/knowledge-health/devday/public-default.md +107 -0
  57. package/docs/knowledge-health/devday/quickstart.md +18 -0
  58. package/docs/knowledge-health/devday/support.md +22 -0
  59. package/docs/knowledge-health/exercise.md +17 -0
  60. package/docs/knowledge-health/facilitator.md +20 -0
  61. package/docs/knowledge-health/quickstart.md +94 -0
  62. package/docs/knowledge-health/readme.md +15 -0
  63. package/docs/knowledge-health/support.md +34 -0
  64. package/docs/knowledge-setup.md +248 -0
  65. package/docs/knowledge-workspace.md +264 -0
  66. package/docs/learning-harnesses.md +238 -0
  67. package/docs/learning.md +215 -0
  68. package/docs/local-services.md +242 -26
  69. package/docs/obsidian-contract.md +878 -36
  70. package/docs/obsidian-plugin.md +619 -0
  71. package/docs/obsidian.md +809 -64
  72. package/docs/optional-ingestion.md +83 -0
  73. package/docs/personal-workspace.md +257 -0
  74. package/docs/practical-judgment.md +92 -0
  75. package/docs/progressive-ingestion-acceptance.md +89 -0
  76. package/docs/progressive-ingestion.md +235 -0
  77. package/docs/project-command-inventory.md +1 -0
  78. package/docs/release-engineering.md +56 -1
  79. package/docs/repo-boundary-guard.md +94 -0
  80. package/docs/research-starter-quickstart.md +66 -0
  81. package/docs/research-starter.md +114 -0
  82. package/docs/semantic-ingestion.md +198 -0
  83. package/docs/semantic-operation.md +317 -0
  84. package/docs/skill-steward.md +6 -0
  85. package/docs/template-profile.md +164 -0
  86. package/docs/template-upgrades.md +114 -0
  87. package/docs/trackables.md +111 -0
  88. package/docs/upgrade.md +68 -3
  89. package/fixtures/atelier-architecture/binding/invalid/document.json +19 -0
  90. package/fixtures/atelier-architecture/binding/valid/document.json +19 -0
  91. package/fixtures/atelier-architecture/catalog/invalid/document.json +290 -0
  92. package/fixtures/atelier-architecture/catalog/valid/document.json +343 -0
  93. package/fixtures/atelier-build-contract/attempt/invalid/document.json +20 -0
  94. package/fixtures/atelier-build-contract/attempt/valid/document.json +19 -0
  95. package/fixtures/atelier-build-contract/candidate/invalid/document.json +24 -0
  96. package/fixtures/atelier-build-contract/candidate/valid/document.json +23 -0
  97. package/fixtures/atelier-build-contract/decision/invalid/document.json +31 -0
  98. package/fixtures/atelier-build-contract/decision/valid/document.json +30 -0
  99. package/fixtures/atelier-build-contract/delivery/invalid/document.json +18 -0
  100. package/fixtures/atelier-build-contract/delivery/valid/document.json +17 -0
  101. package/fixtures/atelier-build-contract/gate/invalid/document.json +22 -0
  102. package/fixtures/atelier-build-contract/gate/valid/document.json +21 -0
  103. package/fixtures/atelier-build-contract/ledger/invalid/document.json +270 -0
  104. package/fixtures/atelier-build-contract/ledger/valid/document.json +269 -0
  105. package/fixtures/atelier-build-contract/objective/invalid/document.json +73 -0
  106. package/fixtures/atelier-build-contract/objective/valid/document.json +72 -0
  107. package/fixtures/atelier-build-contract/progress/invalid/document.json +18 -0
  108. package/fixtures/atelier-build-contract/progress/valid/document.json +17 -0
  109. package/fixtures/atelier-capability-contract/adoption/invalid/document.json +71 -0
  110. package/fixtures/atelier-capability-contract/adoption/valid/document.json +70 -0
  111. package/fixtures/atelier-capability-contract/event/invalid/document.json +18 -0
  112. package/fixtures/atelier-capability-contract/event/valid/document.json +17 -0
  113. package/fixtures/atelier-capability-contract/journal/invalid/document.json +382 -0
  114. package/fixtures/atelier-capability-contract/journal/valid/document.json +381 -0
  115. package/fixtures/atelier-capability-contract/notice/invalid/document.json +8 -0
  116. package/fixtures/atelier-capability-contract/notice/valid/document.json +7 -0
  117. package/fixtures/atelier-capability-contract/package/invalid/document.json +59 -0
  118. package/fixtures/atelier-capability-contract/package/valid/document.json +58 -0
  119. package/fixtures/atelier-capability-contract/plan/invalid/document.json +107 -0
  120. package/fixtures/atelier-capability-contract/plan/valid/document.json +106 -0
  121. package/fixtures/atelier-capability-contract/release/invalid/document.json +85 -0
  122. package/fixtures/atelier-capability-contract/release/valid/document.json +84 -0
  123. package/fixtures/atelier-capability-contract/state/invalid/document.json +316 -0
  124. package/fixtures/atelier-capability-contract/state/valid/document.json +315 -0
  125. package/fixtures/atelier-coordination/dependency/invalid/document.json +37 -0
  126. package/fixtures/atelier-coordination/dependency/valid/document.json +36 -0
  127. package/fixtures/atelier-coordination/directive/invalid/document.json +25 -0
  128. package/fixtures/atelier-coordination/directive/valid/document.json +24 -0
  129. package/fixtures/atelier-coordination/disposition/invalid/document.json +34 -0
  130. package/fixtures/atelier-coordination/disposition/valid/document.json +33 -0
  131. package/fixtures/atelier-coordination/outcome/invalid/document.json +39 -0
  132. package/fixtures/atelier-coordination/outcome/valid/document.json +38 -0
  133. package/fixtures/atelier-coordination/recovery/invalid/document.json +34 -0
  134. package/fixtures/atelier-coordination/recovery/valid/document.json +33 -0
  135. package/fixtures/atelier-harness-contract/handoff/invalid/document.json +25 -0
  136. package/fixtures/atelier-harness-contract/handoff/valid/document.json +24 -0
  137. package/fixtures/atelier-ingestion/invalid/authority.json +19 -0
  138. package/fixtures/atelier-ingestion/semantic-baseline.json +26 -0
  139. package/fixtures/atelier-ingestion/semantic-operation.json +75 -0
  140. package/fixtures/atelier-ingestion/valid/plan-input.json +18 -0
  141. package/fixtures/atelier-inquiry-contract/assessment/invalid/document.json +41 -0
  142. package/fixtures/atelier-inquiry-contract/assessment/valid/document.json +40 -0
  143. package/fixtures/atelier-inquiry-contract/bundle/invalid/document.json +51 -0
  144. package/fixtures/atelier-inquiry-contract/bundle/valid/document.json +50 -0
  145. package/fixtures/atelier-inquiry-contract/campaign/invalid/document.json +17 -0
  146. package/fixtures/atelier-inquiry-contract/campaign/valid/document.json +16 -0
  147. package/fixtures/atelier-inquiry-contract/decision/invalid/document.json +22 -0
  148. package/fixtures/atelier-inquiry-contract/decision/valid/document.json +21 -0
  149. package/fixtures/atelier-inquiry-contract/feedback/invalid/document.json +19 -0
  150. package/fixtures/atelier-inquiry-contract/feedback/valid/document.json +18 -0
  151. package/fixtures/atelier-inquiry-contract/handoff/invalid/document.json +92 -0
  152. package/fixtures/atelier-inquiry-contract/handoff/valid/document.json +91 -0
  153. package/fixtures/atelier-inquiry-contract/hypothesis/invalid/document.json +20 -0
  154. package/fixtures/atelier-inquiry-contract/hypothesis/valid/document.json +19 -0
  155. package/fixtures/atelier-inquiry-contract/ledger/invalid/document.json +680 -0
  156. package/fixtures/atelier-inquiry-contract/ledger/valid/document.json +679 -0
  157. package/fixtures/atelier-inquiry-contract/legacy-assessment/invalid/document.json +19 -0
  158. package/fixtures/atelier-inquiry-contract/legacy-assessment/valid/document.json +18 -0
  159. package/fixtures/atelier-inquiry-contract/request/invalid/document.json +50 -0
  160. package/fixtures/atelier-inquiry-contract/request/valid/document.json +49 -0
  161. package/fixtures/atelier-inquiry-contract/source/invalid/document.json +18 -0
  162. package/fixtures/atelier-inquiry-contract/source/valid/document.json +17 -0
  163. package/fixtures/atelier-inquiry-contract/withdrawal/invalid/document.json +16 -0
  164. package/fixtures/atelier-inquiry-contract/withdrawal/valid/document.json +15 -0
  165. package/fixtures/atelier-instruction-adoption/context-receipt/invalid/document.json +18 -0
  166. package/fixtures/atelier-instruction-adoption/context-receipt/valid/document.json +17 -0
  167. package/fixtures/atelier-instruction-adoption/plan/invalid/document.json +63 -0
  168. package/fixtures/atelier-instruction-adoption/plan/valid/document.json +62 -0
  169. package/fixtures/atelier-instruction-adoption/receipt/invalid/document.json +22 -0
  170. package/fixtures/atelier-instruction-adoption/receipt/valid/document.json +21 -0
  171. package/fixtures/atelier-instruction-adoption/state/invalid/document.json +30 -0
  172. package/fixtures/atelier-instruction-adoption/state/valid/document.json +29 -0
  173. package/fixtures/atelier-interaction/act/invalid/document.json +25 -0
  174. package/fixtures/atelier-interaction/act/valid/document.json +24 -0
  175. package/fixtures/atelier-interaction/assessment/invalid/document.json +32 -0
  176. package/fixtures/atelier-interaction/assessment/valid/document.json +31 -0
  177. package/fixtures/atelier-interaction/observation/invalid/document.json +22 -0
  178. package/fixtures/atelier-interaction/observation/valid/document.json +21 -0
  179. package/fixtures/atelier-interaction/policy/invalid/document.json +20 -0
  180. package/fixtures/atelier-interaction/policy/valid/document.json +19 -0
  181. package/fixtures/atelier-judgment/case/invalid/document.json +76 -0
  182. package/fixtures/atelier-judgment/case/valid/document.json +75 -0
  183. package/fixtures/atelier-judgment/decision/invalid/document.json +38 -0
  184. package/fixtures/atelier-judgment/decision/valid/document.json +37 -0
  185. package/fixtures/atelier-judgment/native-decision.md +3 -0
  186. package/fixtures/atelier-judgment/practice-reconsideration/definition.json +92 -0
  187. package/fixtures/atelier-judgment/practice-reconsideration/scenario.json +435 -0
  188. package/fixtures/atelier-knowledge-contract/activation/invalid/document.json +26 -0
  189. package/fixtures/atelier-knowledge-contract/activation/valid/document.json +25 -0
  190. package/fixtures/atelier-knowledge-contract/contribution/invalid/document.json +34 -0
  191. package/fixtures/atelier-knowledge-contract/contribution/valid/document.json +33 -0
  192. package/fixtures/atelier-knowledge-contract/domain/invalid/document.json +41 -0
  193. package/fixtures/atelier-knowledge-contract/domain/valid/document.json +40 -0
  194. package/fixtures/atelier-knowledge-contract/domain-revision/invalid/document.json +46 -0
  195. package/fixtures/atelier-knowledge-contract/domain-revision/valid/document.json +45 -0
  196. package/fixtures/atelier-knowledge-contract/evaluation/invalid/document.json +21 -0
  197. package/fixtures/atelier-knowledge-contract/evaluation/valid/document.json +20 -0
  198. package/fixtures/atelier-knowledge-contract/ledger/invalid/document.json +318 -0
  199. package/fixtures/atelier-knowledge-contract/ledger/valid/document.json +317 -0
  200. package/fixtures/atelier-knowledge-contract/relation/invalid/document.json +25 -0
  201. package/fixtures/atelier-knowledge-contract/relation/valid/document.json +24 -0
  202. package/fixtures/atelier-knowledge-contract/review/invalid/document.json +18 -0
  203. package/fixtures/atelier-knowledge-contract/review/valid/document.json +17 -0
  204. package/fixtures/atelier-knowledge-contract/withdrawal/invalid/document.json +16 -0
  205. package/fixtures/atelier-knowledge-contract/withdrawal/valid/document.json +15 -0
  206. package/fixtures/atelier-learning/invalid/authority.json +14 -0
  207. package/fixtures/atelier-learning/valid/capture.json +13 -0
  208. package/fixtures/atelier-personal-workspace-contract/manifest/invalid/authority-key.json +17 -0
  209. package/fixtures/atelier-personal-workspace-contract/manifest/invalid/nonempty-ext.json +25 -0
  210. package/fixtures/atelier-personal-workspace-contract/manifest/invalid/wrong-schema.json +22 -0
  211. package/fixtures/atelier-personal-workspace-contract/manifest/valid/manifest.json +22 -0
  212. package/fixtures/atelier-personal-workspace-contract/overlay/invalid/authority-key.json +25 -0
  213. package/fixtures/atelier-personal-workspace-contract/overlay/invalid/unknown-top-level-field.json +27 -0
  214. package/fixtures/atelier-personal-workspace-contract/overlay/invalid/wrong-schema.json +24 -0
  215. package/fixtures/atelier-trackable/definition/invalid/document.json +15 -0
  216. package/fixtures/atelier-trackable/definition/valid/document.json +14 -0
  217. package/fixtures/atelier-trackable/request/invalid/document.json +23 -0
  218. package/fixtures/atelier-trackable/request/valid/document.json +22 -0
  219. package/fixtures/boundary-delegations/invalid/authority-key.v1.json +30 -0
  220. package/fixtures/boundary-delegations/invalid/push-operation.v1.json +28 -0
  221. package/fixtures/boundary-delegations/invalid/unsigned.v1.json +25 -0
  222. package/fixtures/boundary-delegations/valid/empty.v1.json +4 -0
  223. package/fixtures/boundary-delegations/valid/signed-operator.v1.json +30 -0
  224. package/fixtures/boundary-owner-keys/invalid/wrong-algorithm.v1.json +15 -0
  225. package/fixtures/boundary-owner-keys/invalid/wrong-curve.v1.json +15 -0
  226. package/fixtures/boundary-owner-keys/valid/one-owner.v1.json +17 -0
  227. package/fixtures/capability-packages/adoption.example.json +70 -0
  228. package/fixtures/capability-packages/evidence-review/capability-package.json +58 -0
  229. package/fixtures/capability-packages/evidence-review/capability-release.json +84 -0
  230. package/fixtures/capability-packages/evidence-review/evaluation.md +15 -0
  231. package/fixtures/capability-packages/evidence-review/skills/evidence-review/SKILL.md +22 -0
  232. package/fixtures/capability-packages/evidence-review/skills/evidence-review/references/ledger.md +7 -0
  233. package/fixtures/capability-packages/research/capability-package.json +88 -0
  234. package/fixtures/capability-packages/research/capability-release.json +134 -0
  235. package/fixtures/capability-packages/research/evaluation.md +15 -0
  236. package/fixtures/capability-packages/research/skills/research-integrate/SKILL.md +29 -0
  237. package/fixtures/capability-packages/research/skills/research-integrate/references/integration.md +10 -0
  238. package/fixtures/capability-packages/research/skills/research-plan/SKILL.md +27 -0
  239. package/fixtures/capability-packages/research/skills/research-plan/references/inquiries.md +26 -0
  240. package/fixtures/capability-packages/research/skills/research-synthesize/SKILL.md +23 -0
  241. package/fixtures/capability-packages/research/skills/research-synthesize/references/synthesis.md +12 -0
  242. package/fixtures/evidence-navigation/invalid/duplicate-operation.json +16 -0
  243. package/fixtures/evidence-navigation/invalid/instruction-kind.json +36 -0
  244. package/fixtures/evidence-navigation/invalid/invalid-digest.json +36 -0
  245. package/fixtures/evidence-navigation/invalid/invalid-time.json +36 -0
  246. package/fixtures/evidence-navigation/invalid/missing-attribution.json +35 -0
  247. package/fixtures/evidence-navigation/invalid/reserved-extension.json +19 -0
  248. package/fixtures/evidence-navigation/invalid/unattributed-acceptance.json +36 -0
  249. package/fixtures/evidence-navigation/invalid/uncoerced-version.json +16 -0
  250. package/fixtures/evidence-navigation/invalid/unknown-currency.json +17 -0
  251. package/fixtures/evidence-navigation/invalid/unknown-selector-field.json +37 -0
  252. package/fixtures/evidence-navigation/invalid/unsupported-operation.json +15 -0
  253. package/fixtures/evidence-navigation/valid/claim.json +36 -0
  254. package/fixtures/evidence-navigation/valid/host.json +16 -0
  255. package/fixtures/evidence-navigation/valid/profile.json +16 -0
  256. package/fixtures/evidence-navigation/valid/snapshot.json +17 -0
  257. package/fixtures/harness-packages/build-harness/capability-package.json +61 -0
  258. package/fixtures/harness-packages/build-harness/capability-release.json +82 -0
  259. package/fixtures/harness-packages/build-harness/evaluation.md +3 -0
  260. package/fixtures/harness-packages/build-harness/skills/atelier-build-harness/SKILL.md +59 -0
  261. package/fixtures/harness-packages/knowledge-harness/capability-package.json +61 -0
  262. package/fixtures/harness-packages/knowledge-harness/capability-release.json +82 -0
  263. package/fixtures/harness-packages/knowledge-harness/evaluation.md +3 -0
  264. package/fixtures/harness-packages/knowledge-harness/skills/atelier-knowledge-harness/SKILL.md +60 -0
  265. package/fixtures/harnesses/learning-cycle.json +780 -0
  266. package/fixtures/inquiry/workshop.json +675 -0
  267. package/fixtures/inquiry-packages/discovery-harness/capability-package.json +61 -0
  268. package/fixtures/inquiry-packages/discovery-harness/capability-release.json +82 -0
  269. package/fixtures/inquiry-packages/discovery-harness/evaluation.md +3 -0
  270. package/fixtures/inquiry-packages/discovery-harness/skills/atelier-discovery-harness/SKILL.md +48 -0
  271. package/fixtures/inquiry-packages/research-harness/capability-package.json +61 -0
  272. package/fixtures/inquiry-packages/research-harness/capability-release.json +82 -0
  273. package/fixtures/inquiry-packages/research-harness/evaluation.md +3 -0
  274. package/fixtures/inquiry-packages/research-harness/skills/atelier-research-harness/SKILL.md +43 -0
  275. package/fixtures/knowledge-health/calibration-suite.json +26 -0
  276. package/fixtures/knowledge-health/calibration-trial.json +37 -0
  277. package/fixtures/knowledge-health/corrected/checklist.md +18 -0
  278. package/fixtures/knowledge-health/devday/participant-record.json +19 -0
  279. package/fixtures/knowledge-health/expected-summary.json +27 -0
  280. package/fixtures/knowledge-health/workshop/README.md +20 -0
  281. package/fixtures/knowledge-health/workshop/atelier.project.json +7 -0
  282. package/fixtures/knowledge-health/workshop/knowledge-plan.json +65 -0
  283. package/fixtures/knowledge-health/workshop/records/checklist.md +18 -0
  284. package/fixtures/knowledge-health/workshop/records/workshop.md +18 -0
  285. package/fixtures/knowledge-health/workshop/repo-access.v1.json +5 -0
  286. package/fixtures/knowledge-health/workspace/atelier.project.json +18 -0
  287. package/fixtures/knowledge-health/workspace/knowledge-plan.json +74 -0
  288. package/fixtures/knowledge-health/workspace/records/checklist.md +18 -0
  289. package/fixtures/knowledge-health/workspace/records/pilot.md +16 -0
  290. package/fixtures/knowledge-health/workspace/repo-access.v1.json +9 -0
  291. package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/case-only-collision.v2.json +160 -0
  292. package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/flat-note-path.v2.json +160 -0
  293. package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/hidden-folder.v2.json +160 -0
  294. package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/identity-in-the-body.v2.json +160 -0
  295. package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/link-breaking-name.v2.json +160 -0
  296. package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/missing-identity-region.v2.json +156 -0
  297. package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/missing-layout-version.v2.json +159 -0
  298. package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/note-named-like-a-folder.v2.json +160 -0
  299. package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/unknown-region-kind.v2.json +160 -0
  300. package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/v1-contract-version.v2.json +161 -0
  301. package/fixtures/obsidian/contracts/generation-manifest-v2/valid/three-notes.v2.json +160 -0
  302. package/fixtures/obsidian/edits/workspace.json +1 -1
  303. package/fixtures/obsidian/materialization/expected-full.json +21 -21
  304. package/fixtures/obsidian/materialization/expected-full.layout-1.json +99 -0
  305. package/fixtures/obsidian/materialization/expected-scoped.json +7 -7
  306. package/fixtures/obsidian/materialization/expected-scoped.layout-1.json +39 -0
  307. package/fixtures/personal-workspace/overlay.json +24 -0
  308. package/fixtures/templates/local-library.v1.json +75 -0
  309. package/fixtures/templates/upgrade-contracts/adoption-policy.v2/invalid.json +16 -0
  310. package/fixtures/templates/upgrade-contracts/adoption-policy.v2/valid.json +13 -0
  311. package/fixtures/templates/upgrade-contracts/migration.v3/invalid.json +24 -0
  312. package/fixtures/templates/upgrade-contracts/migration.v3/valid.json +21 -0
  313. package/fixtures/templates/upgrade-contracts/template-adoption.v1/invalid.json +60 -0
  314. package/fixtures/templates/upgrade-contracts/template-adoption.v1/valid.json +57 -0
  315. package/fixtures/templates/upgrade-contracts/upgrade-plan.v3/invalid.json +85 -0
  316. package/fixtures/templates/upgrade-contracts/upgrade-plan.v3/valid.json +82 -0
  317. package/package.json +64 -6
  318. package/plugins/obsidian/main.js +444 -0
  319. package/plugins/obsidian/manifest.json +9 -0
  320. package/plugins/obsidian/styles.css +22 -0
  321. package/skills/claude/atelier-build-harness/SKILL.md +59 -0
  322. package/skills/claude/atelier-coordination/SKILL.md +32 -0
  323. package/skills/claude/atelier-discovery-harness/SKILL.md +48 -0
  324. package/skills/claude/atelier-guided-upgrade/SKILL.md +29 -0
  325. package/skills/claude/atelier-interaction/SKILL.md +34 -0
  326. package/skills/claude/atelier-knowledge-harness/SKILL.md +60 -0
  327. package/skills/claude/atelier-knowledge-setup/SKILL.md +72 -0
  328. package/skills/claude/atelier-practical-judgment/SKILL.md +64 -0
  329. package/skills/claude/atelier-research-harness/SKILL.md +43 -0
  330. package/skills/claude/atelier-skill-steward/SKILL.md +53 -0
  331. package/skills/claude/atelier-trackables/SKILL.md +40 -0
  332. package/skills/codex/atelier-build-harness/SKILL.md +59 -0
  333. package/skills/codex/atelier-coordination/SKILL.md +32 -0
  334. package/skills/codex/atelier-discovery-harness/SKILL.md +48 -0
  335. package/skills/codex/atelier-guided-upgrade/SKILL.md +29 -0
  336. package/skills/codex/atelier-interaction/SKILL.md +34 -0
  337. package/skills/codex/atelier-knowledge-harness/SKILL.md +60 -0
  338. package/skills/codex/atelier-knowledge-setup/SKILL.md +72 -0
  339. package/skills/codex/atelier-practical-judgment/SKILL.md +64 -0
  340. package/skills/codex/atelier-research-harness/SKILL.md +43 -0
  341. package/skills/codex/atelier-skill-steward/SKILL.md +53 -0
  342. package/skills/codex/atelier-trackables/SKILL.md +40 -0
  343. package/src/architecture/catalog.mjs +355 -0
  344. package/src/architecture/index.mjs +93 -0
  345. package/src/boundary/content-rules.mjs +4 -2
  346. package/src/boundary/delegation-command.mjs +158 -0
  347. package/src/boundary/delegation.mjs +396 -0
  348. package/src/boundary/policy.mjs +52 -10
  349. package/src/build/git.mjs +18 -0
  350. package/src/build/index.mjs +3 -0
  351. package/src/build/ledger.mjs +88 -0
  352. package/src/capabilities/adoption.mjs +381 -0
  353. package/src/capabilities/evidence.mjs +140 -0
  354. package/src/capabilities/files.mjs +124 -0
  355. package/src/capabilities/index.mjs +4 -0
  356. package/src/capabilities/instructions.mjs +250 -0
  357. package/src/capabilities/package.mjs +136 -0
  358. package/src/cli/command-failure.mjs +36 -0
  359. package/src/cli/execute-command.mjs +16 -2
  360. package/src/cli/run.mjs +65 -9
  361. package/src/coauthor/store.mjs +43 -2
  362. package/src/collaboration/proposals.mjs +189 -0
  363. package/src/commands/architecture.mjs +27 -0
  364. package/src/commands/boundary.mjs +3 -0
  365. package/src/commands/capability.mjs +73 -0
  366. package/src/commands/enroll.mjs +3 -0
  367. package/src/commands/harness.mjs +79 -0
  368. package/src/commands/ingest.mjs +39 -0
  369. package/src/commands/init.mjs +4 -1
  370. package/src/commands/inquiry.mjs +66 -0
  371. package/src/commands/knowledge-semantic.mjs +128 -0
  372. package/src/commands/knowledge.mjs +119 -0
  373. package/src/commands/learn.mjs +61 -0
  374. package/src/commands/obsidian.mjs +832 -62
  375. package/src/commands/practice.mjs +24 -0
  376. package/src/commands/setup.mjs +22 -2
  377. package/src/commands/trackable.mjs +19 -0
  378. package/src/contracts/corpus.mjs +36 -0
  379. package/src/coordination/index.mjs +110 -0
  380. package/src/egress/forbidden-egress.mjs +2 -0
  381. package/src/evidence-navigation/contracts.mjs +60 -0
  382. package/src/evidence-navigation/evaluate.mjs +69 -0
  383. package/src/evidence-navigation/generation.mjs +22 -0
  384. package/src/evidence-navigation/index.mjs +2 -0
  385. package/src/evidence-navigation/local.mjs +167 -0
  386. package/src/evidence-navigation/schema.generated.mjs +604 -0
  387. package/src/evidence-navigation/types.mjs +12 -0
  388. package/src/export/atelier-export-contract.mjs +35 -8
  389. package/src/graph/enroll.mjs +248 -0
  390. package/src/graph/graph.mjs +17 -1
  391. package/src/graph/knowledge-graph.mjs +58 -8
  392. package/src/harnesses/contracts.mjs +41 -0
  393. package/src/harnesses/exchange.mjs +154 -0
  394. package/src/harnesses/feedback.mjs +16 -0
  395. package/src/harnesses/history.mjs +37 -0
  396. package/src/harnesses/index.mjs +4 -0
  397. package/src/harnesses/store.mjs +29 -0
  398. package/src/ingestion/contracts.mjs +41 -0
  399. package/src/ingestion/evaluation.mjs +119 -0
  400. package/src/ingestion/optional/composition/index.mjs +171 -0
  401. package/src/ingestion/optional/evaluation/index.mjs +219 -0
  402. package/src/ingestion/optional/graphify/composition-binding.mjs +60 -0
  403. package/src/ingestion/optional/graphify/composition_receiving.py +185 -0
  404. package/src/ingestion/optional/graphify/graphify_supplier.py +933 -0
  405. package/src/ingestion/optional/graphify/json_bridge.py +163 -0
  406. package/src/ingestion/optional/graphify/query_receiving.py +209 -0
  407. package/src/ingestion/optional/index.mjs +9 -0
  408. package/src/ingestion/optional/jev/bridge.mjs +166 -0
  409. package/src/ingestion/optional/jev/composition-binding.mjs +116 -0
  410. package/src/ingestion/optional/jev/judgments.mjs +174 -0
  411. package/src/ingestion/optional/jev/native-translation.mjs +32 -0
  412. package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/__init__.py +7 -0
  413. package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/adapter.py +966 -0
  414. package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/artifacts.py +101 -0
  415. package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/projection.py +146 -0
  416. package/src/ingestion/optional/lightrag/composition-binding.mjs +67 -0
  417. package/src/ingestion/optional/vanilla/composition-binding.mjs +102 -0
  418. package/src/ingestion/optional/vanilla/index.mjs +218 -0
  419. package/src/ingestion/optional/vanilla/native-normalization.mjs +187 -0
  420. package/src/ingestion/processors.mjs +218 -0
  421. package/src/ingestion/semantic.mjs +203 -0
  422. package/src/ingestion/store.mjs +372 -0
  423. package/src/inquiry/beliefs.mjs +32 -0
  424. package/src/inquiry/contracts.mjs +23 -0
  425. package/src/inquiry/index.mjs +5 -0
  426. package/src/inquiry/ledger.mjs +135 -0
  427. package/src/inquiry/lenses.mjs +15 -0
  428. package/src/inquiry/projection.mjs +77 -0
  429. package/src/inquiry/store.mjs +35 -0
  430. package/src/intake/read-scope.mjs +12 -0
  431. package/src/intake/store.mjs +69 -6
  432. package/src/interaction/index.mjs +91 -0
  433. package/src/judgment/index.mjs +82 -0
  434. package/src/judgment/practice-evaluation.mjs +70 -0
  435. package/src/judgment/practice.mjs +144 -0
  436. package/src/knowledge/context.mjs +217 -0
  437. package/src/knowledge/index.mjs +5 -0
  438. package/src/knowledge/ingestion.mjs +79 -0
  439. package/src/knowledge/intake.mjs +33 -0
  440. package/src/knowledge/ledger.mjs +110 -0
  441. package/src/knowledge/participatory/guidance.mjs +36 -0
  442. package/src/knowledge/participatory/guided-contribution.mjs +44 -0
  443. package/src/knowledge/participatory/index.mjs +2 -0
  444. package/src/knowledge/participatory/installed-workshop.mjs +108 -0
  445. package/src/knowledge/plan.mjs +67 -0
  446. package/src/knowledge/projection.mjs +43 -0
  447. package/src/knowledge/semantic-operation.mjs +567 -0
  448. package/src/knowledge/sessions.mjs +467 -0
  449. package/src/knowledge/workspace.mjs +295 -0
  450. package/src/knowledge-health/assessment.mjs +165 -0
  451. package/src/knowledge-health/capabilities.mjs +350 -0
  452. package/src/knowledge-health/context.mjs +62 -0
  453. package/src/knowledge-health/example.mjs +47 -0
  454. package/src/knowledge-health/index.mjs +10 -0
  455. package/src/knowledge-health/owner-join.mjs +88 -0
  456. package/src/knowledge-health/proof/common.mjs +24 -0
  457. package/src/knowledge-health/proof/installed-check.mjs +100 -0
  458. package/src/knowledge-health/proof/native-readback.mjs +37 -0
  459. package/src/knowledge-health/proof/rehearse.mjs +68 -0
  460. package/src/knowledge-health/proof/validate-participant.mjs +17 -0
  461. package/src/knowledge-health/proof/verify-native-capture.mjs +14 -0
  462. package/src/knowledge-health/proof/workshop-check.mjs +36 -0
  463. package/src/knowledge-health/rule-identity.mjs +44 -0
  464. package/src/knowledge-health/workshop-composition.mjs +19 -0
  465. package/src/knowledge-health/workshop-profile.mjs +176 -0
  466. package/src/knowledge-health/workshop.mjs +99 -0
  467. package/src/learning/adapters.mjs +68 -0
  468. package/src/learning/assistance.mjs +127 -0
  469. package/src/learning/contracts.mjs +65 -0
  470. package/src/learning/store.mjs +272 -0
  471. package/src/personal-workspace/index.mjs +391 -0
  472. package/src/personal-workspace/selection.mjs +385 -0
  473. package/src/project/config.mjs +8 -0
  474. package/src/project/durable-state.mjs +3 -1
  475. package/src/project/file-class.mjs +1 -1
  476. package/src/project/private-state.mjs +10 -1
  477. package/src/projection/obsidian/contracts.mjs +224 -18
  478. package/src/projection/obsidian/edits/apply.mjs +63 -19
  479. package/src/projection/obsidian/edits/regions.mjs +137 -42
  480. package/src/projection/obsidian/materialize/index.mjs +6 -3
  481. package/src/projection/obsidian/materialize/path-registry.mjs +443 -57
  482. package/src/projection/obsidian/materialize/prepare-view.mjs +382 -131
  483. package/src/projection/obsidian/materialize/redaction.mjs +327 -0
  484. package/src/projection/obsidian/materialize/settings.mjs +113 -34
  485. package/src/projection/obsidian/personal-workspace.mjs +436 -0
  486. package/src/projection/obsidian/plugin-bridge/bundle.mjs +50 -0
  487. package/src/projection/obsidian/plugin-bridge/channel.mjs +174 -0
  488. package/src/projection/obsidian/plugin-bridge/index.mjs +10 -0
  489. package/src/projection/obsidian/proposals/adapter.mjs +47 -8
  490. package/src/projection/obsidian/proposals/observation.mjs +20 -7
  491. package/src/projection/obsidian/publication/bridge-script.mjs +11 -5
  492. package/src/projection/obsidian/publication/index.mjs +2 -1
  493. package/src/projection/obsidian/publication/publisher.mjs +312 -36
  494. package/src/projection/obsidian/publication/transport.mjs +63 -8
  495. package/src/projection/obsidian/publication/vault-list.mjs +200 -0
  496. package/src/projection/obsidian/recovery/index.mjs +4 -1
  497. package/src/projection/obsidian/recovery/restart.mjs +3 -1
  498. package/src/projection/obsidian/recovery/store.mjs +168 -8
  499. package/src/projection/obsidian/selection-ui/contribution.mjs +6 -5
  500. package/src/projection/obsidian/selection-ui/focus.mjs +14 -7
  501. package/src/projection/obsidian/selection-ui/selection-state.mjs +2 -2
  502. package/src/projection/obsidian/selection-ui/selection.mjs +8 -3
  503. package/src/readiness/readiness.mjs +10 -3
  504. package/src/reflection/index.mjs +56 -0
  505. package/src/runtime/obsidian/app-capability.mjs +104 -12
  506. package/src/runtime/obsidian/app-production-seams.mjs +161 -42
  507. package/src/runtime/obsidian/app-registration.mjs +313 -0
  508. package/src/runtime/obsidian/app-restart.mjs +154 -0
  509. package/src/runtime/obsidian/engine.mjs +288 -29
  510. package/src/runtime/obsidian/extension-points.mjs +1 -1
  511. package/src/runtime/obsidian/index.mjs +15 -8
  512. package/src/runtime/obsidian/launch-plan.mjs +91 -0
  513. package/src/runtime/obsidian/lifecycle.mjs +193 -22
  514. package/src/runtime/obsidian/login-item.mjs +332 -0
  515. package/src/runtime/obsidian/machine-settings.mjs +119 -12
  516. package/src/runtime/obsidian/opening.mjs +369 -63
  517. package/src/runtime/obsidian/pending-edits.mjs +10 -0
  518. package/src/runtime/obsidian/pipeline.mjs +71 -3
  519. package/src/runtime/obsidian/plugin-channel.mjs +289 -0
  520. package/src/runtime/obsidian/plugin-choice.mjs +182 -0
  521. package/src/runtime/obsidian/plugin-drift.mjs +124 -0
  522. package/src/runtime/obsidian/plugin-presence.mjs +61 -0
  523. package/src/runtime/obsidian/process-identity.mjs +20 -10
  524. package/src/runtime/obsidian/project-views.mjs +282 -0
  525. package/src/runtime/obsidian/questions.mjs +48 -0
  526. package/src/runtime/obsidian/release-watch.mjs +56 -0
  527. package/src/runtime/obsidian/service-main.mjs +113 -17
  528. package/src/runtime/obsidian/service-manager-production.mjs +61 -0
  529. package/src/runtime/obsidian/service-managers.mjs +275 -0
  530. package/src/runtime/obsidian/service-record.mjs +105 -5
  531. package/src/runtime/obsidian/service-server.mjs +50 -12
  532. package/src/runtime/obsidian/service.mjs +134 -7
  533. package/src/runtime/obsidian/startup-adapters.mjs +59 -21
  534. package/src/runtime/obsidian/state-store.mjs +13 -1
  535. package/src/runtime/obsidian/unheld-evidence.mjs +84 -0
  536. package/src/runtime/obsidian/vault-location.mjs +300 -0
  537. package/src/runtime/obsidian/view-counts.mjs +34 -0
  538. package/src/server/local-sidecar.mjs +193 -7
  539. package/src/server/server.mjs +45 -6
  540. package/src/template-bindings/project.mjs +92 -0
  541. package/src/templates/profile.mjs +225 -0
  542. package/src/trackables/domain.mjs +175 -0
  543. package/src/trackables/store.mjs +114 -0
  544. package/src/ui/knowledge-page.mjs +1388 -0
  545. package/src/upgrade/explanation.mjs +1 -1
  546. package/src/upgrade/template-participant.mjs +171 -0
  547. package/src/upgrade/transaction.mjs +72 -25
  548. package/src/upgrade/upgrade.mjs +2 -1
  549. package/templates/external-project-workspace/pack-lifecycle.json +6 -1
  550. package/templates/knowledge-workspace/.gitattributes +2 -0
  551. package/templates/knowledge-workspace/AGENTS.md +27 -0
  552. package/templates/knowledge-workspace/README.md +44 -0
  553. package/templates/knowledge-workspace/atelier.project.json +22 -0
  554. package/templates/knowledge-workspace/gitignore +6 -0
  555. package/templates/knowledge-workspace/knowledge-plan.json +111 -0
  556. package/templates/knowledge-workspace/knowledge-plan.schema.json +281 -0
  557. package/templates/knowledge-workspace/records/forecast.md +22 -0
  558. package/templates/knowledge-workspace/records/inspection.md +25 -0
  559. package/templates/knowledge-workspace/records/telescope.md +24 -0
  560. package/templates/knowledge-workspace/repo-access.v1.json +9 -0
@@ -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
+ }
@@ -0,0 +1,175 @@
1
+ import fs from 'node:fs'
2
+ import Ajv from 'ajv/dist/2020.js'
3
+ import addFormats from 'ajv-formats'
4
+ import { boundedLearningValue, learningDigest as digest } from '../learning/contracts.mjs'
5
+
6
+ const schema = JSON.parse(fs.readFileSync(new URL('../../contracts/atelier-trackable.v1.schema.json', import.meta.url)))
7
+ const ajv = new Ajv({ strict: true, allErrors: true }); addFormats(ajv)
8
+ const validators = new Map()
9
+ export const TRACKABLE_ENGINE_REVISION = 'atelier-trackable-reference@v1'
10
+ export function validateTrackable(value, shape = 'definition') {
11
+ try { boundedLearningValue(value) } catch (error) { return [error.message] }
12
+ if (!['definition', 'request'].includes(shape)) return ['unsupported trackable shape']
13
+ if (!validators.has(shape)) validators.set(shape, ajv.compile({ $schema: schema.$schema, $defs: schema.$defs, $ref: `#/$defs/${shape}` }))
14
+ const validate = validators.get(shape)
15
+ if (!validate(value)) return validate.errors.map(e => `${e.instancePath || '/'}: ${e.message}`)
16
+ if (shape === 'definition') {
17
+ if ((value.profile === 'recurring-practice') !== (value.schedule !== null)) return ['/schedule: daily schedule is required only for recurring practice']
18
+ if (value.schedule) try { new Intl.DateTimeFormat('en', { timeZone: value.schedule.timezone }).format(0) } catch { return ['/schedule/timezone: unsupported timezone'] }
19
+ }
20
+ return []
21
+ }
22
+ function valid(value, shape) {
23
+ const errors = validateTrackable(value, shape)
24
+ if (errors.length) throw new Error(`invalid trackable ${shape}: ${errors.join('; ')}`)
25
+ return value
26
+ }
27
+ export function releaseTrackable(definition) {
28
+ valid(definition, 'definition')
29
+ return { definition: structuredClone(definition), digest: digest(definition), engine: TRACKABLE_ENGINE_REVISION }
30
+ }
31
+ export function initialTrackables(scope) {
32
+ if (typeof scope !== 'string' || !/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/.test(scope)) throw new Error('exact scope identity required')
33
+ return { scope, revision: 0, definitions: [], instances: [], evidence: [] }
34
+ }
35
+ function instanceFor(state, id) {
36
+ const instance = state.instances.find(i => i.id === id)
37
+ if (!instance) throw new Error('trackable instance not found in scope')
38
+ return instance
39
+ }
40
+ function definitionFor(state, pin) {
41
+ const release = state.definitions.find(d => d.digest === pin)
42
+ if (!release || releaseTrackable(release.definition).digest !== pin) throw new Error('immutable definition missing or changed')
43
+ return release.definition
44
+ }
45
+ function adoptionAt(instance, at) {
46
+ const adoption = instance.adoptions.findLast(a => Date.parse(a.effectiveAt) <= Date.parse(at))
47
+ if (!adoption) throw new Error('no adopted definition at occurrence time')
48
+ return adoption
49
+ }
50
+ function dateAt(at, timezone) {
51
+ const parts = new Intl.DateTimeFormat('en', { timeZone: timezone, year: 'numeric', month: '2-digit', day: '2-digit' }).formatToParts(new Date(at))
52
+ return ['year', 'month', 'day'].map(k => parts.find(p => p.type === k).value).join('-')
53
+ }
54
+ export function trackableOccurrence(state, instanceId, occurredAt, eventId = null) {
55
+ return resolveOccurrence(state, instanceId, occurredAt, eventId)
56
+ }
57
+ function resolveOccurrence(state, instanceId, occurredAt, eventId, resolution = null) {
58
+ if (!/^\d{4}-\d\d-\d\dT.*Z$/.test(occurredAt) || !Number.isFinite(Date.parse(occurredAt))) throw new Error('UTC occurrence instant required')
59
+ const instance = instanceFor(state, instanceId), adoption = adoptionAt(instance, occurredAt)
60
+ const definition = definitionFor(state, adoption.definitionDigest)
61
+ if (definition.profile === 'qualitative-series' && (typeof eventId !== 'string' || !eventId)) throw new Error('qualitative occurrence requires source event identity')
62
+ // Replay owns a journal-pinned calendar observation. New commands and previews
63
+ // omit it and resolve using the current host. Never accept it in command input.
64
+ const localDate = resolution ? resolution.occurrence?.localDate
65
+ : definition.schedule ? dateAt(occurredAt, definition.schedule.timezone) : null
66
+ if (definition.schedule) {
67
+ if (typeof localDate !== 'string' || !/^\d{4}-\d\d-\d\d$/.test(localDate) || !Number.isFinite(Date.parse(localDate)) || new Date(localDate).toISOString().slice(0, 10) !== localDate) throw new Error('invalid recorded calendar date')
68
+ } else if (localDate !== null) throw new Error('invalid recorded calendar date')
69
+ const key = { scope: state.scope, instanceId, definitionDigest: adoption.definitionDigest, effectiveAt: adoption.effectiveAt,
70
+ window: localDate ?? (definition.profile === 'milestone' ? 'milestone' : eventId) }
71
+ const occurrence = { id: digest(key), ...key, localDate, timezone: definition.schedule?.timezone ?? null }
72
+ if (resolution) {
73
+ const tzdata = resolution.tzdata
74
+ if (tzdata !== null && (typeof tzdata !== 'string' || !/^[a-zA-Z0-9._-]{1,64}$/.test(tzdata))) throw new Error('invalid recorded timezone data version')
75
+ if (digest(resolution) !== digest({ occurredAt, sourceRef: eventId, occurrence, tzdata })) throw new Error('recorded occurrence identity differs')
76
+ }
77
+ return occurrence
78
+ }
79
+ function checkResult(definition, result, value) {
80
+ if (definition.profile === 'qualitative-series') {
81
+ if (result !== 'observed' || typeof value !== 'string' || !value.trim()) throw new Error('qualitative series requires an attributed text observation')
82
+ } else if (result === 'observed' || (result === 'completed' ? value !== true : value !== null)) {
83
+ throw new Error('practice/milestone completion requires true; other dispositions carry no value')
84
+ }
85
+ }
86
+ function effectiveEvidence(state, instanceId) { return state.evidence.filter(e => e.instanceId === instanceId && !e.withdrawn) }
87
+ // The same reducer is used by authoring preview, durable local execution and
88
+ // host adapters. Host authentication and storage are separate responsibilities.
89
+ export function reduceTrackables(previous, command, { actor, recordedAt, occurrenceResolution = null } = {}) {
90
+ valid(command, 'request')
91
+ if (occurrenceResolution !== null && (command.operation !== 'record' || typeof occurrenceResolution !== 'object' || Array.isArray(occurrenceResolution))) throw new Error('recorded occurrence requires a record command')
92
+ if (typeof actor !== 'string' || !actor || actor.length > 128 || !Number.isFinite(Date.parse(recordedAt))) throw new Error('actor and recording time required')
93
+ if (command.expectedRevision !== previous.revision) throw new Error('trackable revision changed; reload')
94
+ const state = structuredClone(previous), input = command.input
95
+ if (command.operation === 'release') {
96
+ const release = releaseTrackable(input.definition)
97
+ if (state.definitions.some(d => d.definition.id === input.definition.id && d.definition.revision === input.definition.revision)) throw new Error('definition revision already exists; publish a new revision')
98
+ state.definitions.push(release)
99
+ } else if (command.operation === 'instantiate') {
100
+ if (state.instances.some(i => i.id === input.instanceId)) throw new Error('instance identity already exists')
101
+ const definition = definitionFor(state, input.definitionDigest)
102
+ state.instances.push({ id: input.instanceId, subject: input.subject, owner: actor, lifecycle: 'active',
103
+ disposition: definition.profile === 'milestone' ? 'open' : null, dispositionBasis: [],
104
+ adoptions: [{ definitionDigest: input.definitionDigest, effectiveAt: input.effectiveAt }] })
105
+ } else if (command.operation === 'correct') {
106
+ const evidence = state.evidence.find(e => e.id === input.evidenceId)
107
+ if (!evidence) throw new Error('evidence not found in scope')
108
+ if (evidence.withdrawn) throw new Error('withdrawn evidence cannot be silently restored')
109
+ if (input.replacement) checkResult(definitionFor(state, evidence.occurrence.definitionDigest), input.replacement.result, input.replacement.value)
110
+ evidence.corrections.push({ actor, recordedAt, reason: input.reason, previous: { result: evidence.result, value: evidence.value } })
111
+ if (input.replacement) Object.assign(evidence, input.replacement)
112
+ else evidence.withdrawn = true
113
+ } else {
114
+ const instance = instanceFor(state, input.instanceId)
115
+ if (command.operation === 'lifecycle') {
116
+ if (instance.lifecycle === 'retired') throw new Error('retired instance cannot be resumed; instantiate separately')
117
+ instance.lifecycle = input.status
118
+ } else if (command.operation === 'adopt') {
119
+ if (instance.lifecycle === 'retired') throw new Error('retired instance cannot adopt')
120
+ const definition = definitionFor(state, input.definitionDigest), last = instance.adoptions.at(-1)
121
+ const previousDefinition = definitionFor(state, last.definitionDigest)
122
+ if (definition.id !== previousDefinition.id || definition.profile !== previousDefinition.profile || definition.revision <= previousDefinition.revision) throw new Error('adoption requires a later compatible definition revision')
123
+ if (Date.parse(input.effectiveAt) <= Date.parse(last.effectiveAt) || state.evidence.some(e => e.instanceId === instance.id && Date.parse(e.occurredAt) >= Date.parse(input.effectiveAt))) throw new Error('adoption would reinterpret recorded occurrences')
124
+ instance.adoptions.push({ definitionDigest: input.definitionDigest, effectiveAt: input.effectiveAt })
125
+ } else if (command.operation === 'record') {
126
+ if (instance.lifecycle !== 'active') throw new Error('recording requires active tracking')
127
+ const evidence = input.evidence
128
+ if (state.evidence.some(e => e.id === evidence.id)) throw new Error('evidence identity already exists')
129
+ if (Date.parse(evidence.occurredAt) > Date.parse(recordedAt)) throw new Error('future occurrence evidence refused')
130
+ const occurrence = resolveOccurrence(state, instance.id, evidence.occurredAt, evidence.source.ref, occurrenceResolution)
131
+ checkResult(definitionFor(state, occurrence.definitionDigest), evidence.result, evidence.value)
132
+ if (state.evidence.some(e => e.instanceId === instance.id && e.source.kind === evidence.source.kind && e.source.ref === evidence.source.ref)) throw new Error('source event already recorded; retry the original command or correct it')
133
+ state.evidence.push({ ...structuredClone(evidence), instanceId: instance.id, occurrence, recorder: actor, recordedAt, corrections: [], withdrawn: false })
134
+ } else if (command.operation === 'disposition') {
135
+ if (instance.disposition === null) throw new Error('domain fulfillment is available only for the milestone profile')
136
+ const evidence = input.basisEvidenceIds.map(id => effectiveEvidence(state, instance.id).find(e => e.id === id))
137
+ if (evidence.some(e => !e) || new Set(input.basisEvidenceIds).size !== evidence.length) throw new Error('fulfillment basis must identify distinct current evidence in this instance')
138
+ if (input.status === 'fulfilled' && !evidence.some(e => e.result === 'completed')) throw new Error('fulfillment requires current completion evidence')
139
+ instance.disposition = input.status; instance.dispositionBasis = [...input.basisEvidenceIds]
140
+ }
141
+ }
142
+ state.revision++
143
+ return state
144
+ }
145
+ export function trackableView(state, { instanceId, at = new Date().toISOString() }) {
146
+ const instance = instanceFor(state, instanceId), current = trackableOccurrence(state, instanceId, at, 'current')
147
+ const definition = definitionFor(state, current.definitionDigest)
148
+ const groups = new Map()
149
+ for (const evidence of effectiveEvidence(state, instanceId)) {
150
+ if (Date.parse(evidence.occurredAt) > Date.parse(at)) continue
151
+ if (!groups.has(evidence.occurrence.id)) groups.set(evidence.occurrence.id, { occurrence: evidence.occurrence, evidence: [] })
152
+ groups.get(evidence.occurrence.id).evidence.push(evidence)
153
+ }
154
+ if (definition.profile !== 'qualitative-series' && !groups.has(current.id)) groups.set(current.id, { occurrence: current, evidence: [] })
155
+ const occurrences = [...groups.values()].map(({ occurrence, evidence }) => {
156
+ const values = [...new Set(evidence.map(e => e.result))]
157
+ return { occurrence, result: values.length === 0 ? 'unobserved' : values.length > 1 ? 'disputed' : values[0],
158
+ evidenceIds: evidence.map(e => e.id), values: evidence.map(e => e.value), assistance: [...new Set(evidence.map(e => e.assistance))] }
159
+ })
160
+ const staleDisposition = instance.disposition === 'fulfilled' && !effectiveEvidence(state, instanceId).some(e => instance.dispositionBasis.includes(e.id) && e.result === 'completed')
161
+ return { schema: 'atelier-trackable-view@v1', engine: TRACKABLE_ENGINE_REVISION, scope: state.scope, revision: state.revision,
162
+ instanceId, subject: instance.subject, definitionDigest: current.definitionDigest, at, lifecycle: instance.lifecycle,
163
+ disposition: instance.disposition, dispositionEvidence: staleDisposition ? 'needs-reconsideration' : 'current', occurrences,
164
+ coverage: 'recorded-evidence-and-current-window', completedOccurrences: occurrences.filter(o => o.result === 'completed').length,
165
+ evidenceQuality: occurrences.some(o => o.result === 'disputed') ? 'disputed' : (!occurrences.length || occurrences.some(o => o.result === 'unobserved')) ? 'missing' : 'available',
166
+ attention: 'unassessed', progressPercent: null, abilityAssessed: false, interpretationLimits: definition.interpretationLimits }
167
+ }
168
+ export function previewTrackable({ definition, subject = 'preview-subject', at, effectiveAt = at, evidence = [] }) {
169
+ let state = initialTrackables('preview')
170
+ let serial = 0
171
+ const apply = (operation, input) => { state = reduceTrackables(state, { schema: 'atelier-trackable-command@v1', requestId: `preview-${++serial}`, expectedRevision: state.revision, operation, input }, { actor: 'preview-author', recordedAt: at }) }
172
+ apply('release', { definition }); apply('instantiate', { instanceId: 'preview', subject, definitionDigest: digest(definition), effectiveAt })
173
+ for (const entry of evidence) apply('record', { instanceId: 'preview', evidence: entry })
174
+ return { ...trackableView(state, { instanceId: 'preview', at }), mode: 'preview', persisted: false }
175
+ }
@@ -0,0 +1,114 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+ import { execFileSync } from 'node:child_process'
4
+ import { canonicalize } from '../attestation/jcs.mjs'
5
+ import { boundedLearningValue, learningDigest as digest } from '../learning/contracts.mjs'
6
+ import { ensureContainedPrivateDirectory, readRegularTextNoFollow } from '../project/private-state.mjs'
7
+ import { withPrivateLock, publishPrivateFile, createVerifiedFileSequence, isPendingPrivateWrite } from '../project/durable-state.mjs'
8
+ import { initialTrackables, reduceTrackables, trackableView, validateTrackable } from './domain.mjs'
9
+ export * from './domain.mjs'
10
+
11
+ // A private local reference adapter. Product hosts call the domain reducer from
12
+ // their existing authenticated writer; they must not mirror member state here.
13
+ export function createTrackableStore({ workspaceRoot, scope, actor }) {
14
+ const root = fs.realpathSync(workspaceRoot)
15
+ initialTrackables(scope)
16
+ if (typeof actor !== 'string' || !/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/.test(actor)) throw new Error('local actor identity required')
17
+ const identity = { schema: 'atelier-trackable-local@v1', scope, rootDigest: digest(root) }
18
+ function placement() {
19
+ const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.toUpperCase().startsWith('GIT_')))
20
+ try {
21
+ if (execFileSync('git', ['-C', root, 'ls-files', '-z', '--', '.atelier-local'], { env, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] })) throw new Error('tracked')
22
+ execFileSync('git', ['-C', root, 'check-ignore', '--quiet', '.atelier-local/'], { env, stdio: 'ignore' })
23
+ } catch { throw new Error('trackables require ignored, untracked .atelier-local/ in a Git workspace') }
24
+ }
25
+ function directory(...parts) {
26
+ return ensureContainedPrivateDirectory({ workspaceRoot: root, directory: path.join(root, '.atelier-local', 'trackables', digest(scope).slice(7), ...parts) })
27
+ }
28
+ placement()
29
+ const stateDir = directory(), events = directory('events'), identityPath = path.join(stateDir, 'identity.json')
30
+ withPrivateLock(path.join(stateDir, 'operation.lock'), () => {
31
+ if (!fs.existsSync(identityPath)) {
32
+ if (fs.readdirSync(events).some(n => !isPendingPrivateWrite(n))) throw new Error('trackable identity missing from existing history')
33
+ publishPrivateFile(identityPath, canonicalize(identity))
34
+ }
35
+ if (readRegularTextNoFollow(identityPath) !== canonicalize(identity)) throw new Error('trackable scope or workspace identity differs')
36
+ })
37
+ const readSequence = createVerifiedFileSequence({ directory: events, initial: () => ({ state: initialTrackables(scope), events: [], head: null, replayError: null }),
38
+ apply(text, history, index, name) {
39
+ if (name !== `${String(index).padStart(10, '0')}.json`) throw new Error('trackable history is not contiguous')
40
+ const event = JSON.parse(text); boundedLearningValue(event)
41
+ const { digest: pin, ...body } = event
42
+ if (pin !== digest(body) || body.previous !== history.head || body.scope !== scope || body.revision !== index || history.events.some(e => e.command.requestId === body.command.requestId)) throw new Error('trackable history integrity mismatch')
43
+ const legacy = body.schema === 'atelier-trackable-event@v1'
44
+ if (!legacy && body.schema !== 'atelier-trackable-event@v2') throw new Error('unsupported trackable event schema')
45
+ if (validateTrackable(body.command, 'request').length) throw new Error('invalid journal command')
46
+ const invalidResolution = legacy ? Object.hasOwn(body, 'occurrenceResolution')
47
+ : body.command.operation === 'record'
48
+ ? !body.occurrenceResolution || typeof body.occurrenceResolution !== 'object' || Array.isArray(body.occurrenceResolution)
49
+ : body.occurrenceResolution !== null
50
+ if (invalidResolution) throw new Error('trackable event resolution missing or unexpected')
51
+ let state = null, replayError = history.replayError
52
+ if (!replayError) {
53
+ state = reduceTrackables(history.state, body.command, { actor: body.actor, recordedAt: body.recordedAt, occurrenceResolution: legacy ? null : body.occurrenceResolution })
54
+ if (digest(state) !== body.stateDigest) {
55
+ if (!legacy || body.command.operation !== 'record') throw new Error('trackable replay state differs')
56
+ // Old events did not retain their calendar resolution. Keep the chain
57
+ // exportable, but never treat a differing reconstructed state as truth.
58
+ replayError = { code: 'legacy-replay-state-differs', revision: index, message: 'legacy trackable replay state differs; original timezone data may be required' }
59
+ state = null
60
+ }
61
+ }
62
+ return { state, events: [...history.events, event], head: pin, replayError }
63
+ } })
64
+ function read() {
65
+ placement(); directory(); directory('events')
66
+ if (readRegularTextNoFollow(identityPath) !== canonicalize(identity)) throw new Error('trackable identity changed')
67
+ const names = fs.readdirSync(events).filter(n => !isPendingPrivateWrite(n))
68
+ if (names.length > 10000) throw new Error('trackable event limit exceeded')
69
+ let total = 0
70
+ for (const name of names) {
71
+ const stat = fs.lstatSync(path.join(events, name)); total += stat.size
72
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 256 * 1024 || total > 32 * 1024 * 1024) throw new Error('trackable journal limit or custody violation')
73
+ }
74
+ return readSequence()
75
+ }
76
+ read()
77
+ function verifiedRead() {
78
+ const h = read()
79
+ if (h.replayError) throw new Error(h.replayError.message)
80
+ return h
81
+ }
82
+ return Object.freeze({
83
+ snapshot() { const h = verifiedRead(); return { ...h.state, head: h.head, authenticated: false } },
84
+ view(query) { return trackableView(verifiedRead().state, query) },
85
+ execute(command) {
86
+ command = boundedLearningValue(command)
87
+ const errors = validateTrackable(command, 'request')
88
+ if (errors.length) throw new Error(errors.join('; '))
89
+ return withPrivateLock(path.join(directory(), 'operation.lock'), () => {
90
+ const h = verifiedRead(), prior = h.events.find(e => e.command.requestId === command.requestId)
91
+ if (prior) {
92
+ if (digest({ command: prior.command, actor: prior.actor }) !== digest({ command, actor })) throw new Error('request identity reused with different input or actor')
93
+ return { duplicate: true, revision: prior.revision, eventDigest: prior.digest, currentRevision: h.state.revision, persisted: true }
94
+ }
95
+ // Leave bounded space for corrections and retirement after ordinary writes stop.
96
+ if (h.state.revision >= (['correct', 'lifecycle'].includes(command.operation) ? 10000 : 9000)) throw new Error('trackable capacity reached; retain/export this history')
97
+ const recordedAt = new Date().toISOString(), state = reduceTrackables(h.state, command, { actor, recordedAt })
98
+ const occurrenceResolution = command.operation === 'record' ? { occurredAt: command.input.evidence.occurredAt, sourceRef: command.input.evidence.source.ref,
99
+ occurrence: state.evidence.at(-1).occurrence, tzdata: process.versions.tz ?? null } : null
100
+ const body = { schema: 'atelier-trackable-event@v2', scope, revision: state.revision, previous: h.head, command, actor, recordedAt, occurrenceResolution, stateDigest: digest(state) }
101
+ const event = { ...body, digest: digest(body) }, bytes = canonicalize(event)
102
+ if (Buffer.byteLength(bytes) > 256 * 1024) throw new Error('trackable event limit exceeded')
103
+ const used = h.events.reduce((sum, e) => sum + Buffer.byteLength(canonicalize(e)), 0)
104
+ const limit = ['correct', 'lifecycle'].includes(command.operation) ? 32 * 1024 * 1024 : 28 * 1024 * 1024
105
+ if (used + Buffer.byteLength(bytes) > limit) throw new Error('trackable journal capacity reached')
106
+ publishPrivateFile(path.join(events, `${String(state.revision).padStart(10, '0')}.json`), bytes)
107
+ const actual = verifiedRead()
108
+ if (actual.head !== event.digest || digest(actual.state) !== body.stateDigest) throw new Error('trackable durable readback differs')
109
+ return { duplicate: false, revision: state.revision, eventDigest: event.digest, stateDigest: body.stateDigest, persisted: true }
110
+ })
111
+ },
112
+ exportHistory() { const h = read(); return { identity, events: h.events, head: h.head, private: true, authenticated: false, stateVerified: h.replayError === null, replayError: h.replayError } },
113
+ })
114
+ }