@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,385 @@
1
+ // Explicit selection, inventory and restore for a personal workspace's private
2
+ // home. Composition decides eligibility; this module records a person's choice,
3
+ // lists what the home retains, and restores authored inputs from an earlier
4
+ // generation without ever widening enrollment or bindings. It never deletes
5
+ // authored inputs, generations or records; it removes only its own temporary
6
+ // files. Every failure is a PersonalWorkspaceRefusal with a stable code and no path.
7
+ import fs from 'node:fs'
8
+ import path from 'node:path'
9
+ import { createHash, randomUUID } from 'node:crypto'
10
+ import { composePersonalWorkspace, loadPersonalManifest, loadPersonalOverlay, PersonalWorkspaceRefusal } from './index.mjs'
11
+
12
+ const MANIFEST_FILE = 'atelier.personal.json'
13
+ const OVERLAY_FILE = 'atelier.overlay.json'
14
+ const SELECTION_SCHEMA = 'atelier-personal-workspace-selection@v1'
15
+ const RESTORE_SCHEMA = 'atelier-personal-workspace-restore@v1'
16
+ const GENESIS = 'genesis'
17
+ const MAX_RECORD = 1024 * 1024
18
+ // The generation module bounds every generation file at eight times its input limit.
19
+ const MAX_GENERATION_FILE = 8 * 1024 * 1024
20
+ const MAX_RESTORE_RECORD = 4 * MAX_RECORD
21
+ const ID = /^[0-9a-f]{64}$/
22
+ // Every temporary name this module writes, including restore aside copies
23
+ // (.restore-<uuid>-aside-<key>.json).
24
+ const TEMPORARY = /^\.(selection|restore|validate)-[0-9a-f-]{36}(-[a-z]+)*\.json$/
25
+ const ASIDE = (key) => new RegExp(`^\\.restore-[0-9a-f-]{36}-aside-${key}\\.json$`)
26
+ const hash = (bytes) => createHash('sha256').update(bytes).digest('hex')
27
+ const sortObject = (v) => Array.isArray(v) ? v.map(sortObject) : v && typeof v === 'object'
28
+ ? Object.fromEntries(Object.keys(v).sort().map((k) => [k, sortObject(v[k])])) : v
29
+ // The same canonical form the generation id is computed over.
30
+ const canonical = (v) => `${JSON.stringify(sortObject(v))}\n`
31
+ const refuse = (code, extra) => { throw Object.assign(new PersonalWorkspaceRefusal(code), extra ?? {}) }
32
+ const freeze = (v) => { if (v && typeof v === 'object') { Object.values(v).forEach(freeze); Object.freeze(v) } return v }
33
+ const restorePlans = new WeakSet()
34
+
35
+ // Maps any non-refusal failure to a stable code, so no raw error or path escapes.
36
+ function safe(fn, fallback) {
37
+ try { return fn() } catch (error) { if (error instanceof PersonalWorkspaceRefusal) throw error; refuse(fallback) }
38
+ }
39
+ function privateHome(personalHome) {
40
+ if (typeof personalHome !== 'string' || !path.isAbsolute(personalHome) || path.resolve(personalHome) !== personalHome) refuse('path-not-absolute')
41
+ if (process.platform === 'win32' || typeof process.getuid !== 'function') refuse('private-root-unverifiable')
42
+ let stat
43
+ try { stat = fs.lstatSync(personalHome) } catch { refuse('root-missing') }
44
+ if (!stat.isDirectory() || stat.isSymbolicLink() || fs.realpathSync(personalHome) !== personalHome) refuse('root-symlinked')
45
+ if (stat.uid !== process.getuid() || (stat.mode & 0o022) !== 0) refuse('not-private-location')
46
+ return personalHome
47
+ }
48
+ function readBytes(file, limit, tooLarge = 'malformed-input') {
49
+ const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW)
50
+ try {
51
+ const stat = fs.fstatSync(fd)
52
+ if (!stat.isFile()) refuse('malformed-input')
53
+ if (stat.size > limit) refuse(tooLarge)
54
+ const bytes = fs.readFileSync(fd)
55
+ if (bytes.length > limit) refuse(tooLarge)
56
+ return bytes
57
+ } finally { fs.closeSync(fd) }
58
+ }
59
+ const bytesOrNull = (file, limit) => { try { return readBytes(file, limit) } catch (error) { if (error.code === 'ENOENT') return null; throw error } }
60
+ function syncDir(dir) { const fd = fs.openSync(dir, 'r'); try { fs.fsyncSync(fd) } finally { fs.closeSync(fd) } }
61
+ function checkPrivateDir(dir) {
62
+ const stat = fs.lstatSync(dir)
63
+ if (!stat.isDirectory() || stat.isSymbolicLink() || stat.uid !== process.getuid() || (stat.mode & 0o022) !== 0) refuse('not-private-location')
64
+ return dir
65
+ }
66
+ function privateDir(personalHome, name, create) {
67
+ const dir = path.join(personalHome, name)
68
+ if (create) {
69
+ try { fs.mkdirSync(dir, { mode: 0o700 }); syncDir(personalHome) } catch (error) { if (error.code !== 'EEXIST') throw error }
70
+ } else {
71
+ try { fs.lstatSync(dir) } catch (error) { if (error.code === 'ENOENT') return null; throw error }
72
+ }
73
+ return checkPrivateDir(dir)
74
+ }
75
+ function writeExclusive(file, content) {
76
+ const fd = fs.openSync(file, 'wx', 0o600)
77
+ try { fs.writeFileSync(fd, content); fs.fsyncSync(fd) }
78
+ catch (error) { fs.closeSync(fd); fs.rmSync(file, { force: true }); throw error }
79
+ fs.closeSync(fd)
80
+ }
81
+ // Writes complete bytes to a temporary name, then publishes them under `final`
82
+ // with link, which never replaces an existing name. A crash leaves only a
83
+ // temporary file, which readers ignore and the inventory lists.
84
+ function publishExclusive(dir, final, content, kind) {
85
+ const temporary = path.join(dir, `.${kind}-${randomUUID()}.json`)
86
+ writeExclusive(temporary, content)
87
+ try { fs.linkSync(temporary, path.join(dir, final)) } catch (error) { fs.rmSync(temporary, { force: true }); throw error }
88
+ // Published. A temporary that cannot be removed now stays listed by the inventory,
89
+ // and a failed directory sync does not turn a published record into a failure.
90
+ try { fs.unlinkSync(temporary) } catch {}
91
+ try { syncDir(dir) } catch {}
92
+ }
93
+ // An inventory entry for a file that may be missing, oversized, not regular or
94
+ // unreadable: it is listed with its size and without a digest rather than
95
+ // failing the whole inventory.
96
+ function inventoryEntry(file, limit) {
97
+ try {
98
+ const bytes = readBytes(file, limit)
99
+ return { bytes: bytes.length, digest: hash(bytes) }
100
+ } catch (error) {
101
+ if (error.code === 'ENOENT') return null
102
+ let size = null
103
+ try { size = fs.lstatSync(file).size } catch {}
104
+ return { bytes: size, digest: null }
105
+ }
106
+ }
107
+ const eligibility = (personalHome, generationId) => {
108
+ try { composePersonalWorkspace({ personalHome, generationId }); return { eligible: true, reason: null } }
109
+ catch (error) { if (error instanceof PersonalWorkspaceRefusal) return { eligible: false, reason: error.code }; throw error }
110
+ }
111
+ // Validates exact bytes with the module's own loader, through a private temporary copy.
112
+ function validateBytes(personalHome, bytes, kind) {
113
+ const temporary = path.join(personalHome, `.validate-${randomUUID()}.json`)
114
+ writeExclusive(temporary, bytes)
115
+ try { return (kind === 'manifest' ? loadPersonalManifest : loadPersonalOverlay)(temporary, { personalHome }) }
116
+ finally { fs.unlinkSync(temporary) }
117
+ }
118
+
119
+ // The selection history is an append-only hash chain of numbered records. It
120
+ // proves internal consistency only: an edited record whose successors were not
121
+ // re-linked, a gap, or a reordering refuses. Truncating, or consistently rewriting,
122
+ // any trailing run of records (up to the whole history) yields another valid chain
123
+ // and is detectable only by comparing the head with one the host observed earlier.
124
+ function readHistory(personalHome) {
125
+ const dir = privateDir(personalHome, 'selections', false)
126
+ if (!dir) return { records: [], files: [], head: GENESIS }
127
+ const names = fs.readdirSync(dir).filter((name) => !name.startsWith('.')).sort()
128
+ const records = [], files = []
129
+ let head = GENESIS
130
+ for (const [index, name] of names.entries()) {
131
+ if (name !== `${String(index + 1).padStart(6, '0')}.json`) refuse('selection-history-corrupt')
132
+ let bytes
133
+ try { bytes = readBytes(path.join(dir, name), MAX_RECORD, 'selection-history-corrupt') } catch { refuse('selection-history-corrupt') }
134
+ let record
135
+ try { record = JSON.parse(bytes) } catch { refuse('selection-history-corrupt') }
136
+ if (record?.schema !== SELECTION_SCHEMA || record.sequence !== index + 1 || record.previous !== head || !ID.test(record.generationId ?? '') ||
137
+ canonical(record) !== bytes.toString('utf8')) refuse('selection-history-corrupt')
138
+ records.push(record)
139
+ head = hash(bytes)
140
+ files.push({ name, bytes: bytes.length, digest: head })
141
+ }
142
+ return { records, files, head }
143
+ }
144
+ export function selectionConfirmDigest(options) {
145
+ return safe(() => {
146
+ const { generationId, previous } = options ?? {}
147
+ if (typeof generationId !== 'string' || typeof previous !== 'string') refuse('malformed-input')
148
+ return `sha256:${hash(canonical({ action: 'select', generationId, previous }))}`
149
+ }, 'malformed-input')
150
+ }
151
+
152
+ // Records the person's explicit choice of the currently eligible generation.
153
+ // The caller first reads the current head and shows the person what it selects.
154
+ export function selectPersonalGeneration(options) {
155
+ return safe(() => {
156
+ const { personalHome, generationId, confirm } = options ?? {}
157
+ privateHome(personalHome)
158
+ if (typeof generationId !== 'string' || !ID.test(generationId)) refuse('malformed-input')
159
+ const { records, head } = readHistory(personalHome)
160
+ if (confirm !== selectionConfirmDigest({ generationId, previous: head })) refuse('confirmation-mismatch')
161
+ const { eligible, reason } = eligibility(personalHome, generationId)
162
+ if (!eligible) refuse(reason)
163
+ const dir = privateDir(personalHome, 'selections', true)
164
+ const record = { schema: SELECTION_SCHEMA, sequence: records.length + 1, previous: head, generationId }
165
+ const content = canonical(record)
166
+ try { publishExclusive(dir, `${String(record.sequence).padStart(6, '0')}.json`, content, 'selection') }
167
+ catch (error) { if (error.code === 'EEXIST') refuse('selection-concurrent'); throw error }
168
+ return freeze({ generationId, sequence: record.sequence, head: hash(content) })
169
+ }, 'selection-write-failed')
170
+ }
171
+
172
+ // The current selection and whether it is still eligible now. A selection never
173
+ // keeps a generation eligible after its inputs, roots or enrollment change.
174
+ export function readPersonalSelection(options) {
175
+ return safe(() => {
176
+ const { personalHome } = options ?? {}
177
+ privateHome(personalHome)
178
+ const { records, head } = readHistory(personalHome)
179
+ const current = records.at(-1) ?? null
180
+ if (!current) return freeze({ selected: null, head, eligible: false, reason: 'nothing-selected' })
181
+ return freeze({ selected: current.generationId, sequence: current.sequence, head, ...eligibility(personalHome, current.generationId) })
182
+ }, 'personal-home-unavailable')
183
+ }
184
+
185
+ // Lists entries without failing the whole inventory: a concurrently removed entry
186
+ // is skipped; an unreadable, foreign or oversized one is reported without a digest.
187
+ function listFiles(dir, keep) {
188
+ if (!dir) return []
189
+ const listed = []
190
+ for (const name of fs.readdirSync(dir).filter(keep).sort()) {
191
+ try {
192
+ const bytes = readBytes(path.join(dir, name), MAX_RESTORE_RECORD, 'record-too-large')
193
+ listed.push({ name, bytes: bytes.length, digest: hash(bytes) })
194
+ } catch (error) {
195
+ if (error.code === 'ENOENT') continue
196
+ let size = null
197
+ try { size = fs.lstatSync(path.join(dir, name)).size } catch {}
198
+ listed.push({ name, bytes: size, digest: null, unreadable: true })
199
+ }
200
+ }
201
+ return listed
202
+ }
203
+ // A read-only inventory of what this module writes in the private home (authored
204
+ // inputs, generations, staging, selection and restore records, and its temporary
205
+ // files), for a person to review before any deletion they choose to confirm. It
206
+ // writes and deletes nothing.
207
+ export function inventoryPersonalHome(options) {
208
+ return safe(() => {
209
+ const { personalHome } = options ?? {}
210
+ privateHome(personalHome)
211
+ const authored = [MANIFEST_FILE, OVERLAY_FILE].map((name) => {
212
+ const entry = inventoryEntry(path.join(personalHome, name), MAX_RECORD)
213
+ return { name, bytes: entry?.bytes ?? null, digest: entry?.digest ?? null }
214
+ })
215
+ const root = privateDir(personalHome, 'generations', false)
216
+ const entries = root ? fs.readdirSync(root).sort() : []
217
+ const generations = entries.filter((name) => ID.test(name)).map((generationId) => {
218
+ const record = inventoryEntry(path.join(root, generationId, 'generation.json'), MAX_GENERATION_FILE)
219
+ return { generationId, recordDigest: record?.digest ?? null, ...eligibility(personalHome, generationId) }
220
+ })
221
+ const staging = entries.filter((name) => name.startsWith('.staging-'))
222
+ const selectionsDir = privateDir(personalHome, 'selections', false)
223
+ const restoresDir = privateDir(personalHome, 'restores', false)
224
+ let history
225
+ try { history = readHistory(personalHome) } catch (error) {
226
+ if (!(error instanceof PersonalWorkspaceRefusal)) throw error
227
+ history = { files: listFiles(selectionsDir, (name) => !name.startsWith('.')), head: null, corrupt: true }
228
+ }
229
+ const { files: selections, head } = history
230
+ const restores = listFiles(restoresDir, (name) => !name.startsWith('.'))
231
+ const temporary = [
232
+ ...listFiles(personalHome, (name) => TEMPORARY.test(name)).map((file) => ({ ...file, location: '.' })),
233
+ ...listFiles(selectionsDir, (name) => TEMPORARY.test(name)).map((file) => ({ ...file, location: 'selections' })),
234
+ ...listFiles(restoresDir, (name) => TEMPORARY.test(name)).map((file) => ({ ...file, location: 'restores' })),
235
+ ]
236
+ return freeze({ authored, generations, staging, selections: { head, records: selections, corrupt: history.corrupt === true }, restores, temporary })
237
+ }, 'personal-home-unavailable')
238
+ }
239
+
240
+ // A generation's recorded inputs, verified against its own id: the id is the
241
+ // digest of the canonical inputs and this private home, so altered or moved
242
+ // history cannot be restored.
243
+ function recordedInputs(personalHome, generationId) {
244
+ if (typeof generationId !== 'string' || !ID.test(generationId)) refuse('malformed-input')
245
+ const root = privateDir(personalHome, 'generations', false)
246
+ if (!root) refuse('generation-missing')
247
+ let entry
248
+ try { entry = fs.lstatSync(path.join(root, generationId)) } catch { refuse('generation-missing') }
249
+ if (!entry.isDirectory() || entry.isSymbolicLink()) refuse('generation-corrupt')
250
+ let bytes
251
+ try { bytes = readBytes(path.join(root, generationId, 'inputs.json'), MAX_GENERATION_FILE, 'generation-inputs-too-large') }
252
+ catch (error) { if (error instanceof PersonalWorkspaceRefusal) throw error; refuse('generation-corrupt') }
253
+ if (hash(canonical({ inputsDigest: hash(bytes), personalHome })) !== generationId) refuse('generation-corrupt')
254
+ let inputs
255
+ try { inputs = JSON.parse(bytes) } catch { refuse('generation-corrupt') }
256
+ if (!inputs?.manifest || !inputs?.overlay || canonical(inputs) !== bytes.toString('utf8')) refuse('generation-corrupt')
257
+ return inputs
258
+ }
259
+ // The current authored file, read once: its exact bytes, digest and validated value.
260
+ function currentAuthored(personalHome, name, kind) {
261
+ const bytes = bytesOrNull(path.join(personalHome, name), MAX_RECORD)
262
+ if (bytes === null) {
263
+ // A restore interrupted between moving the file aside and linking its
264
+ // replacement leaves the name absent and the aside listed by the inventory.
265
+ if (fs.readdirSync(personalHome).some((entry) => ASIDE(kind).test(entry))) refuse('restore-interrupted')
266
+ refuse('malformed-input')
267
+ }
268
+ return { bytes, digest: hash(bytes), value: validateBytes(personalHome, bytes, kind) }
269
+ }
270
+ // A restore may narrow enrollment and bindings, never widen them: every
271
+ // repository the target enrolls must be enrolled now with the same root and
272
+ // remote, and every binding the target declares must be declared now.
273
+ function wideningCheck(target, current) {
274
+ const now = new Map(current.repos.filter((r) => r.enrolled).map((r) => [r.repoId, r]))
275
+ const readmitted = [], changed = []
276
+ for (const repo of target.repos.filter((r) => r.enrolled)) {
277
+ const present = now.get(repo.repoId)
278
+ if (!present) readmitted.push(repo.repoId)
279
+ else if (present.root !== repo.root || present.remote !== repo.remote) changed.push(repo.repoId)
280
+ }
281
+ if (readmitted.length) refuse('rollback-readmits-repository', { repoIds: Object.freeze(readmitted.sort()) })
282
+ if (changed.length) refuse('rollback-identity-changed', { repoIds: Object.freeze(changed.sort()) })
283
+ const added = target.bindings.filter((binding) => !current.bindings.includes(binding))
284
+ if (added.length) refuse('rollback-readds-binding', { count: added.length })
285
+ }
286
+ const summaryOf = (target, current, targets) => ({
287
+ enrollmentRemoved: current.repos.filter((r) => r.enrolled && !target.repos.some((t) => t.enrolled && t.repoId === r.repoId)).map((r) => r.repoId).sort(),
288
+ bindingsRemoved: current.bindings.filter((b) => !target.bindings.includes(b)).length,
289
+ manifestChanged: targets.manifestChanged,
290
+ overlayChanged: targets.overlayChanged,
291
+ })
292
+ export const restoreConfirmDigest = (plan) => `sha256:${hash(canonical({ action: 'restore', generationId: plan.generationId, from: plan.from, to: plan.to }))}`
293
+
294
+ // Plans restoring the authored manifest and overlay recorded by an earlier
295
+ // generation. The target files are validated now. Re-admission or a re-added
296
+ // binding is the person's fresh edit, never a restore's side effect. Restored
297
+ // files are written in the generation's canonical form (sorted keys, no
298
+ // indentation), not the person's original formatting.
299
+ export function planPersonalRestore(options) {
300
+ return safe(() => {
301
+ const { personalHome, generationId } = options ?? {}
302
+ privateHome(personalHome)
303
+ const inputs = recordedInputs(personalHome, generationId)
304
+ validateBytes(personalHome, canonical(inputs.manifest), 'manifest')
305
+ validateBytes(personalHome, canonical(inputs.overlay), 'overlay')
306
+ const manifest = currentAuthored(personalHome, MANIFEST_FILE, 'manifest')
307
+ const overlay = currentAuthored(personalHome, OVERLAY_FILE, 'overlay')
308
+ wideningCheck(inputs.manifest, manifest.value)
309
+ const to = { manifest: hash(canonical(inputs.manifest)), overlay: hash(canonical(inputs.overlay)) }
310
+ const from = { manifest: manifest.digest, overlay: overlay.digest }
311
+ const fields = { generationId, from, to }
312
+ const plan = freeze({ ...fields, summary: summaryOf(inputs.manifest, manifest.value, { manifestChanged: from.manifest !== to.manifest, overlayChanged: from.overlay !== to.overlay }),
313
+ confirm: restoreConfirmDigest(fields) })
314
+ restorePlans.add(plan)
315
+ return plan
316
+ }, 'personal-home-unavailable')
317
+ }
318
+
319
+ // Replaces `file` with `staged` only if `file` still holds exactly `expected`:
320
+ // the current file is moved aside, its bytes are verified, and the staged file
321
+ // is linked into place, which fails if another writer recreated the name. On any
322
+ // mismatch or failure to read the moved file, it is put back; when its name was
323
+ // taken meanwhile it stays as a listed temporary file. Writers that replace or
324
+ // recreate the file by path therefore keep their bytes. While the swap is in
325
+ // progress the name is briefly absent.
326
+ function compareAndSwap(personalHome, file, staged, expected, key) {
327
+ const aside = path.join(personalHome, `.restore-${randomUUID()}-aside-${key}.json`)
328
+ fs.renameSync(file, aside)
329
+ const putBack = () => {
330
+ try { fs.linkSync(aside, file) } catch { return false }
331
+ try { fs.unlinkSync(aside) } catch {}
332
+ try { syncDir(personalHome) } catch {}
333
+ return true
334
+ }
335
+ let moved
336
+ try { moved = readBytes(aside, MAX_RECORD) } catch { putBack(); refuse('authored-input-changed') }
337
+ if (hash(moved) !== expected) { putBack(); refuse('authored-input-changed') }
338
+ try { fs.linkSync(staged, file) } catch (error) {
339
+ if (error.code === 'EEXIST') refuse('authored-input-changed')
340
+ putBack(); throw error
341
+ }
342
+ fs.unlinkSync(staged)
343
+ fs.unlinkSync(aside)
344
+ syncDir(personalHome)
345
+ }
346
+
347
+ // Restores a plan from planPersonalRestore. The current authored bytes are read
348
+ // once, validated, required to match the plan and re-checked for widening; those
349
+ // exact bytes are kept in an append-only restore record first. Both restored
350
+ // files are staged and validated before either is replaced, then each is
351
+ // replaced by compare-and-swap. A rerun completes an interrupted restore.
352
+ export function restorePersonalInputs(plan, options) {
353
+ return safe(() => {
354
+ const { personalHome, confirm } = options ?? {}
355
+ privateHome(personalHome)
356
+ if (!restorePlans.has(plan) || confirm !== plan.confirm || confirm !== restoreConfirmDigest(plan)) refuse('confirmation-mismatch')
357
+ const inputs = recordedInputs(personalHome, plan.generationId)
358
+ const targets = { manifest: [MANIFEST_FILE, canonical(inputs.manifest)], overlay: [OVERLAY_FILE, canonical(inputs.overlay)] }
359
+ const current = {}
360
+ for (const [key, [name, content]] of Object.entries(targets)) {
361
+ if (hash(content) !== plan.to[key]) refuse('generation-corrupt')
362
+ current[key] = currentAuthored(personalHome, name, key)
363
+ if (current[key].digest !== plan.from[key] && current[key].digest !== plan.to[key]) refuse('authored-input-changed')
364
+ }
365
+ wideningCheck(inputs.manifest, current.manifest.value)
366
+ const dir = privateDir(personalHome, 'restores', true)
367
+ const record = canonical({ schema: RESTORE_SCHEMA, generationId: plan.generationId, from: plan.from, to: plan.to,
368
+ replaced: { manifest: current.manifest.bytes.toString('base64'), overlay: current.overlay.bytes.toString('base64') } })
369
+ publishExclusive(dir, `${Date.now()}-${randomUUID()}.json`, record, 'restore')
370
+ const pending = Object.entries(targets).filter(([key]) => current[key].digest !== plan.to[key])
371
+ const staged = []
372
+ try {
373
+ for (const [key, [, content]] of pending) {
374
+ const file = path.join(personalHome, `.restore-${randomUUID()}-${key}.json`)
375
+ writeExclusive(file, content)
376
+ staged.push([key, file])
377
+ ;(key === 'manifest' ? loadPersonalManifest : loadPersonalOverlay)(file, { personalHome })
378
+ }
379
+ for (const [key, file] of staged) compareAndSwap(personalHome, path.join(personalHome, targets[key][0]), file, current[key].digest, key)
380
+ } finally {
381
+ for (const [, file] of staged) fs.rmSync(file, { force: true })
382
+ }
383
+ return freeze({ generationId: plan.generationId, ...eligibility(personalHome, plan.generationId) })
384
+ }, 'restore-write-failed')
385
+ }
@@ -556,6 +556,14 @@ export function validateProjectConfigDoc(doc, { neutralTemplate = false } = {})
556
556
  if (setup.profile != null && !['single-repo', 'private-domain', 'shared-project', 'multi-repo', 'monorepo', 'control-workspace'].includes(setup.profile)) {
557
557
  errors.push('setup.profile is invalid')
558
558
  }
559
+ // The census reads these as path patterns relative to the project config's
560
+ // folder (docs/knowledge-graph.md), under the contract's pathString rules.
561
+ for (const key of ['include', 'exclude']) {
562
+ errors.push(...stringPathErrors(setup[key], `setup.${key}`))
563
+ if (typeof setup[key] === 'string' && (setup[key].includes('\u0000') || /^[a-z][a-z0-9+.-]*:/.test(setup[key]) || path.isAbsolute(setup[key]))) {
564
+ errors.push(`setup.${key} must be a path pattern relative to the project config folder`)
565
+ }
566
+ }
559
567
  if (alignment.appRepo != null && !firstString(alignment.appRepo)) errors.push('alignment.appRepo must be a non-empty string')
560
568
 
561
569
  const repoNames = new Set()
@@ -149,7 +149,9 @@ export function createVerifiedFileSequence({ directory, initial, apply, ignoreFi
149
149
  if (next.length < names.length) throw new Error('history shortened; preserve and inspect');
150
150
  const ids = next.map(n => identity(path.join(directory, n)));
151
151
  const reuse = names.every((n, i) => n === next[i] && identities[i] === ids[i]);
152
- let result = reuse ? value : initial();
152
+ // Replay into a copy: a refused read must not leave the verified value
153
+ // half-applied, or every later read of this reader would be refused.
154
+ let result = reuse ? structuredClone(value) : initial();
153
155
  for (let i = reuse ? names.length : 0; i < next.length; i++) {
154
156
  const file = path.join(directory, next[i]);
155
157
  result = apply(readRegularTextNoFollow(file), result, i + 1, next[i]);
@@ -188,7 +188,7 @@ export function classifyManagedPath(relativePath) {
188
188
  // moves up a level. Any other failure (a directory that cannot be searched,
189
189
  // a link loop) is thrown: a lexical path standing in for a real one would
190
190
  // weaken the alias check exactly where it matters.
191
- function realLocation(target, realpath) {
191
+ export function realLocation(target, realpath = fs.realpathSync.native) {
192
192
  const missing = []
193
193
  let current = path.resolve(target)
194
194
  for (;;) {
@@ -62,7 +62,7 @@ export function openRegularFileNoFollow(file, flags = fs.constants.O_RDONLY, mod
62
62
  // leaf before open and bind the opened descriptor back to that same file
63
63
  // identity where the filesystem supplies stable device/inode values.
64
64
  if (before && before.ino !== 0 && (before.dev !== opened.dev || before.ino !== opened.ino)) {
65
- throw new Error('state leaf changed while opening')
65
+ throw Object.assign(new Error('state leaf changed while opening'), { code: 'ELEAFCHANGED' })
66
66
  }
67
67
  return descriptor
68
68
  } catch (error) {
@@ -80,6 +80,15 @@ export function readRegularTextNoFollow(file) {
80
80
  }
81
81
  }
82
82
 
83
+ // The real path of a target as the file system stores it: on a volume that
84
+ // folds letter case (macOS by default) each component in the case it is stored
85
+ // in, which is also how the operating system reports a working directory, while
86
+ // Node's own resolution keeps the case it was given. On Windows Node's own
87
+ // resolution is kept: the native one also expands short 8.3 names.
88
+ export function realPathAsStored(target) {
89
+ return process.platform === 'win32' ? fs.realpathSync(target) : fs.realpathSync.native(target)
90
+ }
91
+
83
92
  export function syncPrivateDirectory(directory) {
84
93
  let fd;
85
94
  try { fd = fs.openSync(directory, fs.constants.O_RDONLY); fs.fsyncSync(fd); }