@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,37 @@
1
+ import { assertHarness, harnessDigest, HARNESS_LIMITS } from './contracts.mjs'
2
+ export const unique = (items, label) => { if (new Set(items).size !== items.length) throw new Error(`duplicate ${label}`) }
3
+
4
+ // Shared integrity mechanics; each domain retains its own states and meaning.
5
+ export function replay(records, profile, firstKind, visit) {
6
+ if (!Array.isArray(records) || records.length > HARNESS_LIMITS.records || Buffer.byteLength(JSON.stringify(records)) > HARNESS_LIMITS.bytes) throw new Error('harness history exceeds bounds')
7
+ const entries = new Map(), dependencies = new Map(), stale = new Map()
8
+ const context = {
9
+ entries, stale, dependencies, first: null,
10
+ resolve(ref, kinds, current = true) {
11
+ const r = entries.get(ref.id)
12
+ if (!r || harnessDigest(r) !== ref.digest || !kinds.includes(r.kind)) throw new Error('missing, wrong-kind or mismatched reference')
13
+ if (current && stale.has(r.id)) throw new Error('reference requires reconsideration')
14
+ return r
15
+ },
16
+ invalidate(id, reason) {
17
+ const queue = [id], visited = new Set()
18
+ while (queue.length) {
19
+ const next = queue.shift(); if (visited.has(next)) continue; visited.add(next)
20
+ stale.set(next, [...new Set([...(stale.get(next) ?? []), reason])])
21
+ for (const [child, refs] of dependencies) if (refs.includes(next)) queue.push(child)
22
+ }
23
+ },
24
+ }
25
+ for (const record of records) {
26
+ assertHarness(record, profile)
27
+ if (entries.has(record.id)) throw new Error('immutable record identity already exists')
28
+ if (!context.first) {
29
+ if (record.kind !== firstKind || record.id !== record.run) throw new Error('harness establishment must be first')
30
+ context.first = record
31
+ } else if (record.run !== context.first.id || record.kind === firstKind) throw new Error('harness run mismatch')
32
+ const refs = visit(record, context) ?? []
33
+ entries.set(record.id, record); dependencies.set(record.id, [...new Set(refs)])
34
+ for (const target of refs) for (const reason of stale.get(target) ?? []) context.invalidate(record.id, reason)
35
+ }
36
+ return { establishment: context.first, records, head: harnessDigest(records), reconsider: [...stale].map(([id, reasons]) => ({ id, kind: entries.get(id).kind, reasons })), assurance: 'caller-reported; integrity-and-consistency-only', authority: 'none' }
37
+ }
@@ -0,0 +1,4 @@
1
+ export { HARNESS_LIMITS, EMPTY_HARNESS_HEAD, harnessDigest, harnessRef, contentDigest, validateHarnessDocument } from './contracts.mjs'
2
+ export { inspectHarness, createHarnessHandoff, verifyHarnessHandoff, buildReadiness, buildCoordinationProposal, reconcileKnowledge } from './exchange.mjs'
3
+ export { appendHarness, readHarness } from './store.mjs'
4
+ export { prepareHarnessFeedback } from './feedback.mjs'
@@ -0,0 +1,29 @@
1
+ import { jsonAt, replaceJson, stat, within, workspaceRoot } from '../capabilities/files.mjs'
2
+ import { withOperationLock } from '../capabilities/adoption.mjs'
3
+ import { assertHarness, EMPTY_HARNESS_HEAD, HARNESS_LIMITS } from './contracts.mjs'
4
+ import { inspectHarness } from './exchange.mjs'
5
+ function location(profile, run) {
6
+ if (!['knowledge', 'build'].includes(profile) || !/^[a-z][a-z0-9-]{0,63}$/.test(run ?? '')) throw new Error('invalid stored harness profile or run')
7
+ return `.atelier-local/harnesses/${profile}/${run}/ledger.json`
8
+ }
9
+ function read(root, profile, run) {
10
+ const file = location(profile, run)
11
+ if (!stat(within(root, file))) return inspectHarness(profile, [])
12
+ const ledger = assertHarness(jsonAt(root, file), profile, 'ledger'), state = inspectHarness(profile, ledger.records)
13
+ if (ledger.head !== state.head || state.establishment.id !== run) throw new Error('harness ledger integrity mismatch')
14
+ return state
15
+ }
16
+ export function readHarness({ workspaceRoot: root, profile, run }) { return read(workspaceRoot(root), profile, run) }
17
+ export function appendHarness({ workspaceRoot: input, profile, record, confirm }) {
18
+ assertHarness(record, profile)
19
+ const root = workspaceRoot(input, { write: true })
20
+ return withOperationLock(root, () => {
21
+ const before = read(root, profile, record.run)
22
+ if ((before.head ?? EMPTY_HARNESS_HEAD) !== confirm) throw new Error('append requires current harness history digest')
23
+ const state = inspectHarness(profile, [...before.records, record])
24
+ const ledger = { schema: 'atelier-harness-ledger@v1', profile, records: state.records, head: state.head }
25
+ if (Buffer.byteLength(JSON.stringify(ledger, null, 2)) + 1 > HARNESS_LIMITS.bytes) throw new Error('serialized harness ledger exceeds bounds')
26
+ replaceJson(root, location(profile, record.run), ledger)
27
+ return { recorded: record.id, head: state.head, reconsider: state.reconsider, authority: 'none' }
28
+ })
29
+ }
@@ -0,0 +1,41 @@
1
+ import fs from 'node:fs';
2
+ import { createHash } from 'node:crypto';
3
+ import Ajv2020 from 'ajv/dist/2020.js';
4
+ import addFormats from 'ajv-formats';
5
+ import { canonicalize } from '../attestation/jcs.mjs';
6
+
7
+ export const INGESTION_MAX_PLAN_BYTES = 256 * 1024;
8
+ export const INGESTION_MAX_EVIDENCE_ITEMS = 2000;
9
+ export const ingestionSchema = JSON.parse(fs.readFileSync(new URL('../../contracts/atelier-ingestion.v1.schema.json', import.meta.url), 'utf8'));
10
+ const ajv = new Ajv2020({ allErrors: false, strict: false }); addFormats(ajv); ajv.addSchema(ingestionSchema);
11
+ const validators = new Map();
12
+ export function ingestionDigest(value) { return `sha256:${createHash('sha256').update(canonicalize(value)).digest('hex')}`; }
13
+ export function validateIngestionValue(definition, value) {
14
+ if (!Object.hasOwn(ingestionSchema.$defs, definition)) return ['unknown ingestion definition'];
15
+ if (!validators.has(definition)) validators.set(definition, ajv.getSchema(`${ingestionSchema.$id}#/$defs/${definition}`));
16
+ return validators.get(definition)(value) ? [] : ['ingestion document does not match its contract'];
17
+ }
18
+ export function ingestionJson(value) {
19
+ const seen = new Set();
20
+ function visit(item, depth = 0) {
21
+ if (depth > 24) throw new Error('ingestion JSON depth exceeded');
22
+ if (item === null || typeof item === 'boolean') return;
23
+ if (typeof item === 'string') { if (item.includes('\u0000')) throw new Error('invalid ingestion text'); return; }
24
+ if (typeof item === 'number' && Number.isFinite(item)) return;
25
+ if (typeof item !== 'object' || seen.has(item) || (!Array.isArray(item) && ![null, Object.prototype].includes(Object.getPrototypeOf(item)))) throw new Error('ingestion input must be plain JSON');
26
+ seen.add(item);
27
+ if (Reflect.ownKeys(item).length > 4096) throw new Error('ingestion member ceiling exceeded');
28
+ for (const key of Reflect.ownKeys(item)) {
29
+ if (Array.isArray(item) && key === 'length') continue;
30
+ const descriptor = Object.getOwnPropertyDescriptor(item, key);
31
+ if (typeof key !== 'string' || !descriptor.enumerable || !Object.hasOwn(descriptor, 'value')) throw new Error('ingestion input must be plain JSON');
32
+ visit(descriptor.value, depth + 1);
33
+ }
34
+ if (Array.isArray(item) && Object.keys(item).length !== item.length) throw new Error('ingestion arrays must be dense');
35
+ seen.delete(item);
36
+ }
37
+ visit(value);
38
+ const text = canonicalize(value);
39
+ if (Buffer.byteLength(text) > INGESTION_MAX_PLAN_BYTES) throw new Error('ingestion document byte ceiling exceeded');
40
+ return JSON.parse(text);
41
+ }
@@ -0,0 +1,119 @@
1
+ import { createHash } from 'node:crypto'
2
+
3
+ const digest = value => `sha256:${createHash('sha256').update(JSON.stringify(value)).digest('hex')}`
4
+ const invalid = () => { throw new Error('invalid ingestion evaluation document') }
5
+ function object(value, keys, required = keys) {
6
+ if (!value || typeof value !== 'object' || Array.isArray(value)
7
+ || Object.keys(value).some(key => !keys.includes(key))
8
+ || required.some(key => !Object.hasOwn(value, key))) invalid()
9
+ }
10
+ function text(value, max = 512) { if (typeof value !== 'string' || !value.trim() || value.length > max) invalid() }
11
+ function count(value) { if (!Number.isSafeInteger(value) || value < 0) invalid() }
12
+ function measure(value) { if (value !== null && (!Number.isFinite(value) || value < 0)) invalid() }
13
+ function evidence(items) {
14
+ if (!Array.isArray(items) || items.length > 10000) invalid()
15
+ for (const item of items) {
16
+ object(item, ['locator', 'text']); object(item.locator, ['kind', 'value'])
17
+ if (!['line', 'csv-cell', 'json-pointer'].includes(item.locator.kind)) invalid()
18
+ if (typeof item.locator.value !== 'string' || item.locator.value.length > 2048
19
+ || typeof item.text !== 'string' || item.text.length > 65536) invalid()
20
+ }
21
+ }
22
+ function key(item) { return JSON.stringify([item.locator.kind, item.locator.value, item.text]) }
23
+ function score(expected, actual) {
24
+ const remaining = new Map()
25
+ for (const item of expected) remaining.set(key(item), (remaining.get(key(item)) ?? 0) + 1)
26
+ let matched = 0
27
+ for (const item of actual) {
28
+ const k = key(item)
29
+ if (remaining.get(k) > 0) { matched++; remaining.set(k, remaining.get(k) - 1) }
30
+ }
31
+ return { expected: expected.length, returned: actual.length, matched,
32
+ missed: expected.length - matched, unexpected: actual.length - matched }
33
+ }
34
+
35
+ /** Score exact structural evidence against a predeclared local suite. This does
36
+ * not judge semantic support, authenticate supplied human assessments, execute a
37
+ * provider or grant model qualification. Unknown costs remain unknown. */
38
+ export function evaluateIngestionTrial({ suite, trial }) {
39
+ let serialized
40
+ try { serialized = JSON.stringify({ suite, trial }) } catch { invalid() }
41
+ if (!serialized || Buffer.byteLength(serialized) > 4 * 1024 * 1024) invalid()
42
+ object(suite, ['id', 'revision', 'thresholds', 'cases'])
43
+ text(suite.id); text(suite.revision)
44
+ object(suite.thresholds, ['minimumRecall', 'maximumUnexpected', 'maximumFailed'])
45
+ if (!Number.isFinite(suite.thresholds.minimumRecall) || suite.thresholds.minimumRecall < 0
46
+ || suite.thresholds.minimumRecall > 1) invalid()
47
+ count(suite.thresholds.maximumUnexpected); count(suite.thresholds.maximumFailed)
48
+ if (!Array.isArray(suite.cases) || !suite.cases.length || suite.cases.length > 256) invalid()
49
+ const ids = new Set()
50
+ for (const item of suite.cases) {
51
+ object(item, ['id', 'stratum', 'split', 'sourceDigest', 'expected'])
52
+ text(item.id); text(item.stratum)
53
+ if (ids.has(item.id) || !['calibration', 'held-out'].includes(item.split)
54
+ || !/^sha256:[0-9a-f]{64}$/.test(item.sourceDigest)) invalid()
55
+ ids.add(item.id); evidence(item.expected)
56
+ }
57
+ object(trial, ['id', 'suiteDigest', 'processor', 'currency', 'outcomes'])
58
+ text(trial.id)
59
+ if (trial.suiteDigest !== digest(suite)) throw new Error('evaluation suite digest mismatch')
60
+ object(trial.processor, ['id', 'version', 'configurationDigest'])
61
+ text(trial.processor.id); text(trial.processor.version)
62
+ if (!/^sha256:[0-9a-f]{64}$/.test(trial.processor.configurationDigest)
63
+ || !/^[A-Z]{3}$/.test(trial.currency)) invalid()
64
+ if (!Array.isArray(trial.outcomes) || trial.outcomes.length > suite.cases.length) invalid()
65
+ const outcomes = new Map()
66
+ for (const item of trial.outcomes) {
67
+ object(item, ['caseId', 'sourceDigest', 'status', 'evidence', 'attempts', 'elapsedMs', 'cost', 'humanAssessment'])
68
+ if (!ids.has(item.caseId) || outcomes.has(item.caseId)
69
+ || !['complete', 'failed', 'uncertain'].includes(item.status)) invalid()
70
+ if (item.sourceDigest !== suite.cases.find(c => c.id === item.caseId).sourceDigest) {
71
+ throw new Error('evaluation source digest mismatch')
72
+ }
73
+ evidence(item.evidence); count(item.attempts); measure(item.elapsedMs); measure(item.cost)
74
+ if (item.attempts < 1 || (item.status !== 'complete' && item.evidence.length)) invalid()
75
+ if (item.humanAssessment !== null) {
76
+ object(item.humanAssessment, ['verdict', 'actorId', 'evidenceRef'])
77
+ if (!['accepted', 'rejected', 'deferred'].includes(item.humanAssessment.verdict)) invalid()
78
+ text(item.humanAssessment.actorId); text(item.humanAssessment.evidenceRef)
79
+ if (item.humanAssessment.verdict === 'accepted' && item.status !== 'complete') invalid()
80
+ }
81
+ outcomes.set(item.caseId, item)
82
+ }
83
+ const cases = suite.cases.map(item => {
84
+ const outcome = outcomes.get(item.id)
85
+ return { caseId: item.id, stratum: item.stratum, split: item.split,
86
+ status: outcome?.status ?? 'missing', ...score(item.expected, outcome?.evidence ?? []),
87
+ attempts: outcome?.attempts ?? null, elapsedMs: outcome?.elapsedMs ?? null,
88
+ cost: outcome?.cost ?? null, humanVerdict: outcome?.humanAssessment?.verdict ?? 'unassessed' }
89
+ })
90
+ function summarize(rows) {
91
+ const totals = { cases: rows.length, expected: 0, returned: 0, matched: 0, missed: 0, unexpected: 0, failed: 0 }
92
+ for (const row of rows) {
93
+ for (const k of ['expected', 'returned', 'matched', 'missed', 'unexpected']) totals[k] += row[k]
94
+ if (row.status !== 'complete') totals.failed++
95
+ }
96
+ const recall = totals.expected ? totals.matched / totals.expected : null
97
+ // A zero-evidence sample cannot establish extraction recall.
98
+ const passed = recall !== null && recall >= suite.thresholds.minimumRecall
99
+ && totals.unexpected <= suite.thresholds.maximumUnexpected && totals.failed <= suite.thresholds.maximumFailed
100
+ return { ...totals, recall, passed }
101
+ }
102
+ const strata = [...new Set(cases.filter(c => c.split === 'held-out').map(c => c.stratum))]
103
+ .map(stratum => ({ stratum, ...summarize(cases.filter(c => c.split === 'held-out' && c.stratum === stratum)) }))
104
+ const heldOut = summarize(cases.filter(c => c.split === 'held-out'))
105
+ const accepted = cases.filter(c => c.humanVerdict === 'accepted').length
106
+ const unknownCostCases = cases.filter(c => c.cost === null).length
107
+ const knownCost = cases.reduce((total, c) => total + (c.cost ?? 0), 0)
108
+ if (!Number.isFinite(knownCost)) throw new Error('evaluation cost total exceeds numeric bounds')
109
+ const totalCost = unknownCostCases ? null : knownCost
110
+ return { schema: 'mnstry.atelier-ingestion-evaluation@v1', suiteDigest: digest(suite),
111
+ trialDigest: digest(trial), processor: structuredClone(trial.processor), cases,
112
+ calibration: summarize(cases.filter(c => c.split === 'calibration')), heldOut, strata,
113
+ structuralThresholdsPassed: heldOut.passed && strata.length > 0 && strata.every(s => s.passed),
114
+ costs: { currency: trial.currency, knownCost, unknownCostCases, totalCost, acceptedResults: accepted,
115
+ costPerAcceptedResult: accepted && totalCost !== null ? totalCost / accepted : null },
116
+ semanticQualification: 'not-assessed', humanAssessmentIdentity: 'caller-asserted', grantsAuthority: false }
117
+ }
118
+
119
+ export function ingestionEvaluationDigest(value) { return digest(value) }
@@ -0,0 +1,171 @@
1
+ import { createHash } from 'node:crypto'
2
+ import { PRESENCE_PROFILES } from '../evaluation/index.mjs'
3
+
4
+ // Private host execution declarations reuse existing capability owners. This is
5
+ // not a capability-adoption, identity, evidence or knowledge canonical.
6
+ const json = value => JSON.stringify(value)
7
+ const digest = value => createHash('sha256').update(json(value)).digest('hex')
8
+ const ownValue = (object, key) => Object.getOwnPropertyDescriptor(object ?? {}, key)?.value
9
+ const callableFor = (bindings, step) => ownValue(ownValue(bindings, step.provider), step.operation)
10
+ const compiledCallables = new WeakMap()
11
+
12
+ export function compileRoute({ profile, selected, bindings, operationRoles }) {
13
+ if (!PRESENCE_PROFILES.includes(profile) || !selected || !ownValue(bindings, 'vanilla') || !operationRoles) throw new TypeError('Known presence profile, selected route, vanilla binding and own operation-role declarations required')
14
+ const present = new Set(['vanilla', ...(profile === 'vanilla' ? [] : profile.split('+'))])
15
+ const used = new Set(), callables = new Map()
16
+ const resolve = (step, role) => {
17
+ const callable = callableFor(bindings, step ?? {})
18
+ if (typeof step?.provider !== 'string' || typeof step?.operation !== 'string' || !present.has(step.provider) ||
19
+ typeof callable !== 'function') throw new Error(`Selected ${role} capability is unavailable in this profile`)
20
+ if (step.provider === 'jev' && role !== 'assess') throw new Error('Jev supplies bounded judgments rather than extraction or generation')
21
+ const id = `${step.provider}/${step.operation}`
22
+ if (used.has(id)) throw new Error('Duplicate selected operation across route roles')
23
+ if (ownValue(ownValue(operationRoles, step.provider), step.operation) !== role) throw new Error(`Selected operation is not declared for ${role}`)
24
+ used.add(id)
25
+ callables.set(id, callable)
26
+ return Object.freeze({ provider: step.provider, operation: step.operation, role, options: structuredClone(step.options ?? {}) })
27
+ }
28
+ const list = (values, role) => {
29
+ if (!Array.isArray(values)) throw new TypeError(`Explicit selected ${role} operations required`)
30
+ return values.map(x => resolve(x, role))
31
+ }
32
+ const route = { profile, extractor: selected.extractor ? resolve(selected.extractor, 'extract') : null,
33
+ assessors: list(selected.assessors ?? [], 'assess'), projectors: list(selected.projectors ?? [], 'project'),
34
+ retrievers: list(selected.retrievers ?? [], 'retrieve'), answerer: selected.answerer ? resolve(selected.answerer, 'answer') : null,
35
+ nativeAnswer: selected.nativeAnswer ? resolve(selected.nativeAnswer, 'native-answer') : null,
36
+ view: selected.view ?? 'native-exploration' }
37
+ if (!['native-exploration', 'accepted-knowledge'].includes(route.view)) throw new Error('Explicit native exploration or existing accepted knowledge view required')
38
+ if (route.answerer && !route.retrievers.length) throw new Error('Grounded answer route needs selected evidence retrieval')
39
+ if (route.nativeAnswer && (route.answerer || route.retrievers.length)) throw new Error('Coupled native retrieval/answer is a separate route; do not duplicate retrieval or generation')
40
+ const compiled = Object.freeze({ ...route, routeDigest: digest(route) })
41
+ compiledCallables.set(compiled, callables)
42
+ return compiled
43
+ }
44
+
45
+ function answerReference(ref) {
46
+ if (!ref || ['sourceId', 'revision', 'representationId'].some(k => typeof ownValue(ref, k) !== 'string' || !ownValue(ref, k) || ownValue(ref, k).length > 256)) return null
47
+ const locator = ownValue(ref, 'locator')
48
+ if (typeof locator === 'string' ? !locator || locator.length > 16384 :
49
+ !locator || Object.keys(locator).sort().join(',') !== 'kind,value' || ['kind','value'].some(k => typeof ownValue(locator, k) !== 'string' || !ownValue(locator, k) || ownValue(locator, k).length > 16384)) return null
50
+ return { sourceId: ownValue(ref, 'sourceId'), revision: ownValue(ref, 'revision'),
51
+ representationId: ownValue(ref, 'representationId'), locator: typeof locator === 'string' ? locator :
52
+ { kind: ownValue(locator, 'kind'), value: ownValue(locator, 'value') } }
53
+ }
54
+
55
+ export async function composeEvidence(bundles, { eligible }) {
56
+ if (!Array.isArray(bundles) || typeof eligible !== 'function') throw new TypeError('Located bundles and actual current evidence owner required')
57
+ const combined = new Map(), unresolved = [], excluded = []
58
+ for (const bundle of bundles) {
59
+ if (!bundle?.provider || !Array.isArray(bundle.hits)) throw new TypeError('Each native bundle needs provider and hits')
60
+ // Explicit declined native citations survive receiving and prevent a coupled
61
+ // answer from appearing fully grounded merely because some hits mapped.
62
+ for (const field of ['unresolved', 'unresolvedReferences']) {
63
+ if (bundle[field] !== undefined && !Array.isArray(bundle[field])) throw new TypeError('Native receiving gaps must be explicit arrays')
64
+ for (const gap of bundle[field] ?? []) unresolved.push({ provider: bundle.provider, receivingGap: structuredClone(gap) })
65
+ }
66
+ for (const hit of bundle.hits) {
67
+ // Detach the exact inspected quote/reference before asynchronous receiving.
68
+ const nativeHit = structuredClone(hit), reference = answerReference(nativeHit?.evidenceRef), quote = nativeHit?.quote
69
+ if (!reference || typeof quote !== 'string' || !quote) { unresolved.push({ provider: bundle.provider, hit: nativeHit }); continue }
70
+ const identity = json([reference.sourceId, reference.revision, reference.representationId, reference.locator, quote])
71
+ const verdict = await eligible(structuredClone(reference), { quote, provider: bundle.provider, generation: bundle.generation })
72
+ if (verdict?.current !== true || verdict?.permitted !== true || verdict?.supported !== true) { excluded.push({ provider: bundle.provider, hit: nativeHit, reason: verdict?.reason ?? 'exact-quote-support-unreceived' }); continue }
73
+ if (!combined.has(identity)) combined.set(identity, { evidenceRef: reference, quote, observations: [] })
74
+ combined.get(identity).observations.push({ provider: bundle.provider, generation: bundle.generation ?? null,
75
+ nativeHit, score: nativeHit.score ?? null, scoreSemantics: nativeHit.scoreSemantics ?? 'unmapped-native-score' })
76
+ }
77
+ }
78
+ return { hits: [...combined.values()], unresolved, excluded,
79
+ providerArtifacts: bundles.map(x => ({ provider: x.provider, generation: x.generation ?? null, artifactRef: x.artifactRef ?? null })),
80
+ distinctSourceCount: new Set([...combined.values()].map(x => x.evidenceRef.sourceId)).size,
81
+ combinedScore: null, acceptance: 'existing-owner-view-only' }
82
+ }
83
+
84
+ // Only eligible located evidence reaches ordinary answer generation. Raw hits,
85
+ // artifacts, view content and native rejection strings stay in receipt custody.
86
+ function answerEvidence(evidence) {
87
+ return { hits: evidence.hits.map(x => ({ evidenceRef: answerReference(x.evidenceRef), quote: x.quote })),
88
+ distinctSourceCount: evidence.distinctSourceCount,
89
+ rejectionCounts: { unlocated: evidence.unresolved.length, ineligible: evidence.excluded.length } }
90
+ }
91
+
92
+ export class CompositionFailure extends Error {
93
+ constructor(cause, state) {
94
+ super(`Selected route needs owner reconciliation: ${cause.message}`, { cause })
95
+ this.name = 'CompositionFailure'
96
+ Object.assign(this, state)
97
+ }
98
+ }
99
+
100
+ /** Host bridges retain actual effect admission, view and evidence authority.
101
+ * No implicit retry or fallback occurs. Receipt custody survives late failures.
102
+ */
103
+ export function createComposition({ bindings, operationRoles, invoke, readKnowledgeView, eligible }) {
104
+ if (!operationRoles || typeof invoke !== 'function' || typeof readKnowledgeView !== 'function' || typeof eligible !== 'function') throw new TypeError('Actual operation roles, effect, knowledge-view and evidence owners required')
105
+ return Object.freeze({ async execute({ route, input, question, signal }) {
106
+ const compiled = compileRoute({ profile: route.profile, selected: route, bindings, operationRoles })
107
+ const receipts = [], retainedResults = [], dispatched = []
108
+ let failedStep = null, effectUncertain = false
109
+ const call = async (step, payload) => {
110
+ failedStep = step
111
+ if (signal?.aborted) throw new Error('Host cancelled selected route')
112
+ // Each owner receives an isolated payload; prior results remain under
113
+ // separate custody even when a later callable mutates its own input.
114
+ const invocationPayload = structuredClone(payload)
115
+ dispatched.push(step); effectUncertain = true
116
+ const returned = await invoke({ step, callable: compiledCallables.get(compiled).get(`${step.provider}/${step.operation}`), payload: invocationPayload, routeDigest: compiled.routeDigest, signal })
117
+ let result
118
+ try { result = structuredClone(returned) }
119
+ catch (snapshotError) {
120
+ // Returned data/costs remain in custody even when a snapshot cannot be
121
+ // made. Do not continue grounding or pretend this live value is frozen.
122
+ retainedResults.push({ step, result: returned, snapshotState: 'failed', snapshotError })
123
+ throw snapshotError
124
+ }
125
+ // Retain a detached snapshot even when its receipt is malformed.
126
+ retainedResults.push({ step, result, snapshotState: 'received' })
127
+ if (!result || !result.receiptRef) throw new Error('Selected operation must return its actual receipt reference')
128
+ receipts.push(structuredClone({ ...step, receiptRef: result.receiptRef, usage: result.usage ?? null }))
129
+ effectUncertain = false; failedStep = null
130
+ return structuredClone(result)
131
+ }
132
+ const counts = () => ({ selectedExtractorCount: dispatched.filter(x => x.role === 'extract').length,
133
+ selectedAnswererCount: dispatched.filter(x => ['answer', 'native-answer'].includes(x.role)).length })
134
+ try {
135
+ const extraction = compiled.extractor ? await call(compiled.extractor, { input }) : null
136
+ const assessments = []
137
+ for (const step of compiled.assessors) assessments.push(await call(step, { input, extraction }))
138
+ const view = structuredClone(await readKnowledgeView(structuredClone({ requestedView: compiled.view, input, extraction, assessments, routeDigest: compiled.routeDigest })))
139
+ if (!view?.ownerRef || view.kind !== compiled.view) throw new Error('Existing knowledge owner must return the selected view')
140
+ const projections = []
141
+ for (const step of compiled.projectors) projections.push(await call(step, { input, view }))
142
+ if (compiled.nativeAnswer) {
143
+ // Existing host must admit authorized current input before a coupled
144
+ // native model call. Final evidence validation is a separate check.
145
+ const native = await call(compiled.nativeAnswer, { input, question, view, projections })
146
+ if (!Array.isArray(native.hits)) throw new Error('Coupled native answer must return located evidence references')
147
+ const evidence = await composeEvidence([{ ...native, provider: compiled.nativeAnswer.provider }], { eligible })
148
+ const current = evidence.excluded.length === 0 && evidence.unresolved.length === 0 && evidence.hits.length > 0
149
+ return { routeDigest: compiled.routeDigest, extraction, assessments, view, projections, evidence,
150
+ answer: current ? native : null, withheldNativeResult: current ? null : native,
151
+ answerStatus: current ? 'current-grounding-received' : 'withheld-grounding-unreceived', receipts,
152
+ ...counts(), nativeAnswerGroundingCurrent: current,
153
+ canonicalMutation: false, semanticAcceptance: 'existing-owner-only', nativeQualification: false }
154
+ }
155
+ const bundles = []
156
+ for (const step of compiled.retrievers) {
157
+ const result = await call(step, { input, question, view, projections })
158
+ if (!Array.isArray(result.hits)) throw new Error('Retriever must return located evidence hits')
159
+ bundles.push({ ...result, provider: step.provider })
160
+ }
161
+ const evidence = await composeEvidence(bundles, { eligible })
162
+ const answer = compiled.answerer ? await call(compiled.answerer, { question, evidence: answerEvidence(evidence),
163
+ view: { kind: view.kind, ownerRef: view.ownerRef } }) : null
164
+ return { routeDigest: compiled.routeDigest, extraction, assessments, view, projections, evidence, answer, receipts,
165
+ ...counts(), canonicalMutation: false, semanticAcceptance: 'existing-owner-only', nativeQualification: false }
166
+ } catch (cause) {
167
+ throw new CompositionFailure(cause, { routeDigest: compiled.routeDigest, receipts, retainedResults, failedStep,
168
+ effectUncertain, ...counts(), retryAuthorized: false })
169
+ }
170
+ } })
171
+ }
@@ -0,0 +1,219 @@
1
+ import { createHash } from 'node:crypto'
2
+ import { performance } from 'node:perf_hooks'
3
+
4
+ // Private Q02 execution/assessment sidecar. Existing intake and knowledge owners
5
+ // retain all authoritative records; this module grants no execution permission.
6
+ export const PRESENCE_PROFILES = Object.freeze(['vanilla', 'graphify', 'lightrag', 'jev',
7
+ 'graphify+lightrag', 'graphify+jev', 'lightrag+jev', 'graphify+lightrag+jev'])
8
+ const digest = value => createHash('sha256').update(JSON.stringify(value)).digest('hex')
9
+ const phases = new Set(['cold', 'incremental', 'warm', 'recovery'])
10
+ const terminalStates = new Set(['completed', 'failed', 'unavailable', 'unsupported', 'not-run', 'uncertain'])
11
+ const numberOrNull = value => value === null || Number.isFinite(value) && value >= 0
12
+ const requiredPins = ['installedCandidate', 'corpusDigest', 'targetsDigest', 'routeDigest', 'questionOrderDigest']
13
+
14
+ export function validateExecutionPlan(plan) {
15
+ if (!plan || requiredPins.some(key => typeof plan[key] !== 'string' || !plan[key]) ||
16
+ !Array.isArray(plan.tasks) || !plan.tasks.length) throw new TypeError('Pinned installed target, frozen inputs and explicit task cohort required')
17
+ const ids = new Set()
18
+ for (const task of plan.tasks) {
19
+ if (!task?.id || ids.has(task.id) || !PRESENCE_PROFILES.includes(task.profile) ||
20
+ !phases.has(task.phase) || !task.routeId || !Number.isSafeInteger(task.repetition) || task.repetition < 1 ||
21
+ !task.questionId || !task.cacheState) throw new TypeError('Unique tasks require profile, phase, route, repetition, question and cache state')
22
+ ids.add(task.id)
23
+ }
24
+ return Object.freeze(structuredClone(plan))
25
+ }
26
+
27
+ // Host returns all attempted components, including failures and fallback. Unknown
28
+ // or omitted required charges remain unknown rather than becoming free work.
29
+ export function accountComponents(components) {
30
+ if (!Array.isArray(components)) throw new TypeError('Explicit accounting components required')
31
+ const currencies = new Set(), ids = new Set()
32
+ let knownSubtotal = 0, unknown = false
33
+ for (const item of components) {
34
+ if (!item?.id || ids.has(item.id) || !item.kind || !numberOrNull(item.cost) ||
35
+ !numberOrNull(item.elapsedMs) || typeof item.complete !== 'boolean') throw new TypeError('Unique valid attempted cost components required')
36
+ ids.add(item.id)
37
+ if (item.currency !== null && !/^[A-Z]{3}$/.test(item.currency ?? '')) throw new TypeError('Explicit currency or null required')
38
+ if (item.cost === null || !item.complete) unknown = true
39
+ else knownSubtotal += item.cost
40
+ if (item.cost !== null && item.cost !== 0 && item.currency === null) unknown = true
41
+ if (item.currency !== null) currencies.add(item.currency)
42
+ }
43
+ if (!components.length || currencies.size > 1) unknown = true
44
+ return { totalCost: unknown ? null : knownSubtotal, knownSubtotal,
45
+ currency: currencies.size === 1 ? [...currencies][0] : null,
46
+ complete: !unknown, attemptedComponents: components.length,
47
+ incompleteComponents: components.filter(x => x.cost === null || !x.complete).map(x => x.id),
48
+ unknownOrMixedCurrency: currencies.size > 1 || components.some(x => x.cost !== null && x.cost !== 0 && x.currency === null) }
49
+ }
50
+
51
+ // Separate existing assessor receipts are verified by a trusted receiving host.
52
+ // Producer-supplied assessment fields never enter the useful-answer numerator.
53
+ export function assessmentBinding(record) {
54
+ return { taskId: record.taskId, planDigest: record.planDigest, questionId: record.questionId,
55
+ routeId: record.routeId, resultDigest: record.resultDigest }
56
+ }
57
+ function assessmentReceived(record, receipt, verifyAssessment) {
58
+ if (record.outcome !== 'completed' || typeof verifyAssessment !== 'function' || !receipt) return false
59
+ const binding = assessmentBinding(record)
60
+ if (Object.values(binding).some(x => typeof x !== 'string' || !x) ||
61
+ !receipt.receiptRef || !receipt.assessorRef || !receipt.rubricRef || receipt.bindingDigest !== digest(binding)) return false
62
+ try {
63
+ const verified = verifyAssessment(structuredClone(receipt), structuredClone(binding))
64
+ if (typeof verified?.then === 'function') { Promise.resolve(verified).catch(() => {}); return false }
65
+ return verified?.verified === true && verified?.current === true && !!verified.ownerRef &&
66
+ verified.receiptRef === receipt.receiptRef && verified.bindingDigest === receipt.bindingDigest &&
67
+ receipt.usefulAccepted === true && receipt.supportAccepted === true
68
+ } catch { return false }
69
+ }
70
+
71
+ export function summarizeCohort({ assignedTaskIds, records, sharedComponents = [], assessmentReceipts = [], verifyAssessment }) {
72
+ if (!Array.isArray(assignedTaskIds) || !assignedTaskIds.length || new Set(assignedTaskIds).size !== assignedTaskIds.length ||
73
+ !Array.isArray(records) || !Array.isArray(assessmentReceipts)) throw new TypeError('Complete assigned denominator required')
74
+ const assigned = new Set(assignedTaskIds), observed = new Set(), assessmentTasks = new Set()
75
+ for (const record of records) {
76
+ if (!assigned.has(record.taskId) || observed.has(record.taskId) || !terminalStates.has(record.outcome)) throw new TypeError('Records must uniquely match the assigned cohort')
77
+ observed.add(record.taskId)
78
+ }
79
+ for (const receipt of assessmentReceipts) {
80
+ if (!assigned.has(receipt?.taskId) || assessmentTasks.has(receipt.taskId)) throw new TypeError('Separate assessor receipts must uniquely match assigned tasks')
81
+ assessmentTasks.add(receipt.taskId)
82
+ }
83
+ const useful = records.filter(record => assessmentReceived(record, assessmentReceipts.find(x => x.taskId === record.taskId), verifyAssessment))
84
+ const costs = [...sharedComponents, ...records.flatMap(x => x.components ?? [])]
85
+ let accounting
86
+ try { accounting = accountComponents(costs) }
87
+ catch (error) {
88
+ // Retain malformed/colliding accounting for reconciliation without losing
89
+ // already executed tasks or treating unverified charges as free work.
90
+ accounting = { totalCost: null, knownSubtotal: null, currency: null, complete: false,
91
+ attemptedComponents: costs.length, accountingIssue: error.message, retainedComponents: costs }
92
+ }
93
+ const missing = assignedTaskIds.filter(id => !observed.has(id))
94
+ const complete = !missing.length && records.every(x => Array.isArray(x.components) && x.components.length)
95
+ const totalCost = complete ? accounting.totalCost : null
96
+ return { assigned: assigned.size, observed: observed.size, missing,
97
+ usefulAccepted: useful.length, usefulAcceptedFraction: useful.length / assigned.size,
98
+ failedOrUnavailable: assigned.size - useful.length, accounting: { ...accounting, complete: complete && accounting.complete, totalCost },
99
+ costPerUsefulAcceptedAnswer: totalCost === null || !useful.length ? null : totalCost / useful.length,
100
+ elapsedObservations: records.map(x => ({ taskId: x.taskId, outcome: x.outcome, elapsedMs: x.elapsedMs ?? null })),
101
+ semanticQualityQualified: false, humanAuthorityVerified: false }
102
+ }
103
+
104
+ export function amortizedCosts({ ingestionComponents, queryComponents, queryCounts = [1, 10, 100] }) {
105
+ const initial = accountComponents(ingestionComponents), query = accountComponents(queryComponents)
106
+ return queryCounts.map(count => {
107
+ if (!Number.isSafeInteger(count) || count < 1) throw new TypeError('Positive predeclared query counts required')
108
+ const compatible = initial.currency === query.currency || initial.totalCost === 0 || query.totalCost === 0
109
+ return { queryCount: count, totalCost: initial.totalCost === null || query.totalCost === null || !compatible ? null : initial.totalCost + count * query.totalCost,
110
+ costPerQuery: initial.totalCost === null || query.totalCost === null || !compatible ? null : initial.totalCost / count + query.totalCost,
111
+ assumedWorkload: true, observedDemand: false }
112
+ })
113
+ }
114
+
115
+ /**
116
+ * Admission, native effects, resource bounds and cleanup belong to injected
117
+ * existing host owners. The host enforces per-call provider/CPU/deadline limits.
118
+ * No automatic retry/fallback, provider construction or rubric loading occurs.
119
+ */
120
+ export function createQualificationExecutor({ admit, openTrial, executeTask, closeTrial, appendSidecar,
121
+ receiveAssessment, verifyAssessment,
122
+ monotonic = () => performance.now(), now = () => new Date().toISOString() }) {
123
+ for (const fn of [admit, openTrial, executeTask, closeTrial, appendSidecar]) if (typeof fn !== 'function') throw new TypeError('Actual admission, execution, sidecar and cleanup owners required')
124
+ if ((receiveAssessment !== undefined || verifyAssessment !== undefined) &&
125
+ (typeof receiveAssessment !== 'function' || typeof verifyAssessment !== 'function')) throw new TypeError('Separate assessment receiving and receipt verification owners required together')
126
+ return Object.freeze({ async run(planInput, { signal } = {}) {
127
+ const plan = validateExecutionPlan(planInput), planDigest = digest(plan)
128
+ const admission = await admit({ plan: structuredClone(plan), planDigest, signal })
129
+ if (admission?.planDigest !== planDigest || admission?.executionAllowed !== true ||
130
+ !admission?.ownerRef || !admission?.limitsRef) throw new Error('Existing owner refused or did not bind actual execution admission')
131
+ const records = [], trials = new Map(), sharedComponents = [], terminalCleanup = [], assessmentReceipts = [], componentIds = new Set(), sidecarFailures = []
132
+ const retainComponents = components => {
133
+ accountComponents(components)
134
+ if (components.some(x => componentIds.has(x.id))) throw new Error('Duplicate attempted accounting component across the cohort')
135
+ for (const x of components) componentIds.add(x.id)
136
+ }
137
+ const unknownAttempt = (task, stage, attemptedResult, error) => {
138
+ let id = `${task.id}/${stage}/unreported-attempt`
139
+ while (componentIds.has(id)) id += '/unreported'
140
+ componentIds.add(id)
141
+ return { outcome: 'uncertain', effectStage: stage, attemptedResult,
142
+ reconciliationReason: error?.message ?? 'actual result unreceived',
143
+ components: [{ id, kind: 'unreported-native-attempt', cost: null, currency: null, elapsedMs: null, complete: false }] }
144
+ }
145
+ const save = async (task, result, started, startedAt) => {
146
+ const record = { outcome: result.outcome, components: result.components, effectStage: result.effectStage,
147
+ reconciliationReason: result.reconciliationReason, executionResult: result, resultDigest: null,
148
+ taskId: task.id, profile: task.profile, phase: task.phase, routeId: task.routeId, repetition: task.repetition,
149
+ questionId: task.questionId, cacheState: task.cacheState, planDigest, startedAt, endedAt: now(), elapsedMs: Math.max(0, monotonic() - started),
150
+ qualification: 'unscored-execution-sidecar', admissionOwnerRef: admission.ownerRef }
151
+ // Retain execution before invoking a separate assessor or sidecar sink.
152
+ records.push(record)
153
+ record.executionResult = structuredClone(result)
154
+ record.resultDigest = digest(record.executionResult)
155
+ if (record.outcome === 'completed' && receiveAssessment) {
156
+ try {
157
+ const receipt = await receiveAssessment({ binding: assessmentBinding(record), result: structuredClone(result), plan: structuredClone(plan), signal })
158
+ if (receipt) {
159
+ record.assessmentReceiving = structuredClone(receipt)
160
+ if (receipt.taskId === task.id && receipt.bindingDigest === digest(assessmentBinding(record)) &&
161
+ receipt.receiptRef && receipt.assessorRef && receipt.rubricRef) assessmentReceipts.push(structuredClone(receipt))
162
+ }
163
+ } catch (error) { record.assessmentReceivingError = error.message }
164
+ }
165
+ await appendSidecar(structuredClone(record))
166
+ }
167
+ let failure
168
+ try {
169
+ for (const task of plan.tasks) {
170
+ if (signal?.aborted) break
171
+ const trialKey = `${task.profile}/${task.routeId}/${task.phase}/${task.repetition}`
172
+ if (!trials.has(trialKey)) {
173
+ const started = monotonic(), startedAt = now(), entry = { handle: null, task: structuredClone(task) }
174
+ trials.set(trialKey, entry)
175
+ let opened
176
+ try {
177
+ opened = await openTrial({ task: structuredClone(task), trialKey, planDigest, admission, signal })
178
+ if (opened?.handle) entry.handle = opened.handle
179
+ if (!entry.handle || !Array.isArray(opened.components) || !opened.components.length) throw new Error('Trial owner must return an actual handle and explicit shared accounting')
180
+ retainComponents(opened.components)
181
+ sharedComponents.push(...opened.components)
182
+ } catch (error) {
183
+ await save(task, unknownAttempt(task, 'trial-open', opened ?? error.attemptedResult, error), started, startedAt)
184
+ break
185
+ }
186
+ }
187
+ const started = monotonic(), startedAt = now()
188
+ let result
189
+ try {
190
+ result = await executeTask({ task: structuredClone(task), trial: trials.get(trialKey).handle, planDigest, admission, signal })
191
+ if (!terminalStates.has(result?.outcome) || !Array.isArray(result.components) || !result.components.length) throw new Error('Incomplete host task receipt')
192
+ retainComponents(result.components)
193
+ } catch (error) {
194
+ result = unknownAttempt(task, 'task-execution', result ?? error.attemptedResult, error)
195
+ }
196
+ await save(task, result, started, startedAt)
197
+ if (result.outcome === 'uncertain') break
198
+ }
199
+ } catch (error) { failure = error }
200
+ finally {
201
+ for (const [trialKey, entry] of [...trials].reverse()) {
202
+ let cleanup
203
+ try { cleanup = await closeTrial({ trial: entry.handle, task: entry.task, trialKey, planDigest, admission }) }
204
+ catch (error) { cleanup = { verified: false, outcome: 'cleanup-uncertain', reason: error.message } }
205
+ terminalCleanup.push({ trialKey, ...cleanup })
206
+ }
207
+ try { await appendSidecar({ kind: 'trial-cleanup', planDigest, terminalCleanup, qualification: 'host-reported-not-independently-verified' }) }
208
+ catch (error) { sidecarFailures.push({ stage: 'trial-cleanup', error }); failure ??= error }
209
+ }
210
+ if (failure) {
211
+ // Original sidecar/cleanup failure retains all executed output and custody.
212
+ Object.assign(failure, { records, sharedComponents, assessmentReceipts, terminalCleanup, sidecarFailures, planDigest, retryAuthorized: false })
213
+ throw failure
214
+ }
215
+ return { planDigest, records, sharedComponents, assessmentReceipts, terminalCleanup,
216
+ cohort: summarizeCohort({ assignedTaskIds: plan.tasks.map(x => x.id), records, sharedComponents, assessmentReceipts, verifyAssessment }),
217
+ cleanupVerified: terminalCleanup.every(x => x.verified === true), nativeQualification: false, benefitQualified: false }
218
+ } })
219
+ }