@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,174 @@
1
+ import { createHmac } from 'node:crypto'
2
+
3
+ // The channel between Atelier's own plugin, inside a vault Atelier manages,
4
+ // and the maintenance service of the workspace that manages that vault.
5
+ //
6
+ // A closed command surface over the service's literal loopback listener:
7
+ //
8
+ // POST /plugin/challenge a fresh nonce and the time it was made, and a hint
9
+ // at which vault's key it means that only the holder
10
+ // of that key can read; the service answers, once
11
+ // and only while it is fresh, with its own nonce and
12
+ // a proof that it holds the key
13
+ // POST /plugin/hello only after the plugin checked that proof: its own
14
+ // proof, the plugin and app versions, an id of this
15
+ // launch of the plugin, and a proof of the vault path
16
+ // the app has open (the path itself is never sent)
17
+ // POST /plugin/lease "this vault is open in an app", renewed every two
18
+ // seconds; it lapses on its own six seconds later
19
+ // POST /plugin/release the vault closed, or the plugin unloaded
20
+ // POST /plugin/status freshness, reason and held edits of this view
21
+ //
22
+ // The key is the vault's bearer, which Atelier writes into the plugin's data
23
+ // file of that vault only and keeps in private state. It never crosses the
24
+ // wire, and neither does anything that names the view or the vault before the
25
+ // listener proved it holds the key: a program squatting the service's port
26
+ // while the service is down learns a nonce and a hint it cannot link to a
27
+ // vault, and cannot answer. Both proofs bind the listener's exact address, so
28
+ // an answer relayed from a service listening elsewhere does not verify.
29
+ //
30
+ // A challenge recorded by such a program and sent to the service later is
31
+ // worth little: the service answers a challenge only within thirty seconds of
32
+ // the time it names, and a nonce only once, and at most four handshakes wait
33
+ // for their hello per view, so one view's challenges never hold up another's.
34
+ //
35
+ // The handshake derives a key for one session. Every later request carries
36
+ // the session, a counter that only goes up, and a MAC over both and the
37
+ // command under that key; every answer carries a MAC over the request's counter
38
+ // and the exact bytes of the answer. A handshake is used once and lapses after
39
+ // five seconds, a session after fifteen minutes. Nothing here takes a path to
40
+ // act on, a command or code, and every command maps to state the service
41
+ // already keeps (see docs/obsidian-plugin.md).
42
+ //
43
+ // The plugin cannot import this module: it runs inside the app, from the
44
+ // vault. It carries the same constants and the same computations, and the test
45
+ // suite holds the two to each other: a plugin that computes any one of them
46
+ // otherwise cannot complete a handshake.
47
+
48
+ export const PLUGIN_ID = 'atelier-projection'
49
+ export const PLUGIN_DIRECTORY = `.obsidian/plugins/${PLUGIN_ID}`
50
+ // Shipped with the package under plugins/obsidian/ and copied into every vault unchanged.
51
+ export const PLUGIN_SOURCE_FILES = Object.freeze(['manifest.json', 'main.js', 'styles.css'])
52
+ // Written per vault: the channel address and that vault's bearer.
53
+ export const PLUGIN_DATA_FILE = 'data.json'
54
+ export const PLUGIN_DATA_PATH = `${PLUGIN_DIRECTORY}/${PLUGIN_DATA_FILE}`
55
+ // The data file holds the vault's key: only the person who runs the vault reads it. The app reads the others.
56
+ export const PLUGIN_DATA_MODE = 0o600
57
+ export const PLUGIN_SOURCE_MODE = 0o644
58
+
59
+ export const PLUGIN_CHANNEL_PROTOCOL = 'atelier-obsidian-plugin-channel/v2'
60
+ export const PLUGIN_DATA_SCHEMA = 'atelier-obsidian-plugin-data/v1'
61
+ export const PLUGIN_STATUS_SCHEMA = 'atelier-obsidian-plugin-status/v1'
62
+
63
+ export const PLUGIN_COMMANDS = Object.freeze(['challenge', 'hello', 'lease', 'release', 'status'])
64
+ export const PLUGIN_ROUTES = Object.freeze(Object.fromEntries(PLUGIN_COMMANDS.map((command) => [command, `/plugin/${command}`])))
65
+ export const PLUGIN_REQUEST_FIELDS = Object.freeze({
66
+ challenge: Object.freeze(['protocol', 'keyHint', 'clientNonce', 'issuedAt']),
67
+ hello: Object.freeze(['handshakeId', 'pluginVersion', 'appVersion', 'instanceId', 'vaultProof', 'clientProof']),
68
+ lease: Object.freeze(['sessionId', 'counter', 'mac']),
69
+ release: Object.freeze(['sessionId', 'counter', 'mac']),
70
+ status: Object.freeze(['sessionId', 'counter', 'mac']),
71
+ })
72
+
73
+ export const PLUGIN_MAX_REQUEST_BYTES = 1024
74
+ export const PLUGIN_RENEW_INTERVAL_MS = 2000
75
+ // Three renewals may be missed before a vault stops counting as open.
76
+ export const PLUGIN_LEASE_TTL_MS = 3 * PLUGIN_RENEW_INTERVAL_MS
77
+ export const PLUGIN_MAX_SESSIONS_PER_SCOPE = 8
78
+ export const PLUGIN_HANDSHAKE_TTL_MS = 5000
79
+ // A challenge is answered only this close to the time it names (both sides read one machine's clock).
80
+ export const PLUGIN_CHALLENGE_WINDOW_MS = 30 * 1000
81
+ export const PLUGIN_MAX_PENDING_HANDSHAKES_PER_SCOPE = 4
82
+ // A session key is short-lived: the plugin shakes hands again after this long.
83
+ export const PLUGIN_MAX_SESSION_AGE_MS = 15 * 60 * 1000
84
+ // The app version the plugin needs, the floor of the publication protocol (app-capability.mjs).
85
+ export const PLUGIN_MINIMUM_APP_VERSION = '1.13.7'
86
+
87
+ export const PLUGIN_BEARER = /^[A-Za-z0-9_-]{43}$/
88
+ export const PLUGIN_NONCE = /^[0-9a-f]{64}$/
89
+ // A key hint, a proof and a MAC are each one HMAC-SHA256, in hex.
90
+ export const PLUGIN_MAC = /^[0-9a-f]{64}$/
91
+ export const PLUGIN_HANDSHAKE_ID = /^ph-[0-9a-f]{32}$/
92
+ export const PLUGIN_SESSION_ID = /^ps-[0-9a-f]{32}$/
93
+ export const PLUGIN_INSTANCE_ID = /^pi-[0-9a-f]{32}$/
94
+ const IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/
95
+ const VERSION_TEXT = /^[0-9A-Za-z.+-]{1,40}$/
96
+ const LOOPBACK_HOSTS = Object.freeze(['127.0.0.1', '::1'])
97
+
98
+ const isPlainObject = (value) => value !== null && typeof value === 'object' && !Array.isArray(value)
99
+
100
+ // The route of a request path, or null. Exact paths only: no query, no prefix.
101
+ export function pluginCommandOf(requestPath) {
102
+ return PLUGIN_COMMANDS.find((command) => PLUGIN_ROUTES[command] === requestPath) ?? null
103
+ }
104
+
105
+ // { ok: true, body } or { ok: false, code }. The body has exactly the fields
106
+ // of its command, each of its own shape; anything else refuses.
107
+ export function validatePluginRequest(command, body) {
108
+ const fields = PLUGIN_REQUEST_FIELDS[command]
109
+ if (fields === undefined) return { ok: false, code: 'unknown-command' }
110
+ if (!isPlainObject(body)) return { ok: false, code: 'request-malformed' }
111
+ const keys = Object.keys(body)
112
+ if (keys.length !== fields.length || !fields.every((field) => Object.hasOwn(body, field))) return { ok: false, code: 'request-malformed' }
113
+ const shaped = (field, pattern) => typeof body[field] === 'string' && pattern.test(body[field])
114
+ if (command === 'challenge') {
115
+ if (body.protocol !== PLUGIN_CHANNEL_PROTOCOL) return { ok: false, code: 'protocol-unsupported' }
116
+ if (!shaped('keyHint', PLUGIN_MAC) || !shaped('clientNonce', PLUGIN_NONCE) || !Number.isSafeInteger(body.issuedAt) || body.issuedAt < 1) return { ok: false, code: 'request-malformed' }
117
+ } else if (command === 'hello') {
118
+ if (!shaped('handshakeId', PLUGIN_HANDSHAKE_ID) || !shaped('instanceId', PLUGIN_INSTANCE_ID) || !shaped('vaultProof', PLUGIN_MAC) || !shaped('clientProof', PLUGIN_MAC)) return { ok: false, code: 'request-malformed' }
119
+ if (!shaped('pluginVersion', VERSION_TEXT) || !shaped('appVersion', VERSION_TEXT)) return { ok: false, code: 'request-malformed' }
120
+ } else if (!shaped('sessionId', PLUGIN_SESSION_ID) || !shaped('mac', PLUGIN_MAC) || !Number.isSafeInteger(body.counter) || body.counter < 1) {
121
+ return { ok: false, code: 'request-malformed' }
122
+ }
123
+ return { ok: true, body }
124
+ }
125
+
126
+ // The computations of the handshake and the session, shared with the plugin.
127
+ // Every input but a vault path is one of the shapes above, without a line
128
+ // break, and a vault path is the only input of its computation, so joining
129
+ // them by lines is unambiguous. `key` is the vault's bearer text, or a
130
+ // session key.
131
+ const transcript = (label, fields) => [PLUGIN_CHANNEL_PROTOCOL, label, ...fields.map(String)].join('\n')
132
+ const hmac = (key, label, fields) => createHmac('sha256', key).update(transcript(label, fields), 'utf8').digest()
133
+ const hmacHex = (key, label, fields) => hmac(key, label, fields).toString('hex')
134
+
135
+ // A hint at the vault's key, fresh with every nonce and bound to the time the
136
+ // challenge names: the service finds the key it names by computing it for each
137
+ // key it holds, and a listener that holds none cannot tell two hints of one
138
+ // vault apart, nor move one to another time.
139
+ export const pluginKeyHint = ({ bearer, clientNonce, issuedAt }) => hmacHex(bearer, 'key-hint', [clientNonce, issuedAt])
140
+ // What binds a handshake: the view, the listener's exact address, both nonces and the handshake.
141
+ const handshakeFields = ({ scopeId, authority, clientNonce, serverNonce, handshakeId }) => [scopeId, authority, clientNonce, serverNonce, handshakeId]
142
+ export const pluginServerProof = (input) => hmacHex(input.bearer, 'server-proof', handshakeFields(input))
143
+ export const pluginSessionKey = (input) => hmac(input.bearer, 'session-key', handshakeFields(input))
144
+ // The vault the app has open, proven under the session key rather than named.
145
+ export const pluginVaultProof = ({ sessionKey, vaultPath }) => hmacHex(sessionKey, 'vault', [vaultPath])
146
+ export const pluginClientProof = (input) => hmacHex(input.bearer, 'client-proof', [...handshakeFields(input), input.pluginVersion, input.appVersion, input.instanceId, input.vaultProof])
147
+ export const pluginRequestMac = ({ sessionKey, command, sessionId, counter }) => hmacHex(sessionKey, 'request', [command, sessionId, counter])
148
+ // An answer is the exact text of its JSON document and a MAC over it, the command and the request's counter.
149
+ export const pluginResponseMac = ({ sessionKey, command, sessionId, counter, payload }) => hmacHex(sessionKey, 'response', [command, sessionId, counter, payload])
150
+
151
+ // The plugin's data file of one vault: where the service listens and the
152
+ // bearer of this vault. Canonical bytes, so an unchanged channel republishes
153
+ // nothing.
154
+ export function pluginDataDocument({ host, port, scopeId, bearer }) {
155
+ const document = { schema: PLUGIN_DATA_SCHEMA, channel: { host, port }, scopeId, bearer }
156
+ if (validatePluginData(document) === null) throw new TypeError('the plugin data needs a literal loopback address, a port, a view and a bearer')
157
+ return document
158
+ }
159
+
160
+ export function pluginDataBytes(input) {
161
+ const { schema, channel, scopeId, bearer } = pluginDataDocument(input)
162
+ return Buffer.from(`${JSON.stringify({ schema, channel: { host: channel.host, port: channel.port }, scopeId, bearer }, null, 2)}\n`, 'utf8')
163
+ }
164
+
165
+ // The document when it is exactly a plugin data document, else null.
166
+ export function validatePluginData(document) {
167
+ if (!isPlainObject(document) || Object.keys(document).sort().join(',') !== 'bearer,channel,schema,scopeId') return null
168
+ if (document.schema !== PLUGIN_DATA_SCHEMA || typeof document.scopeId !== 'string' || !IDENTIFIER.test(document.scopeId)) return null
169
+ if (typeof document.bearer !== 'string' || !PLUGIN_BEARER.test(document.bearer)) return null
170
+ const { channel } = document
171
+ if (!isPlainObject(channel) || Object.keys(channel).sort().join(',') !== 'host,port') return null
172
+ if (!LOOPBACK_HOSTS.includes(channel.host) || !Number.isInteger(channel.port) || channel.port < 1024 || channel.port > 65535) return null
173
+ return document
174
+ }
@@ -0,0 +1,10 @@
1
+ // Atelier's own Obsidian plugin: the channel it speaks and the files that put
2
+ // it into a vault. The plugin source ships under plugins/obsidian/.
3
+ export {
4
+ PLUGIN_BEARER, PLUGIN_CHALLENGE_WINDOW_MS, PLUGIN_CHANNEL_PROTOCOL, PLUGIN_COMMANDS, PLUGIN_DATA_FILE, PLUGIN_DATA_PATH, PLUGIN_DATA_SCHEMA, PLUGIN_DIRECTORY, PLUGIN_HANDSHAKE_ID, PLUGIN_HANDSHAKE_TTL_MS,
5
+ PLUGIN_ID, PLUGIN_INSTANCE_ID, PLUGIN_LEASE_TTL_MS, PLUGIN_MAC, PLUGIN_MAX_PENDING_HANDSHAKES_PER_SCOPE, PLUGIN_MAX_REQUEST_BYTES, PLUGIN_MAX_SESSION_AGE_MS, PLUGIN_MAX_SESSIONS_PER_SCOPE,
6
+ PLUGIN_MINIMUM_APP_VERSION, PLUGIN_NONCE, PLUGIN_RENEW_INTERVAL_MS, PLUGIN_REQUEST_FIELDS, PLUGIN_ROUTES, PLUGIN_SESSION_ID, PLUGIN_SOURCE_FILES,
7
+ PLUGIN_STATUS_SCHEMA, pluginClientProof, pluginCommandOf, pluginDataBytes, pluginDataDocument, pluginKeyHint, pluginRequestMac, pluginResponseMac, pluginServerProof, pluginSessionKey,
8
+ pluginVaultProof, validatePluginData, validatePluginRequest,
9
+ } from './channel.mjs'
10
+ export { PLUGIN_DATA_MODE, PLUGIN_SOURCE_MODE, PLUGIN_SOURCE_ROOT, preparePluginFiles, readPluginSource } from './bundle.mjs'
@@ -6,7 +6,7 @@ import { AtelierDiagnosticError } from '../../../project/config.mjs'
6
6
  import { compareText, isoTime } from '../../../runtime/obsidian/documents.mjs'
7
7
  import { ObsidianMaintenanceRefusal } from '../../../runtime/obsidian/errors.mjs'
8
8
  import { readMachineSettings } from '../../../runtime/obsidian/machine-settings.mjs'
9
- import { DEFAULT_ELIGIBILITY, createProductionSeams } from '../../../runtime/obsidian/pipeline.mjs'
9
+ import { createProductionSeams, eligibilityFor } from '../../../runtime/obsidian/pipeline.mjs'
10
10
  import { acquirePrivateGenerationLock, createAbandonmentProof } from '../../../runtime/obsidian/private-lock.mjs'
11
11
  import { OBSIDIAN_EXT_KEY, ObsidianContractRefusal, assertObsidianContract } from '../contracts.mjs'
12
12
  import { SOURCE_APPLY_PRIMITIVES } from '../edits/apply.mjs'
@@ -15,6 +15,7 @@ import { isIdentifier } from '../edits/object-identity.mjs'
15
15
  import { openObjectStore } from '../edits/object-store.mjs'
16
16
  import { decideApply } from '../edits/policy.mjs'
17
17
  import { applyEditLens } from '../edits/regions.mjs'
18
+ import { PERSONAL_VALIDATION_PENDING, personalOverlayRepoOf } from '../personal-workspace.mjs'
18
19
  import { PublicationRefusal } from '../recovery/store.mjs'
19
20
  import {
20
21
  PROPOSAL_BACKPRESSURE, PROPOSAL_LEDGER_LIMITS, classifyLedgerRead, classifyStoreRefusal, estimateEventLineBytes, isDue, isExhausted, nextAttemptAt, preflightAppend,
@@ -168,7 +169,7 @@ export function createProposalAdapterForOracleTests(primitives = PROPOSAL_ADAPTE
168
169
  // two questions asked of the machine; `proveAbandoned` is the proof a held lock is passed over with.
169
170
  return function createProposalAdapter(options = {}) {
170
171
  const {
171
- crash = () => {}, bounds: boundsInput = {}, limits = PROPOSAL_LEDGER_LIMITS, eligibility = DEFAULT_ELIGIBILITY,
172
+ crash = () => {}, bounds: boundsInput = {}, limits = PROPOSAL_LEDGER_LIMITS, eligibility: fixedEligibility = null,
172
173
  openStore = createProposalStore, objectStore = openObjectStore, isGitIgnored = SOURCE_APPLY_PRIMITIVES.isGitIgnored, proveAbandoned = createAbandonmentProof(), queueCrash = () => {},
173
174
  } = options
174
175
  const bounds = { ...PROPOSAL_BACKPRESSURE, ...boundsInput }
@@ -184,6 +185,9 @@ export function createProposalAdapterForOracleTests(primitives = PROPOSAL_ADAPTE
184
185
  let observation = { key: null, run: null }
185
186
  // Edits whose operation is known not to be a proposal. A cache: losing it costs one read of an object.
186
187
  const notProposals = new Set()
188
+ // Set when an observation found the personal workspace's graph pending: the edits it could not look at are offered
189
+ // again on the next observation, not parked until a full reconciliation.
190
+ let observePendingAgain = false
187
191
 
188
192
  // -- the workspace of one call -------------------------------------------
189
193
 
@@ -198,12 +202,31 @@ export function createProposalAdapterForOracleTests(primitives = PROPOSAL_ADAPTE
198
202
  if (!stores.has(scopeId)) stores.set(scopeId, seams.createRecoveryStore({ workspaceRoot, workspaceId, scopeId, repositoryRoots }))
199
203
  return stores.get(scopeId)
200
204
  }
201
- // The canonical graph as it is now and the corpus profile of this machine, built once per call. Throws typed.
205
+ // The canonical graph as it is now and the corpus profile of this machine, built once per call with the notes the
206
+ // machine settings let into a view, as the engine builds it (eligibilityFor). Throws typed.
202
207
  let corpus = null
203
- const currentCorpus = () => (corpus ??= {
204
- graph: seams.buildGraph({ project, eligibility }),
205
- profile: seams.profileFor({ project, workspaceId, audienceAllow: readMachineSettings({ workspaceRoot, workspaceId })?.audienceAllow ?? [] }),
206
- })
208
+ // A pending answer is kept for the call too, so a call that looks at several edits builds at most once.
209
+ let pendingCorpus = null
210
+ const currentCorpus = () => {
211
+ if (corpus !== null) return corpus
212
+ if (pendingCorpus !== null) throw pendingCorpus
213
+ const machine = readMachineSettings({ workspaceRoot, workspaceId })
214
+ corpus = {
215
+ // The engine hands its deferral over: a personal workspace is then never composed on its event loop here.
216
+ graph: (() => {
217
+ try { return seams.buildGraph({ project, eligibility: fixedEligibility ?? eligibilityFor({ machine, project }), deferPersonalValidation: context.deferPersonalValidation === true }) } catch (error) {
218
+ if (error?.code === PERSONAL_VALIDATION_PENDING) pendingCorpus = error
219
+ throw error
220
+ }
221
+ })(),
222
+ profile: seams.profileFor({ project, workspaceId, audienceAllow: machine?.audienceAllow ?? [] }),
223
+ }
224
+ return corpus
225
+ }
226
+ // Whether the personal workspace's graph is pending on this call. Asked only where a graph would be built anyway.
227
+ const personalPending = () => {
228
+ try { currentCorpus(); return false } catch (error) { if (error?.code === PERSONAL_VALIDATION_PENDING) return true; if (isTyped(error)) return false; throw error }
229
+ }
207
230
  // true, false, or null when the canonical graph cannot be read now. The rule is the one source apply asks.
208
231
  const visible = options.isVisible ? (identity) => options.isVisible(identity, context) : ({ repoId, nodeId }) => {
209
232
  let built
@@ -223,6 +246,7 @@ export function createProposalAdapterForOracleTests(primitives = PROPOSAL_ADAPTE
223
246
  return {
224
247
  ...context, recoveryOf, visible, clock, env: env ?? context.env ?? process.env,
225
248
  corpus: currentCorpus,
249
+ personalPending,
226
250
  objects: () => (objects ??= objectStore({ stateRoot: workspaceRoot, workspaceId, repositoryRoots, clock })),
227
251
  queue,
228
252
  managedRoots: () => [workspaceRoot, ...[...stores.values()].map((store) => store.vaultRoot)],
@@ -304,6 +328,8 @@ export function createProposalAdapterForOracleTests(primitives = PROPOSAL_ADAPTE
304
328
  if (requireOpenEdit && editState !== 'open') return known ? refused(`edit-${editState}`) : outcome('skipped', null, { code: `edit-${editState}` })
305
329
  // The object store no longer says this is a proposed structural edit, so there is nothing to describe.
306
330
  if (item.observed === null) return refused('operation-not-proposed')
331
+ // A personal workspace whose graph is pending leaves the operation as it is: no attempt is spent on this tick.
332
+ if (workspace.deferPersonalValidation === true && personalOverlayRepoOf(workspace.project) !== null && workspace.personalPending()) return outcome('deferred', history?.head ?? null, { code: PERSONAL_VALIDATION_PENDING })
307
333
  const resolved = resolveProposalRoute({ project: workspace.project, workspaceId: workspace.workspaceId, identity: item, sourcePath: item.sourcePath, managedRoots: workspace.managedRoots(), isVisible: workspace.visible, isGitIgnored, env: env ?? workspace.env ?? process.env })
308
334
  if (!resolved.ok && !resolved.transient) return refused(resolved.code)
309
335
  // Recorded before anything is asked of the store.
@@ -432,7 +458,20 @@ export function createProposalAdapterForOracleTests(primitives = PROPOSAL_ADAPTE
432
458
  async observe(context, { retryRefused = false } = {}) {
433
459
  const workspace = open(context)
434
460
  const edits = Array.isArray(context.edits) ? context.edits : []
435
- return { adapterId: PROPOSAL_ADAPTER_ID, observed: observation.run(workspace, { edits, retryRefused }) }
461
+ // A private note of a personal workspace lives in a generation the personal-workspace module owns: its edit is
462
+ // not observed, recorded or proposed, and stays held in the vault. Answered by name, with nothing written.
463
+ const overlayRepoId = personalOverlayRepoOf(workspace.project)
464
+ const isPrivate = (edit) => overlayRepoId !== null && edit?.identity?.repoId === overlayRepoId
465
+ const privateNotes = edits.filter((edit) => isPrivate(edit) && edit.closedAt === null)
466
+ .map((edit) => ({ editId: edit.editId, repoId: edit.identity.repoId, nodeId: edit.identity.nodeId, status: 'refused', code: 'personal-overlay-not-proposed' }))
467
+ const shared = edits.filter((edit) => !isPrivate(edit))
468
+ // The graph is built only when an edit is looked at, as for any project. When the personal workspace's graph is
469
+ // pending, the edits it reached are not reported or recorded, and are offered again on the next observation.
470
+ const retry = retryRefused || observePendingAgain
471
+ const looked = observation.run(workspace, { edits: shared, retryRefused: retry })
472
+ const pending = looked.filter((item) => item.code === PERSONAL_VALIDATION_PENDING)
473
+ observePendingAgain = pending.length > 0
474
+ return { adapterId: PROPOSAL_ADAPTER_ID, observed: [...privateNotes, ...looked.filter((item) => item.code !== PERSONAL_VALIDATION_PENDING)], ...(pending.length > 0 ? { deferred: PERSONAL_VALIDATION_PENDING } : {}) }
436
475
  },
437
476
 
438
477
  // The tick. `context` is { project, workspaceRoot, workspaceId, repositoryRoots, edits }: the pending edit
@@ -5,7 +5,7 @@ import { compareText, isoTime } from '../../../runtime/obsidian/documents.mjs'
5
5
  import { readObsidianEnablement } from '../../../runtime/obsidian/enablement.mjs'
6
6
  import { ObsidianMaintenanceRefusal } from '../../../runtime/obsidian/errors.mjs'
7
7
  import { OPEN_EDIT_STATES, createMaintenanceStateStore } from '../../../runtime/obsidian/state-store.mjs'
8
- import { OBSIDIAN_EXT_KEY, ObsidianContractRefusal } from '../contracts.mjs'
8
+ import { OBSIDIAN_EXT_KEY, ObsidianContractRefusal, manifestLayoutVersion } from '../contracts.mjs'
9
9
  import { SOURCE_APPLY_PRIMITIVES, SourceApplyRefusal, locateSource } from '../edits/apply.mjs'
10
10
  import { EditArbitrationRefusal } from '../edits/arbitrate.mjs'
11
11
  import { createEditObserverForOracleTests, editIdempotencyKey, observeEdit } from '../edits/observe.mjs'
@@ -126,22 +126,35 @@ export function createTickObservation(options = {}) {
126
126
  // The note exactly as it was published, which the lens needs to tell generated bytes from authored ones. The
127
127
  // publisher keeps no copy of a note it created, so the note is prepared again from the sources as they are now and
128
128
  // used only when it has the digest the manifest recorded; a retained object of that digest serves as well.
129
+ //
130
+ // A source replaced between the look at its path and its open (ELEAFCHANGED) is another program writing it, as it
131
+ // is to the publisher and to source apply: the preparation answers null, and the caller reads the source again and
132
+ // refuses stale-source when it changed. Only a preparation that succeeded is kept for its scope and generation: one
133
+ // failure tells of one moment and is never the answer for the other edits of that generation in the tick.
129
134
  function publishedNoteOf(workspace, { scope, manifest, noteEntry }, now) {
130
135
  const store = workspace.recoveryOf(scope.scopeId)
131
136
  const kept = retained(store, noteEntry.noteDigest)
132
137
  if (kept) return kept
133
138
  workspace.prepared ??= new Map()
134
- if (!workspace.prepared.has(scope.scopeId)) {
135
- let files = []
139
+ // Prepared in the vault layout of the edit's generation, once per generation.
140
+ const preparedKey = `${scope.scopeId}\u0000${manifest.generationId}`
141
+ if (!workspace.prepared.has(preparedKey)) {
142
+ let files
136
143
  try {
137
144
  const { graph, profile } = workspace.corpus()
138
145
  const snapshot = seams.captureSnapshot({ project: workspace.project, graph, workspaceId: workspace.workspaceId, index: new Map(), configDigest: manifest.ext?.[EXT]?.configDigest ?? `sha256:${'0'.repeat(64)}`, capturedAt: now })
139
146
  const persistentPathRegistry = createMaintenanceStateStore({ workspaceRoot: workspace.workspaceRoot, workspaceId: workspace.workspaceId }).readPathRegistry()
140
- files = seams.prepareView({ snapshot, profile, scope, persistentPathRegistry, priorManifest: manifest, existingSettings: null, clock: workspace.clock, vaultRootBytes: Buffer.byteLength(store.vaultRoot, 'utf8') }).files
141
- } catch (error) { if (!isTyped(error) && !GONE.has(error?.code)) throw error }
142
- workspace.prepared.set(scope.scopeId, files)
147
+ files = seams.prepareView({
148
+ snapshot, profile, scope, persistentPathRegistry, priorManifest: manifest, existingSettings: null, clock: workspace.clock,
149
+ vaultRootBytes: Buffer.byteLength(store.vaultRoot, 'utf8'), layout: manifestLayoutVersion(manifest),
150
+ }).files
151
+ } catch (error) {
152
+ if (!isTyped(error) && error?.code !== 'ELEAFCHANGED' && !GONE.has(error?.code)) throw error
153
+ return null
154
+ }
155
+ workspace.prepared.set(preparedKey, files)
143
156
  }
144
- const file = workspace.prepared.get(scope.scopeId).find((item) => item.path === noteEntry.path && item.digest === noteEntry.noteDigest)
157
+ const file = workspace.prepared.get(preparedKey).find((item) => item.path === noteEntry.path && item.digest === noteEntry.noteDigest)
145
158
  return file ? file.bytes : null
146
159
  }
147
160
 
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from 'node:module'
2
+ import { PLUGIN_OWNED_PATHS, POLICY_SETTINGS_PATHS } from '../materialize/settings.mjs'
2
3
  import { EXCHANGE_CONSTANTS } from './exchange.mjs'
3
4
 
4
5
  // The fixed script that performs one conditional publication. It runs in two
@@ -26,11 +27,14 @@ const KEYS = {
26
27
  const isAbsolute = (value) => typeof value === 'string' && value.startsWith('/') && !value.includes('\u0000')
27
28
 
28
29
  // A vault-relative path the publisher may address: visible files only, plus
29
- // the one policy-owned settings file. Every other settings path is the
30
- // person's and cannot be named in a payload at all.
30
+ // the settings Atelier owns (the two policy settings files and its own
31
+ // plugin's files). Every other settings path is the person's and cannot be
32
+ // named in a payload at all.
33
+ const ATELIER_OWNED_SETTINGS = new Set([...POLICY_SETTINGS_PATHS, ...PLUGIN_OWNED_PATHS])
34
+
31
35
  export function isAddressableVaultPath(value) {
32
36
  if (typeof value !== 'string' || value.length === 0 || value.length > 1024 || value.startsWith('/') || value.includes('\\') || value.includes('\u0000')) return false
33
- if (value === POLICY_SETTINGS_PATH) return true
37
+ if (ATELIER_OWNED_SETTINGS.has(value)) return true
34
38
  return value.split('/').every((part) => part !== '' && part !== '.' && part !== '..' && !part.startsWith('.'))
35
39
  }
36
40
 
@@ -43,7 +47,7 @@ export function validatePayload(payload) {
43
47
  for (const key of Object.keys(payload)) if (!allowed.includes(key)) fail(`unknown key ${key}`)
44
48
  for (const key of allowed) if (payload[key] === undefined) fail(`missing key ${key}`)
45
49
  if (!isAbsolute(payload.vaultRoot)) fail('vaultRoot must be absolute')
46
- if (!isAddressableVaultPath(payload.path)) fail('path must be a relative visible path or the policy settings file')
50
+ if (!isAddressableVaultPath(payload.path)) fail('path must be a relative visible path or a settings file Atelier owns')
47
51
  if (payload.op === 'publish') {
48
52
  if (!OPERATION_ID.test(payload.operationId)) fail('operationId required')
49
53
  if (!SHA.test(payload.baseSha256)) fail('baseSha256 required')
@@ -76,7 +80,9 @@ export function criticalSection(P, host) {
76
80
  const trace = []
77
81
  const step = (event, detail) => trace.push({ ms: Number((host.performance.now() - t0).toFixed(3)), event, ...(detail || {}) })
78
82
  const crash = (point) => { if (host.crashSeam && host.crashSeam.at === point) host.crashSeam.halt(point) }
79
- const real = (target) => { try { return fs.realpathSync(target) } catch (error) { return null } }
83
+ // As the file system stores the path, except on Windows (see realPathAsStored): an app that holds the vault under
84
+ // another spelling of its folder (a letter case, on a volume that folds it) holds this vault.
85
+ const real = (target) => { try { return host.process.platform !== 'win32' && fs.realpathSync.native ? fs.realpathSync.native(target) : fs.realpathSync(target) } catch (error) { return null } }
80
86
  const vaultBasePath = app ? real(app.vault.adapter.getBasePath()) : real(P.vaultRoot)
81
87
  const full = nodePath.join(P.vaultRoot, P.path)
82
88
  const views = () => {
@@ -1,5 +1,6 @@
1
1
  // Conditional publication of a prepared view into an editable Obsidian vault.
2
2
  export { PROTOCOL_ID, buildEvalCode, createInProcessHost, criticalSection, isAddressableVaultPath, runInProcess, validatePayload } from './bridge-script.mjs'
3
3
  export { EXCHANGE_CONSTANTS, ExchangeRefusal, exchangeFiles, probeExchange, resetExchangeProbeCache, resolveExchange } from './exchange.mjs'
4
- export { TransportTimeout, createDirectAdapter, createEditorAdapter, createInProcessCall, createObsidianCliAdapter, createObsidianCliCall, defaultCliPath, defaultObsidianProcessProbe } from './transport.mjs'
4
+ export { NEUTRAL_DIRECTORY, TransportTimeout, createDirectAdapter, createEditorAdapter, createInProcessCall, createObsidianCliAdapter, createObsidianCliCall, defaultCliPath, defaultObsidianProcessProbe, publicationRoute } from './transport.mjs'
5
+ export { MAX_OBSIDIAN_SETTINGS_BYTES, OBSIDIAN_SETTINGS_FILE, enclosingVaults, findVaultEntry, obsidianSandboxedBuild, obsidianUserDataDir, readObsidianSettings, vaultRoute } from './vault-list.mjs'
5
6
  export { publishView } from './publisher.mjs'