@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
package/docs/obsidian.md CHANGED
@@ -72,13 +72,18 @@ A focus keeps the full vault on disk and filters the native Graph view. The
72
72
  query is
73
73
 
74
74
  ```
75
- path:"<note path>" OR path:"<note path>" ...
75
+ path:/^<note path>$/ OR path:/^<note path>$/ ...
76
76
  ```
77
77
 
78
- in the order of the selected set (canonical-id order). Inside the quoted term
79
- a backslash and a double quote are escaped with a backslash; nothing else is
80
- changed, so a path that itself contains search operators (`tag:`, `-`, `OR`,
81
- parentheses) stays text. Paths are NFC-normalized before they are quoted. A
78
+ in the order of the selected set (canonical-id order). Each term is a regular
79
+ expression anchored at both ends, so it matches exactly one note: a plain
80
+ `path:` term matches any path that contains it, and with titles as names one
81
+ note's path can lie inside another's. In the term every regular-expression
82
+ character and the `/` delimiter is escaped with a backslash, and a space and
83
+ a double quote are written as `\x20` and `\x22`, so a path that itself
84
+ contains search operators (`tag:`, `-`, `OR`, parentheses) stays text. Paths
85
+ are NFC-normalized first. A persisted focus of the earlier query version
86
+ (`obsidian-graph-search-paths/v1`, quoted substring terms) is still read. A
82
87
  path that carries a control character, a C1 control, or the U+2028 or U+2029
83
88
  line separator cannot be a search term and refuses with
84
89
  `focus-path-unrepresentable`; so does an absolute path, a backslash path or a
@@ -256,7 +261,7 @@ nothing under `scripts/obsidian/` ships.
256
261
  | --- | --- |
257
262
  | `@mnstry/atelier/obsidian` | `src/runtime/obsidian/index.mjs`: enablement, machine settings, engine, service lifecycle, production seams, app qualification |
258
263
  | `@mnstry/atelier/obsidian/contracts` | `src/projection/obsidian/contracts.mjs`: contract validation, `selectScope`, note paths |
259
- | `@mnstry/atelier/obsidian/materialize` | `prepareView`, settings, path registry |
264
+ | `@mnstry/atelier/obsidian/materialize` | `prepareView`, settings (including the plugin's files and entry), path registry |
260
265
  | `@mnstry/atelier/obsidian/publication` | `publishView`, editor adapters, the exchange |
261
266
  | `@mnstry/atelier/obsidian/recovery` | recovery store, journals, late-writer recheck |
262
267
  | `@mnstry/atelier/obsidian/edits` | edit observation, arbitration, apply policy, source apply |
@@ -272,22 +277,35 @@ What the tarball must and must not carry, and the package proof, are in
272
277
 
273
278
  ## First vault: what to expect
274
279
 
275
- This is the path an adopter walks on one machine, in the order the command
276
- requires it. Every operation and flag below is the shipped `atelier obsidian`
277
- usage text; `atelier obsidian --help` prints it.
280
+ This is the path an adopter walks on one machine. Every operation and flag
281
+ below is the shipped `atelier obsidian` usage text; `atelier obsidian --help`
282
+ prints it. There are three steps, and none of them is done by hand in
283
+ Obsidian.
278
284
 
279
- 1. Enable the projection in the project configuration. The member
280
- `ext["mnstry.atelier.obsidian"]` is an `atelier-obsidian-ext-settings/v1`
285
+ 1. Declare a view in the project configuration:
286
+ `atelier obsidian view add everything --all`. It adds a view of every note
287
+ the machine may show to `atelier.project.json`, shows the change as a diff
288
+ and how many notes the view would show, and makes it the default view. You
289
+ commit the file; Atelier commits nothing. See
290
+ [Adding a view](#adding-a-view). The member it writes,
291
+ `ext["mnstry.atelier.obsidian"]`, is an `atelier-obsidian-ext-settings/v1`
281
292
  document: `enabled: true`, `scopes` (each with a `scopeId`, a `mode` of
282
293
  `full`, `scoped` or `focus`, a `selector`, and for an expansion an explicit
283
- `depth` and `maxNodes`), and optionally `defaultScopeId`. A project without
284
- this member is `disabled (not-configured)` and nothing is published.
285
- `atelier obsidian scope list` shows what was declared.
294
+ `depth` and `maxNodes`), and optionally `defaultScopeId`. It can also be
295
+ written by hand. A project without this member is
296
+ `disabled (not-configured)` and nothing is published.
297
+ `atelier obsidian view list` (or `scope list`) shows what was declared.
286
298
  2. Set the private machine settings. They live outside every repository and
287
299
  are never committed:
288
- - `atelier obsidian audience set A,B` names the audiences this machine lets
289
- into a view. No audience is admitted by default, so a view is empty until
290
- one is set; `audience clear` empties it again.
300
+ - `atelier obsidian audience set me` lets "only you" into a view: every
301
+ audience of a note but `sensitive`, and the notes that carry no
302
+ classification, which only a vault that is yours alone shows.
303
+ `audience set A,B` names the audiences instead, and never shows notes
304
+ without a classification; `sensitive` is added by name
305
+ (`audience set me,sensitive`, which is such a list). No audience is
306
+ admitted by default, so a view is empty until one is set;
307
+ `audience clear` empties it again. See
308
+ [Notes without a classification](#notes-without-a-classification).
291
309
  - `atelier obsidian mode set manual` keeps every queued edit waiting for a
292
310
  person. `mode set automatic` is refused until an active automatic policy
293
311
  is installed.
@@ -295,19 +313,248 @@ usage text; `atelier obsidian --help` prints it.
295
313
  has to carry; `policy install FILE` installs it and leaves the mode
296
314
  unchanged; `policy revoke` returns the mode to `manual` and stops every
297
315
  later apply.
298
- 3. Start the maintenance service:
299
- `atelier obsidian service start --consent-actor ID --adapter=obsidian-cli`.
300
- Reaching the installed app is never a default, so `--adapter=obsidian-cli`
301
- is required, and the consent actor records who allowed the service to run.
302
- `service status` and `service stop` manage it; `service unit --print
303
- --adapter=obsidian-cli` prints a startup unit and installs nothing.
304
- 4. Open a view: `atelier obsidian open --scope ID --adapter=obsidian-cli`.
305
- The command starts or reconnects maintenance, verifies the vault by reading
306
- it back, and asks the app (version 1.13.7 or later) to open it. Quit
307
- Obsidian before a view's first publication (on Linux, also any app that
308
- runs on a system Electron): while a process that may be Obsidian runs, the
309
- first publication stops, and `open` and `status` say so; see
310
- [First publication while Obsidian is running](#first-publication-while-obsidian-is-running).
316
+ 3. Open a view:
317
+ `atelier obsidian open --scope ID --adapter=obsidian-cli`.
318
+ That's it. Reaching the installed app is never a default, so
319
+ `--adapter=obsidian-cli` is needed the first time; the workspace remembers
320
+ it, and later runs need no flag. The first open of a workspace records who
321
+ allowed the maintenance service to run: for a person at a terminal, the
322
+ account's name; for a program, `--consent-actor ID`, which it must pass.
323
+ Later opens reconnect and need neither. `atelier obsidian settings` shows
324
+ what was remembered and how to change it. `open` starts or
325
+ reconnects maintenance and asks it for a tick, makes Obsidian (version
326
+ 1.13.7 or later) know the view's vault as one of its vaults, opens it,
327
+ publishes the view, verifies the vault by reading it back, and waits until
328
+ the app answers for exactly that vault. It does this in whatever state
329
+ Obsidian is in; see
330
+ [What `open` does in each state of Obsidian](#what-open-does-in-each-state-of-obsidian).
331
+
332
+ The maintenance service can also be managed on its own:
333
+ `atelier obsidian service start [--consent-actor ID] [--adapter=obsidian-cli]`,
334
+ `service status` and `service stop`; `service unit --print` prints the login
335
+ item that `service unit --install` would write, and installs nothing (see
336
+ [Start at login](#start-at-login)). The flags are needed only until the
337
+ workspace remembers them, as for `open`.
338
+
339
+ ### Adding a view
340
+
341
+ ```sh
342
+ atelier obsidian view add ID (--all | --folder PATH [--folder PATH ...] [--repo R] | --tag T) [--expand DEPTH:MAX] [--default] [--allow-empty] [--yes]
343
+ ```
344
+
345
+ - `--all` is a view of every note (`full`). `--folder` takes the notes under a
346
+ folder of a repository, written from the repository's root (`docs/notes`),
347
+ and can be given as often as needed; `--repo R` names the repository when
348
+ the project has more than one. `--tag T` takes the notes with a tag. Each of
349
+ these is a `scoped` view, and `--expand DEPTH:MAX` adds the notes they link
350
+ to, following links up to DEPTH steps and adding at most MAX notes.
351
+ - The first view declared is the default; `--default` makes a later one the
352
+ default.
353
+ - Before writing, the command counts the notes the view would show on this
354
+ machine, as the machine decided who may see (as "only you", with the notes
355
+ without a classification it shows, while nobody decided), and names how
356
+ many of the notes it selects are withheld: those without a classification
357
+ (a `kg` block) that the decision does not show, and those whose audience is
358
+ not admitted. A view that would show no note is refused with
359
+ `view-would-be-empty` and the counts; `--allow-empty` declares it anyway.
360
+ When the machine admits no audience at all (`audience clear`), the refusal
361
+ says so and names `atelier obsidian audience set me`.
362
+ - While nobody decided who may see, the maintenance service publishes no
363
+ note, whatever the counts say: the next step `view add` names is then
364
+ `atelier obsidian audience set me`, before `atelier obsidian open` (`next`
365
+ under `--json`, in order).
366
+ - A person at a terminal sees the change and the counts and is asked
367
+ `Write this change? [Y/n]`; `--yes` answers beforehand, and no answer within
368
+ 10 minutes writes nothing (`unanswered`). For anyone else the command is the
369
+ consent: the change is made, and printed (`diff` under `--json`).
370
+ - The file is rewritten only when it is in the form Atelier writes JSON in
371
+ (two-space indentation, LF or CRLF line ends, with or without a final one),
372
+ so the change is the new view and nothing else. A file in another form is
373
+ refused with `project-config-format-unknown`, and the member to add by hand
374
+ is in the refusal's `detail`. The file is replaced atomically, keeps its
375
+ mode, and only while it holds the bytes the change was made from
376
+ (`project-config-changed` otherwise). A link is not followed
377
+ (`project-file-not-regular`).
378
+ - The workspace pointer is only ever written into an ignored `.atelier-local/`.
379
+ When that folder is not ignored, the same change adds `.atelier-local/` to
380
+ the project's `.gitignore`, and shows it.
381
+ - A name that is taken is refused with `view-exists`. Removing a view is done
382
+ by hand for now.
383
+ - The contract's `type` selector matches a note's file type (`markdown`,
384
+ `html`), not its `kg.type`, so `view add` does not offer it; a view by
385
+ `kg.type` needs a contract change.
386
+
387
+ ### Notes without a classification
388
+
389
+ A note whose front matter carries no `kg` block (or that has no front matter)
390
+ is unclassified: the knowledge graph gives it the `private` audience, and by
391
+ default no view shows it. A vault that is only yours shows it: the audience
392
+ decision `only-you` with `unclassified: shown`, which `audience set me`
393
+ records. A list of audiences never shows one, even a list that names every
394
+ audience "only you" stands for, so a vault for anyone else can never carry
395
+ them.
396
+
397
+ A note whose labels cannot be known is never shown, in any vault: front matter
398
+ Atelier cannot read (a block scalar such as `description: |`, a value wrapped
399
+ onto a second line), front matter that is not plain `key: value` lines at its
400
+ top level (a quoted key such as `"kg":`, a one-line flow or JSON mapping, a
401
+ complex key), or any top-level `kg` key that is not a block. Such a note may
402
+ say `sensitive`, which "only you" leaves out. Only a note with no front
403
+ matter, or with front matter of plain top-level keys at the start of the line,
404
+ none of which is `kg`, counts as one without a classification. A plain key is
405
+ written with ASCII letters, digits, `_` and `-`, starting with a letter or
406
+ `_`: a note whose front matter uses a key with a space (`due date:`), other
407
+ letters (`título:`), a dot or a leading digit, or a quoted key, stays withheld
408
+ even when it holds nothing sensitive. Give it a `kg` block to show it.
409
+
410
+ In an "only you" vault such a note is shown only when its bytes read as a note
411
+ the way the emitter reads every note, so one file that cannot be published
412
+ (front matter that opens and never closes, closes at once, or carries a
413
+ trailing space on its first line; bytes that are not UTF-8) never stops the
414
+ vault: it stays withheld, as does a file that cannot be read. A shown note is
415
+ part of the view in every respect: links to it are links inside the view,
416
+ generated text may name it, the selection operation resolves it, and an edit
417
+ to it is applied to its source like any other.
418
+
419
+ A decision recorded before this release, "only you" with those notes
420
+ withheld, keeps them withheld until `audience set me` is run again. Changing
421
+ the decision rebuilds every view at the next tick (the eligibility revision
422
+ changes).
423
+
424
+ ### What this machine remembers
425
+
426
+ A workspace's machine settings (`atelier-obsidian-machine-settings/v2`, owner
427
+ only, under the private data root, never in a repository) hold what a person
428
+ decided once, so no later run asks again or has to be told again. Each
429
+ decision is null until it is made, and otherwise records what was decided,
430
+ when, by whom when that is known (an actor identifier), and how: answered at
431
+ a terminal (`question`), given on the command line (`command`), taken as the
432
+ defaults (`defaults`), or carried over from an earlier release (`v1`).
433
+
434
+ | Decision | Holds | Made today by |
435
+ | --- | --- | --- |
436
+ | `audience` | `only-you` or `custom`, and whether notes that carry no classification are `shown` or `withheld`. Only `only-you` may show them; a list of audiences always withholds them. The admitted list stays in `audienceAllow`, the engine's audience input | `audience set me` (only you, shown) or `audience set A,B` / `audience clear` (withheld) |
437
+ | `location` | the absolute folder that holds this workspace's vaults | `location set DIR` |
438
+ | `loginItem` | `on` or `off` | `service unit --install` (on); `service unit --remove` and `uninstall` (off) |
439
+ | `adapter` | `obsidian-cli` | `--adapter=obsidian-cli` given to `open`, `service start` or `service unit --install` |
440
+
441
+ Who allowed the maintenance service is not a second copy here: it stays the
442
+ consent the service reads from its own settings. `atelier obsidian settings`
443
+ shows all of it, and how each answer is changed.
444
+
445
+ Releases up to 0.2.0-alpha.12 wrote `atelier-obsidian-machine-settings/v1`.
446
+ Such a file is read as the v2 document it stands for, an audience list set
447
+ with it becoming that person's decision, and stays v1 on disk until the next
448
+ write, which writes v2. A release that knows only v1 refuses a v2 file as
449
+ `invalid-machine-settings`: after an upgrade, a maintenance service that
450
+ still runs the earlier release fails its ticks until it is replaced, which
451
+ `atelier obsidian open` and `atelier obsidian service start` do.
452
+
453
+ ### Start at login
454
+
455
+ Without a login item the vault is complete after a restart, but nothing keeps
456
+ it fresh until the next `open`. A login item starts the maintenance service
457
+ when you log in:
458
+
459
+ ```sh
460
+ atelier obsidian service unit --install [--consent-actor ID] [--adapter=obsidian-cli]
461
+ atelier obsidian service unit --print # what --install writes; writes nothing
462
+ atelier obsidian service unit --remove
463
+ atelier obsidian uninstall
464
+ ```
465
+
466
+ - On macOS it is a launchd agent,
467
+ `~/Library/LaunchAgents/ai.mnstry.atelier.<project>.<workspace-id>.plist`.
468
+ macOS says "Background Items Added" and lists it as "node" under System
469
+ Settings → General → Login Items & Extensions, because there is no app to
470
+ name it after. Switched off there, it stays off: `status` reports
471
+ `installed, switched off in System Settings`, and `open` starts the service
472
+ for that session only.
473
+ - On Linux it is a systemd user unit,
474
+ `~/.config/systemd/user/atelier-obsidian-<workspace-id>.service`
475
+ (`$XDG_CONFIG_HOME` when set), enabled with `systemctl --user`. Disabled
476
+ with `systemctl --user disable`, it stays disabled: `status` reports
477
+ `installed, disabled`, and `open` starts the service for that session
478
+ only. Where no user instance of systemd answers (WSL, a container), the
479
+ answer is `login-item-unavailable` and nothing is left behind.
480
+ - Windows is not offered (`startup-platform-unqualified`), as publication
481
+ itself is refused there.
482
+
483
+ It runs the package installed in the project
484
+ (`<project>/node_modules/@mnstry/atelier/…`, named by that path, so the next
485
+ start runs whatever release the project has installed then), with the Node
486
+ that installed it and the search path you had, in the root folder. It names
487
+ the data folder and the workspace it was installed for, so it finds them at
488
+ login whatever variables your shell set. Run from
489
+ `npx` in a project that has no package installed, it refuses with
490
+ `login-item-needs-installed-package`: install `@mnstry/atelier` in the project
491
+ first. Installing records that you allowed the service to run at login (the
492
+ consent then covers startup): `--consent-actor ID` names who allows it; for a
493
+ person at a terminal, the actor already recorded for this workspace, or else
494
+ the account's name. A program that passes no `--consent-actor` for a
495
+ workspace whose consent covers the service alone is refused
496
+ (`startup-consent-required`). The answer is remembered as the `loginItem`
497
+ decision. `--remove` takes the consent back to the service alone, removes the
498
+ item, and starts the service again for this session only, as a process of its
499
+ own (with the adapter given or remembered; otherwise the next `open` starts
500
+ it). Each workspace has its own login item, port, log and consent, and
501
+ projects may pin different releases; `launchctl list | grep ai.mnstry.atelier`
502
+ lists every one on a Mac.
503
+
504
+ Once a login item is installed, the service is started through it: `service
505
+ start`, `open` and the replacement of an outdated service ask launchd or
506
+ systemd to start it, never start a second one beside it, and accept only the
507
+ service that proves itself with the installed entry. When the unit differs from
508
+ what would be written now (the Node it names was removed, say), the next start
509
+ writes it again and says `login item refreshed`. A refusal of the service at
510
+ login (another service already runs, or the consent does not cover startup)
511
+ ends the process cleanly, so it is not retried every minute, and `status` says
512
+ `the login item did not start the service: <code>` with the next step. A crash
513
+ is restarted after a minute.
514
+
515
+ `uninstall` removes the login item and stops the service. It keeps the vaults,
516
+ the private state, the project file and Obsidian's vault list as they are, and
517
+ prints where each is: every vault under the data root and every folder
518
+ allocated to a view where the workspace decided its vaults live, a view no
519
+ longer declared included. It does not start the service again.
520
+
521
+ After an upgrade of Atelier, a maintenance service started earlier still runs
522
+ the earlier release. One the login item started notices it: after each tick
523
+ it compares the package on disk (read through the path the item names, so a
524
+ re-pointed link or a `file:` install counts) with the release it started
525
+ with, and when they differ it exits after that tick with code 75, and its
526
+ service manager starts the new release within a minute. Otherwise `open` and
527
+ `service start` replace it: when the service of this workspace proves itself
528
+ Atelier's own but runs an earlier release than the installed one (its record
529
+ names the release it runs: the package version and a digest of its modules;
530
+ an earlier version, the same version with other modules, or no release
531
+ named), or refuses a tick that names a view as releases up to 0.2.0-alpha.11
532
+ do, they stop it as `service stop` would and start the installed release under
533
+ the consent already recorded (through the login item when there is one), and
534
+ say `service: restarted (outdated)`. The installed release is read from the
535
+ package of the entry that would be started, so with a login item it is the
536
+ project's own package. A service of a later release is never replaced by an
537
+ earlier one, so two installations used on one workspace (a global and a
538
+ project-local one, say) do not replace each other's service on every open:
539
+ `open` answers `service-unavailable` / `service-other-release`, and `service
540
+ start` answers that it runs, with `release: later`; run `atelier obsidian
541
+ service stop`, then open again, or open with the later release. A service in a
542
+ long tick is not stopped (`open` answers `busy`), and a listener that does not
543
+ prove itself this workspace's service is never touched.
544
+
545
+ The first time Obsidian opens a view's vault it asks "Do you trust the author
546
+ of this vault?", because every vault Atelier publishes carries Atelier's own
547
+ plugin. "Trust author and enable plugins" turns it on: a status bar item then
548
+ says whether the view is current, updating, held for an edit you made, stale,
549
+ or whether the maintenance service does not answer, and "Atelier: show status"
550
+ says why. "Browse vault in Restricted Mode" keeps every community plugin off
551
+ in that vault, and nothing else changes: maintenance, `open` and `status` work
552
+ without the plugin, as they always did. Obsidian keeps the answer per vault in
553
+ its own storage and asks again the next time until the vault is trusted or
554
+ restricted mode is switched on for good in its settings. Turning the plugin
555
+ off in a vault, or uninstalling it there, is respected from then on;
556
+ `atelier obsidian plugin on --scope ID` brings it back. What the plugin does
557
+ and never does is in [obsidian-plugin.md](obsidian-plugin.md).
311
558
 
312
559
  `open` and `status` answer with a freshness state, not a promise. `current`
313
560
  means the vault is the present generation, verified by read-back, and the app
@@ -319,7 +566,9 @@ and the view is not republished over it. The remaining outcomes (`indexing`,
319
566
  `publisher-conflict`, `app-missing`, `app-version-unsupported`,
320
567
  `app-cli-unavailable`, `launch-failed`, `service-unavailable`, `busy`,
321
568
  `disabled`) each name what to do next, and exit code 3 says the answer is not
322
- success.
569
+ success. Both also report Atelier's plugin for each view, under `plugin`:
570
+ present while it holds the vault open in the app, with the app and plugin
571
+ versions, or not present with the reason.
323
572
 
324
573
  Edits made in the vault are preserved before anything is republished. A body
325
574
  replacement is held as a pending edit; under manual mode it reaches its source
@@ -334,42 +583,444 @@ proposals list` and `proposals show OPERATION` read them, and nothing applies
334
583
  one. A body edit to a note whose source uses CRLF line endings is also a
335
584
  proposal, never an apply, because the app normalizes line endings on save.
336
585
 
337
- The vault itself lives under the private data root, outside every repository:
338
- `~/Library/Application Support/Atelier` on macOS, `$XDG_DATA_HOME/atelier`
339
- (else `~/.local/share/atelier`) on Linux and `%LOCALAPPDATA%\Atelier` on
340
- Windows, under `obsidian/<workspace-id>/`. `--data-root DIR` names another
341
- absolute directory. Private state inside an enrolled repository is refused.
342
- Publication is proven on macOS arm64 only and is refused on Windows; see
343
- [Known limits](#known-limits).
586
+ Atelier's private state lives under the private data root, outside every
587
+ repository: `~/Library/Application Support/Atelier` on macOS,
588
+ `$XDG_DATA_HOME/atelier` (else `~/.local/share/atelier`) on Linux and
589
+ `%LOCALAPPDATA%\Atelier` on Windows, under `obsidian/<workspace-id>/`.
590
+ `--data-root DIR` names another absolute directory. Private state inside an
591
+ enrolled repository is refused. Publication is proven on macOS arm64 only and
592
+ is refused on Windows; see [Known limits](#known-limits).
593
+
594
+ Where the vaults are is decided per workspace. Until a person decides it, a
595
+ view's vault is under the data root too, in `vaults/<view>`, as in every
596
+ earlier release. `atelier obsidian location set DIR` decides a visible folder
597
+ instead (`~/Atelier`, say). Each view that has not been published yet is then
598
+ given its own folder there at its first publication, named
599
+ `<project> (<view>)`: `~/Atelier/harbor-notes (everything)`. That name is also
600
+ the vault's name in Obsidian's vault switcher, so it is readable and unique
601
+ across projects and views. The first name that is free is taken, with a number
602
+ inside the parentheses when needed (`harbor-notes (everything 2)`): free means
603
+ nothing on the disk has it, and no vault Obsidian lists has it in any letter
604
+ case. The folder is created private to this user (mode 0700), and so is `DIR`
605
+ when Atelier creates it; a missing folder on the way to `DIR` is made as any
606
+ other folder is. The allocation is recorded in the workspace's private
607
+ state (`state/allocations/<view>.json`) and is never recomputed: renaming the
608
+ project or deciding another folder later moves no vault, and a vault published
609
+ before under the data root stays there. `location show` says where each
610
+ view's vault is, or will be, or why its record cannot be read.
611
+
612
+ Atelier publishes only into the folder it made. The allocation records the
613
+ folder's real path, device and inode, and the maintenance service checks them
614
+ at every tick, as the publisher does before it writes anything and again
615
+ before each note and before its commit (see Known limits). Another folder
616
+ put where the one made was (moved in, or a link) is not published into
617
+ (`vault-allocation-replaced`), nor is the same folder reached through a link
618
+ put on the way since, into a vault the app lists, say
619
+ (`vault-allocation-moved`). A folder that has gone is made again at the next
620
+ tick, only where its recorded real path leads and only where a location would
621
+ be accepted (not inside a repository, the private state, a vault Atelier
622
+ publishes or one the app lists, and on the data root's volume). A vault
623
+ removed, moved or replaced while it is being published stops that publication
624
+ (`vault-allocation-missing`, `vault-allocation-replaced`, or
625
+ `vault-root-moved` for a vault under the data root). Once the folder is put
626
+ back, or made again, the running service publishes into it again at its next
627
+ tick; no restart is needed. One case waits for the next change at the view's
628
+ sources: a folder made again after a publication that stopped just before its
629
+ commit. That generation is finished by restart recovery, its notes went with
630
+ the folder that went away, and the view reads `vault-note-missing` until then. A
631
+ view that was published and whose vault Atelier cannot find is refused
632
+ (`vault-allocation-lost`) rather than published again into a new, empty vault
633
+ under the data root: its record was lost, or, for a view that never had a
634
+ record (published under the data root), its `vaults/<view>` folder was
635
+ removed. Restore the record from a backup if its vault was allocated
636
+ elsewhere; otherwise make the folder `vaults/<view>` under the data root, and
637
+ the view is published there at the next change at its sources. `status` and
638
+ `open` name that next step for this code, and for `vault-allocation-moved`,
639
+ `vault-allocation-replaced` and `vault-allocation-missing`.
640
+
641
+ `DIR` is refused inside an enrolled repository or the project, inside a vault
642
+ Atelier publishes, inside a folder Obsidian lists as a vault, and on another
643
+ volume than the data root (a note is published by an atomic exchange, which
644
+ cannot cross volumes). A folder a sync client keeps in step (iCloud Drive, a
645
+ cloud storage provider, Dropbox, OneDrive, Google Drive, or a Desktop or
646
+ Documents folder iCloud syncs) is refused unless `--allow-synced-location` is
647
+ given: another machine's Obsidian could hold a vault there unseen. A folder
648
+ macOS protects (Desktop, Documents, Downloads) is accepted with a warning,
649
+ because macOS asks before Obsidian or the maintenance service may read it.
650
+ `~/` is read against the account's home folder, which a shell does not do
651
+ after `--x=`. Each check compares the folder as written and as its real path,
652
+ in any letter case, so a link into a listed or synced vault is refused as
653
+ that vault is. Obsidian's vault list is read through the app when it runs and
654
+ answers, and otherwise from its settings file, never written. When the list
655
+ cannot be read, `location set` says so and accepts the folder, and the
656
+ maintenance service allocates no vault there until it can read the list
657
+ (the view is `stale`, `app-vault-list-unreadable`, and is tried again at
658
+ the next tick).
659
+
660
+ ### What `open` does in each state of Obsidian
661
+
662
+ Obsidian opens a vault from a link only when the folder is in its own vault
663
+ list, the `vaults` of `obsidian.json` in its user-data directory. `open` puts
664
+ the view's vault there first, and never asks a person to open a folder by
665
+ hand. It then names the vault by its id (`obsidian://open?vault=<id>`), never
666
+ by path where the id reaches it first: a path is matched against the list by
667
+ string prefix, so a link for a note under `/A/strategy-lab` can open the
668
+ listed vault `/A/strategy`. When another vault's folder is named like the id,
669
+ or the id is unusual, the vault's own path is used, which the app matches
670
+ exactly because the folder is listed.
671
+
672
+ - **Obsidian runs and answers its command line (any vault is open).** `open`
673
+ reads the app's vault list through its command line. A vault the app does
674
+ not list is added and opened in a new window through the app itself (it
675
+ writes its own settings), and read back from its list. `open`
676
+ then waits, bounded, until the app answers for exactly this vault, and asks
677
+ maintenance for the view again: the publication runs through the app, which
678
+ now holds the vault, with every editor check of the publication protocol.
679
+ Obsidian does not have to be quit.
680
+ - **Obsidian is not running.** The view is published on the path with no app,
681
+ as before. `open` then adds the vault to the app's settings file (see
682
+ [Obsidian's vault list](obsidian-contract.md#obsidians-vault-list) for
683
+ exactly when and how that one file is written, marked to reopen) and starts
684
+ Obsidian plainly, with no link: an Obsidian started with a link opens only
685
+ that vault and drops the reopen flag of every other one, so your other
686
+ vaults would not come back the next time you start it. Started plainly, it
687
+ reopens every vault it had open, this one included, and once its command
688
+ line answers (the app itself, not the tool's "unable to find Obsidian")
689
+ `open` hands it the vault's link through that tool, never through the
690
+ operating system. A vault Obsidian already lists but had closed is not
691
+ reopened by a plain start, so if Obsidian refuses that link just after it
692
+ started, `open` hands it the link once more, again through that tool and
693
+ never through the operating system, before it gives up. On Linux a plain start is not
694
+ qualified yet, and Obsidian is started with the link (the other vaults'
695
+ reopen marks are lost there; a known limit).
696
+ In the same write, `open` turns Obsidian's command line on (`cli: true`)
697
+ when it is off: publishing into a vault Obsidian holds needs it until
698
+ Atelier's plugin takes over that part, and it is off in a new installation.
699
+ The answer says what was written and where the backup of the file as it
700
+ was is kept.
701
+ - **Obsidian is installed and never started** (no `obsidian.json` yet). When
702
+ the process table says, positively, that no Obsidian runs, `open` creates
703
+ the file, with this vault flagged open and the command line on, and starts
704
+ Obsidian plainly; Obsidian opens the vault. Nothing else is in the file,
705
+ and the settings folder is created private to you when it does not exist
706
+ yet. The file is created exclusively: if Obsidian writes its own first,
707
+ that one is kept and `open` says so. While an Obsidian may run, nothing is
708
+ created (`app-may-be-running`).
709
+ - **Obsidian runs with no vault open.** Its command line then answers every
710
+ command with "Vault not found.", so nothing can be asked of it, and its
711
+ settings file belongs to the running app, so it is only read. A vault the
712
+ app already lists is opened by path and published through the app as
713
+ above. For a vault it does not list yet, see "Restart, only when you ask"
714
+ below; without that, `open` answers `app-version-unsupported` with reason
715
+ `no-vault-open` and names `--restart-obsidian`.
716
+ - **Obsidian runs with its command line turned off** (the default of a new
717
+ installation). It then answers every command with "Command line interface
718
+ is not enabled", only a link reaches it, and its settings belong to the
719
+ running app, which keeps the switch in memory and would write it back. See
720
+ "Restart, only when you ask" below; without that, `open` adds and launches
721
+ nothing, and answers `app-cli-unavailable` with reason `cli-turned-off`,
722
+ naming `--restart-obsidian` and where to turn the switch on yourself:
723
+ Settings > General > Advanced > Command line interface.
724
+
725
+ **Restart, only when you ask.** In the two states where Obsidian runs but
726
+ `open` cannot reach it (its command line off, or no vault open and the view's
727
+ vault not in its list), `atelier obsidian open --restart-obsidian` lets `open`
728
+ restart Obsidian once:
729
+
730
+ 1. Before anything is sent, it checks, reading only, that the write the
731
+ restart is for can succeed: the view's vault folder exists, Obsidian's
732
+ settings file can be read and is yours, is no link and has no second
733
+ name, is not a Flatpak or snap build's, is not too large to take the
734
+ vault, and lists no vault at a folder above the view's. An Obsidian that
735
+ has shown only its starter window has written no settings file yet; then
736
+ the folder it goes in must be yours (or be missing inside a folder of
737
+ yours), and the file is created after the quit, as for an Obsidian that
738
+ never started. Otherwise `open` answers that refusal and leaves Obsidian
739
+ running. Right before the first signal, once the process below is proven,
740
+ it keeps a copy of the settings file beside it
741
+ (`obsidian.json.atelier-backup-<time>`), which this run's write does not
742
+ prune, and the answer names it; a copy that cannot be kept sends nothing.
743
+ 2. It sends SIGTERM to Obsidian's main process, and to nothing else. That
744
+ process is found in the process table and must be the only Obsidian main
745
+ process there, one of yours, named by the app bundle's path, with every
746
+ other Obsidian process its child; it is read again just before the signal
747
+ and must be the same process, yours, under the same parent. Anything else
748
+ (two Obsidians, another user's, a helper of another process, a table that
749
+ cannot be read) is answered `app-main-process-unproven`, with the reason in
750
+ `restart.detail`, and nothing is sent.
751
+ 3. Obsidian handles that SIGTERM as a quit: it closes every window, keeping
752
+ the vaults it had open flagged to reopen, and its helper processes end. On
753
+ macOS its main process can then stay, with no window. Electron handles
754
+ only the first SIGTERM as a quit: a second one ends the process at once,
755
+ without its quit handlers. `open` sends that second SIGTERM only when the
756
+ same process has shown no window and no helper at every reading for at
757
+ least 5 seconds (a margin, not a proof that it has nothing left to save). There
758
+ is never a third signal, and never a stronger one: an Obsidian that is not
759
+ gone within 30 seconds is answered `launch-failed` with reason
760
+ `app-did-not-quit`, and nothing is written. The answer says how many
761
+ signals were delivered.
762
+ 4. It adds the vault to Obsidian's settings and turns the command line on, in
763
+ the one write described above.
764
+ 5. It starts Obsidian plainly, and the vaults it had open come back, with this
765
+ one beside them. If the vault could not be added after all, Obsidian is
766
+ still started again, as it was.
767
+
768
+ The answer says that Obsidian was restarted, which process was signalled,
769
+ how, and what was written. The flag is never implied by any other flag or
770
+ answer; every other operation refuses it. The restart is qualified on macOS
771
+ only (on Linux a quit Obsidian is started with a link, which would drop the
772
+ other vaults' reopen flags): elsewhere `open` answers
773
+ `restart-platform-unqualified`.
774
+
775
+ Two limits. `open` asks nothing at a terminal yet: the restart needs the flag
776
+ there too, until the first-run command asks "Obsidian must restart once to add
777
+ this vault … Restart now?" in that state. And if `open` itself is interrupted
778
+ (Ctrl-C, or its process ended) after Obsidian quit and before it was started
779
+ again, Obsidian stays quit: start it yourself, and it reopens its vaults; run
780
+ `open` again to add the view's vault.
781
+
782
+ Only `open` adds a vault to Obsidian: the maintenance service never does, so
783
+ it never opens a window nobody asked for. A declared view that was never
784
+ opened is published while Obsidian is quit, and `open --scope ID` adds it.
785
+
786
+ In no state does `open` add a view's vault inside a folder Obsidian already
787
+ lists as a vault (your home folder, say): that vault would show the view's
788
+ notes too, and a command-line call run in the view's folder would reach it.
789
+ `open` answers `launch-failed` with reason `vault-inside-another-vault`:
790
+ remove that vault from Obsidian's vault list, or keep Atelier's data root
791
+ outside that folder. A view's vault that Obsidian lists already, below such a
792
+ vault, is reached by its id instead (see "Which window answers" in
793
+ [Known limits](#known-limits)).
344
794
 
345
- ### First publication while Obsidian is running
795
+ Obsidian can list one folder more than once, under another letter case or
796
+ through a link, and then open it in one window per entry. Each of those
797
+ windows holds the view's vault, and a publication coordinates with one window
798
+ only, so none is made: the view reports `publisher-conflict` with reason
799
+ `vault-open-in-several-windows`, and `open` answers the same, names the
800
+ entries (`open in Obsidian as: …`; `duplicates` in JSON) and launches nothing.
801
+ Remove the extra entries from Obsidian's vault list and open again. Closing
802
+ the extra windows is not enough: Obsidian keeps the last window it closed
803
+ marked open, so two entries can stay marked open with one window showing, and
804
+ the view stays refused until the list names the folder once.
805
+ While one entry of the folder has a window, `open` reaches only that one and
806
+ never opens another entry of the same folder beside it.
346
807
 
347
- The publisher writes into a vault only when it can coordinate with every
808
+ A view's first publication into the folder allocated for it is the one
809
+ exception to the rule below. While no list the app keeps names that folder, a
810
+ folder above it or one inside it, the vault is filled even while Obsidian
811
+ runs, by creating files only. When that run completes, `open` adds the vault
812
+ to Obsidian and opens it after the commit. A run that stops partway commits
813
+ nothing and falls back to the coordinated path. That happens when the list
814
+ names the folder meanwhile, a settings file with other bytes is already
815
+ there, or an interrupted run left a replacement or a removal. `open` may then
816
+ add and open the vault before it is complete, and the app publishes the rest.
817
+ Anything that would replace or remove a file waits for the app. See "First publication into a vault no Obsidian lists" in
818
+ [obsidian-contract.md](obsidian-contract.md). The plugin's entry written this
819
+ way counts as offered until the plugin says hello from the vault.
820
+
821
+ The publisher still writes into a vault only when it can coordinate with every
348
822
  Obsidian that may hold it, or when the process table shows, positively, that
349
823
  none runs. Otherwise it stops, and the view reports `publisher-conflict` with
350
824
  reason `editor-uncoordinated`. That reason has several causes, and the report
351
825
  does not say which: Obsidian runs without this vault open, or with it open but
352
826
  its command line did not answer; the process table could not be read, or on
353
827
  Linux shows an app that runs on a system Electron, which may be Obsidian; the
354
- app's version was not checked when the publication began (it started just
355
- then, or on macOS it is installed outside `/Applications`; see "Known
356
- limits"); or an app started while the view was being published with the app
357
- closed, which stops that publication partway. Before a first publication no
358
- app has the vault open, so only the first way on applies: quit Obsidian (on
359
- Linux, also any app that runs on a system Electron). The service then
360
- publishes the view directly, and
361
- `atelier obsidian open --scope ID --adapter=obsidian-cli` starts Obsidian on
362
- it. `status` and `open` give that as the next step. Once a view
363
- has been published, the last published vault stays in place and there is a
364
- second way on: open it in the running app as it is with
365
- `atelier obsidian open --allow-stale`, which waits, bounded, until the app
366
- answers for it. The publication is retried automatically either way.
367
-
368
- An Obsidian with no vault open at all answers its command line with "Vault not
369
- found." for every command, its version included. That is reported as outcome
370
- `app-version-unsupported` with reason `no-vault-open`, under `service.app` in
371
- `status` and by `open`. Opening any vault in Obsidian resolves it, because the
372
- version can then be read, and so does quitting Obsidian.
828
+ app's version was not checked when the publication began; or an app started
829
+ while the view was being published with the app closed. `status` gives
830
+ `atelier obsidian open` as the next step (it adds the vault to Obsidian and
831
+ publishes through it), or quitting Obsidian (on Linux, also any app that runs
832
+ on a system Electron). When `open` itself already did that and the
833
+ publication still stopped, it says to quit Obsidian and open again.
834
+
835
+ The tick `open` asks for prepares and publishes its view once more, whatever
836
+ state the view is in. A view whose last publication did not settle (refused,
837
+ stale or still updating) is also tried again without waiting for a change: as
838
+ soon as the app looks different (it quit or started, or opened or closed a
839
+ vault in its list), and otherwise after a delay that starts at 30 seconds and
840
+ doubles per attempt, up to the full reconciliation every five minutes. What
841
+ the app looks like is read from the process table and the app's vault list
842
+ alone: maintenance runs nothing in Obsidian to find out, and asks it for its
843
+ version only when a view is about to be published, without blocking.
844
+
845
+ While Atelier's plugin holds the view's vault open in the app and the command
846
+ line gives no version (as it answers while a vault is still loading), the
847
+ version the plugin reports stands in (reason `plugin-reported`); a version the
848
+ command line gives always decides, and while the process table shows no app
849
+ running, a plugin's report counts for nothing. `open` then asks the command
850
+ line nothing: a vault the app's settings file lists is opened by path, and one
851
+ it does not show is answered as `app-cli-unavailable` with reason
852
+ `vault-open-cli-silent` (the app has the vault open; make sure its
853
+ command-line interface is turned on). So is a listed vault the command line
854
+ does not answer for once it is opened, while only the plugin reports the
855
+ version: `open` says so at once instead of waiting for the app.
856
+
857
+ ## Personal workspaces
858
+
859
+ A personal workspace (see [personal-workspace.md](personal-workspace.md)) is
860
+ projected through one route, `bindPersonalWorkspace({ personalHome,
861
+ generationId })` in `src/projection/obsidian/personal-workspace.mjs`. Both
862
+ arguments are explicit; neither is discovered from the environment or the
863
+ working directory. A loader uses `loadBoundProject` with the same arguments,
864
+ or `loadBoundProjectOffThread`, which composes in a worker thread; the engine
865
+ awaits its loader. No command or service binds one yet: that waits for the selection seam that
866
+ keeps one projection configuration per person.
867
+
868
+ - **One graph.** The binding is attached to the generation's resolved
869
+ `atelier.project.json` under one symbol key. When a project carries it, the
870
+ pipeline's `buildGraph` builds the canonical graph with the caller's file
871
+ cache and composition's own rule that no private note is a link target, then
872
+ applies eligibility as for any project. The engine, view counts, source apply,
873
+ the proposal adapter and the selection operation all build through it, so they
874
+ read one graph. A plain build of the generation's configuration would differ:
875
+ there, a shared note's link can name a private note.
876
+ - **Only a confirmed graph is used.** A graph is returned only under a validity
877
+ key that `composePersonalWorkspace` confirmed. The key names:
878
+ - the authored manifest and overlay, as regular files of at most 1 MiB;
879
+ - every file of the generation, as regular files of at most 8 MiB (the
880
+ personal-workspace module's own limits);
881
+ - the private home's own state, any `.git` above it, and a `.git` in its
882
+ `generations` folder;
883
+ - each enrolled root's link status and its Git configuration (its remote and
884
+ its helpers), through a `gitdir:` file and its common directory where `.git`
885
+ is a file;
886
+ - the user's Git configuration (`~/.gitconfig` and the XDG one), through a
887
+ link, and any ambient Git variable;
888
+ - a digest of the graph's facts: nodes, edges, embeds, assets, resolved links
889
+ and every finding.
890
+
891
+ The inputs are read before the composition runs. A key is confirmed only when
892
+ the composition then accepted the inputs and composed a graph with exactly
893
+ those facts, so every graph used is one the composition built too. A single
894
+ change that lands while it runs is either seen by it or leaves the key behind;
895
+ a change and its exact undo during one composition is caught at the next full
896
+ reconciliation. Binding confirms the first key. An authored or generation
897
+ file the key reads is opened without following a link and without waiting on
898
+ it. A FIFO, a device or a link refuses at once, with the composition's code
899
+ (`generation-corrupt`, `malformed-input`). A Git configuration file is read
900
+ through a link, as Git reads it, but never waited on.
901
+
902
+ When a build finds its key unconfirmed, a caller that builds once composes in
903
+ its own thread. The engine, and the proposal adapter it runs, compose in a
904
+ worker, off the event loop, then build again. The adapter builds only when it
905
+ looks at a newly observed edit, as for any project. On a tick whose graph is
906
+ pending (`personal-validation-pending`), it records nothing for the edits it
907
+ reached and reports `observed.deferred`. It offers those edits, and every
908
+ earlier refusal, again next tick, and leaves a due proposal operation as it
909
+ is, without spending one of its attempts. While shared sources change before
910
+ every tick, every tick is pending: the edit is deferred again, with one full
911
+ build per tick, until a tick with no change. A worker that does not answer
912
+ within two minutes is let go of and refuses `personal-composition-unavailable`,
913
+ so the tick does not wait. A worker blocked in a system call (a FIFO opened for
914
+ reading) cannot be stopped until that call returns. Until then it keeps the
915
+ process from exiting, and each such expiry leaves one thread behind: a service
916
+ stopped in that state needs a kill. A graph that still differs refuses
917
+ `personal-graph-unconfirmed`.
918
+ - **A copy is refused.** The settings member names the bound generation. A copy
919
+ of a bound project that lost the binding (a structured clone, a JSON round
920
+ trip) refuses `personal-binding-lost`, and is never built as an ordinary
921
+ project.
922
+ - **Validity.** Composition decides whether the generation may be used, at bind
923
+ time and at every build. Its refusals keep their codes (`stale-generation`,
924
+ `generation-missing`, `generation-corrupt`, `generation-relocated`,
925
+ `retained-removed-reference`, `not-private-location`, `root-symlinked`,
926
+ `ambient-git-environment`, `future-schema`, `ambiguous-binding` and the rest).
927
+ `bindPersonalWorkspace` throws them as `PersonalWorkspaceRefusal`.
928
+ `loadBoundProject` and every build refuse with the same code as a maintenance
929
+ refusal, so the engine and every command report it typed, and no view stays
930
+ current. Nothing is published, and no vault, manifest, journal or private home
931
+ byte is written. A refusal the projection adds (`personal-view-id-reserved`,
932
+ `personal-views-exceed-settings-limit`) is a maintenance refusal from the
933
+ start.
934
+ - **Staleness is noticed at the next tick.** The engine observes the person's
935
+ `atelier.personal.json` and `atelier.overlay.json` and the bound generation's
936
+ `generation.json` as configuration. A change to any of them prepares every
937
+ view again on the next tick, unasked, and a generation that no longer holds
938
+ is refused there. Every full reconciliation composes a bound workspace again,
939
+ in a worker, whatever else it prepares. A cause the key does not name is
940
+ therefore refused within the reconciliation interval (five minutes by
941
+ default). Those causes are:
942
+ - the system Git configuration, and files a Git configuration includes
943
+ (`include.path`, `config.worktree`);
944
+ - an enrolled repository's work tree or top level changing;
945
+ - a change of the authored inputs and its exact undo during one composition;
946
+ - a shared note replaced by a link;
947
+ - an untracked note becoming ignored.
948
+
949
+ The views are prepared again only when that composition refuses or confirms a
950
+ different key: an unchanged workspace is not rebuilt, republished or locked.
951
+ After a refusal the engine
952
+ loads the project again at the next tick, so a loader that binds the next
953
+ generation is followed. The generation's record and the settings member both
954
+ name the generation, so a rebound generation invalidates every view even when
955
+ its configuration file is byte-identical.
956
+ - **Views.** The overlay's saved views become scopes in memory: each is a
957
+ scoped view of the repositories it names, and `everything` is a full view of
958
+ the composed graph. A saved view named `everything` is refused. More than 255
959
+ saved views, or a view of more than 256 repositories, is refused at bind
960
+ (`personal-views-exceed-settings-limit`), within the Obsidian settings
961
+ contract. The preferred view, when it names a saved one, is the default. No
962
+ generation file is written. `view add` refuses `views-from-personal-overlay`;
963
+ `audience set`, `audience clear` and `location set` refuse
964
+ `personal-binding-decision-unavailable`. Each refuses before anything is
965
+ resolved or written.
966
+ - **Preferences and coverage.** `preferences` are returned read only. A theme
967
+ is reported and never written: `appearance.json` stays the person's.
968
+ `coverage` is passed through untouched. No Obsidian code reads it: it states
969
+ what is not enforced, and is never permission.
970
+ - **Workspace identity.** The bound project's folder is the private home, so
971
+ the workspace pointer is `<personal home>/.atelier-local/obsidian.json`. A
972
+ generation is a closed, verified inventory, and an extra file there would
973
+ invalidate it. This is provisional until the per-person projection
974
+ configuration exists.
975
+ - **Edits.** A vault edit to a private note is never applied. Source apply
976
+ refuses it with `personal-overlay-proposals-only`. The proposal adapter does
977
+ not observe, record or propose it either: it answers
978
+ `personal-overlay-not-proposed`. Nothing is written into a generation, and
979
+ the view stays
980
+ `held-for-your-edit` until the edit is undone. Turning such an edit into a
981
+ change of the person's overlay is not built.
982
+ - **Reserved settings key.** The settings member
983
+ `ext["mnstry.atelier.personal-workspace"]` is reserved: the binding writes it
984
+ in memory. A project configuration that declares it itself is refused at
985
+ every build (`personal-binding-lost`, naming the key).
986
+
987
+ Measured once on 2,156 notes in three repositories, with 50 annotations and five
988
+ saved views (six views in all). The production contributions were loaded, as
989
+ the service loads them, so the proposal adapter was registered. The host's load
990
+ average was near 35, slower than an idle one, so read the times as upper
991
+ bounds. "Main-thread compositions" counts the Git probe a composition makes on
992
+ the event loop's thread.
993
+
994
+ | Step | Wall time | Longest event-loop block | Main-thread compositions |
995
+ | --- | --- | --- | --- |
996
+ | Load, composed in a worker (`loadBoundProjectOffThread`) | 3.8 s | 0.03 s | none |
997
+ | Load in the calling thread (`loadBoundProject`) | 3.5 s | 3.5 s | one |
998
+ | One build without a file cache (a caller that builds once) | 1.4 s | 1.3 s | none |
999
+ | First tick: load, build, and first publication of six vaults | 582 s | 574 s | none |
1000
+ | Tick with every view prepared again | 4.5 s | 4.5 s | none |
1001
+ | Idle tick | 0.25 s | 0.23 s | none |
1002
+ | Tick with a vault edit and a graph change: the adapter defers, the engine confirms in a worker | 12.4 s | 2.8 s | none |
1003
+ | Next tick: the adapter observes that edit (one build) | 2.1 s | 2.0 s | none |
1004
+ | Full-reconciliation tick, the edit still open | 5.0 s | 1.0 s | none |
1005
+
1006
+ **What is met.** No composition runs on the engine's event loop, the proposal
1007
+ adapter's included. The load, a confirmation after a change, and the
1008
+ composition at every full reconciliation run in a worker; the tick does not
1009
+ wait on one that misses its deadline. An unchanged workspace is not prepared
1010
+ again. While an edit stays open and shared sources are quiet, the adapter
1011
+ builds nothing more; under continuous change it builds once per tick until a
1012
+ quiet tick.
1013
+
1014
+ **What remains before a service or `open` binds a personal workspace of this
1015
+ size.** The first publication blocks the event loop far longer than the
1016
+ service's five-second health probe: about ten minutes here. That is the
1017
+ publication path every project takes, not this route: an ordinary project of
1018
+ 450 notes, measured the same way, blocked as long as a bound one. Preparing six
1019
+ views again blocked 4.5 s, under the probe. The proposal adapter's build of a
1020
+ newly observed edit, without the engine's file cache, blocked 2.0 s, at a
1021
+ two-second target, as for an ordinary project. A service binding needs the
1022
+ first publication to yield to the event loop (or run off it), or a corpus small
1023
+ enough to stay within the probe.
373
1024
 
374
1025
  ## Known limits
375
1026
 
@@ -402,8 +1053,10 @@ test reported as a pass.
402
1053
  built on it does not coordinate with any app it then finds running, and
403
1054
  asks it nothing. That publication stops as `publisher-conflict` with reason
404
1055
  `editor-uncoordinated`, and the next publication asks again and reads the
405
- version. A view that stopped is tried again on its next change, or at the
406
- next full reconciliation, every five minutes.
1056
+ version. A view that stopped is tried again on the next tick `open` asks
1057
+ for, as soon as the app looks different, and otherwise after a delay that
1058
+ starts at 30 seconds and doubles, up to the full reconciliation every five
1059
+ minutes. A tick `open` asks for never reuses a remembered app answer.
407
1060
  - Install location (macOS): the app is found only at
408
1061
  `/Applications/Obsidian.app`, and its command-line tool only inside it. An
409
1062
  Obsidian installed elsewhere, or a renamed bundle, is `app-missing`.
@@ -417,9 +1070,75 @@ test reported as a pass.
417
1070
  published through the app. `status` (under `service.app`) and `open` say what
418
1071
  to do: open any vault in Obsidian, or quit Obsidian. With the app quit,
419
1072
  maintenance publishes on its own path again; with a vault open, it reads
420
- the version again on a later tick. `open` launches nothing while the app
421
- runs with no vault open, and after its own launch it waits, bounded, while
422
- the app is still opening the vault.
1073
+ the version again on a later tick. While the app runs with no vault open,
1074
+ `open` has it open a vault only when its list already knows that vault,
1075
+ by path; one it does not know cannot be added then (see below). After its
1076
+ own launch `open` waits, bounded, while the app is still opening the vault;
1077
+ an app whose vault window is still loading answers a command with `Error:
1078
+ Command "…" not found`, which is read as not up yet, never as a version.
1079
+ - Vault location: consent to a folder a sync client keeps in step
1080
+ (`--allow-synced-location`) is given when `location set` decides the folder,
1081
+ and is not recorded, so the maintenance service does not check for sync
1082
+ clients again. The decision keeps the folder as it was written, so at a
1083
+ view's first allocation a folder on the way that has since become a link
1084
+ into a synced folder is followed, and its vault is allocated there without
1085
+ being asked; a folder that becomes synced without any link (iCloud's
1086
+ "Desktop & Documents Folders" turned on, say) is not refused either, at the
1087
+ first allocation or when a vault is made again. Every other check runs
1088
+ again at both, and a vault made again, or published into, through a link on
1089
+ the way is refused (`vault-allocation-moved`).
1090
+ - Replacing a vault during a publication: the allocated folder is checked
1091
+ before the vault lock is taken and again once its folder is made, after the
1092
+ app is asked about the vault, before each note and before the generation is
1093
+ committed. A program of the same account that puts a link in its place
1094
+ between one check and what follows it can still have that go through the
1095
+ link: one note's writes (the folders on its way and the note), or the vault
1096
+ lock's folder (made there, or made private if it was there already) and, in
1097
+ a folder that already holds one (another Atelier vault), a lock ticket and
1098
+ its release flag. For a note replaced or removed while the app holds the
1099
+ vault, the app is asked about that note between the check and the write, so
1100
+ that window lasts a round trip to the app. The next check refuses the
1101
+ publication, and it is never reported current.
1102
+ - Vault identity: an allocated folder is known by its real path, device and
1103
+ inode. On Linux a folder made at the same path after the allocated one was
1104
+ removed can be given the same inode number (ext4 reuses a freed one), and is
1105
+ then taken for the one made; macOS (APFS) did not reuse one in 200 tries.
1106
+ Such a folder is published into only at the recorded real path, and the
1107
+ plugin's bearer only when this account owns it.
1108
+ - Obsidian's settings file: `open` adds a view's vault to the app's own list,
1109
+ `obsidian.json` in its user-data directory: `~/Library/Application
1110
+ Support/obsidian` on macOS and `$XDG_CONFIG_HOME/obsidian` (else
1111
+ `~/.config/obsidian`) on Linux. It is written only while the process table
1112
+ shows, positively, that no Obsidian runs, only when Obsidian created it
1113
+ before, and with a backup beside it; see
1114
+ [Obsidian's vault list](obsidian-contract.md#obsidians-vault-list). A
1115
+ Flatpak or snap build keeps its list inside its sandbox and never reads that
1116
+ file. Which build is in use is read from which vault list was written last
1117
+ (every build rewrites its own when a vault window opens or closes), and only
1118
+ when no build wrote one from its installation; for a Flatpak or snap build
1119
+ the file is then neither read nor written: `open` answers
1120
+ `obsidian-sandboxed` and says to start Obsidian with any vault open, then
1121
+ open again, and adds the vault through the app. On Windows no location is
1122
+ known, and `open` adds the vault through the app in the same way.
1123
+ - Recent items: adding a vault through the running app is Obsidian's own
1124
+ "open folder as vault", which also puts the vault's folder in the operating
1125
+ system's recently used documents (Recent Items on macOS).
1126
+ - Which window answers: Obsidian answers a command-line call that names a
1127
+ vault (`vault=<id>` first) in that vault's window; any other call in the
1128
+ window of the first vault in its list whose folder is the tool's working
1129
+ directory or contains it, and otherwise in the vault window that had focus
1130
+ last. A vault that takes a call is opened when it is closed. Calls about a
1131
+ vault therefore name its id; only when that id would name another vault
1132
+ first (one whose folder has the id as its name) do they run inside its
1133
+ folder instead, and not at all when a vault listed before it at a folder
1134
+ above it (your home folder, say) would take them there. The vault root is
1135
+ taken in the spelling the file system stores, so a data root given in
1136
+ another letter case on macOS changes nothing. Publication calls do this
1137
+ only while the app's list shows the view's vault open, and run in a
1138
+ directory that is no vault otherwise. So maintenance never reopens a vault
1139
+ window you closed while Obsidian keeps running, and never reaches another
1140
+ vault: publication then stops as `publisher-conflict` until `atelier
1141
+ obsidian open` opens the vault again or Obsidian quits.
423
1142
  - Detecting the app: the process table is read with `ps -A -o comm=`
424
1143
  (`pid=,comm=` on Linux), and the app is recognised by the executable a
425
1144
  process runs, never by its arguments, so a path argument that contains an
@@ -469,6 +1188,26 @@ test reported as a pass.
469
1188
  - An edit the byte lens cannot turn into source bytes (a new or changed link
470
1189
  to another note of the vault, an edited front matter) becomes a copy-only
471
1190
  proposal. No operation applies one.
1191
+ - Atelier's plugin (phase 1) reports presence and status and decides
1192
+ nothing: publication still coordinates with the app through its
1193
+ command-line tool, which must be enabled, and the plugin only supplies the
1194
+ app version where that tool gives none. A person who turns the plugin off
1195
+ in a vault, or uninstalls it there, is followed: its entry is not added
1196
+ back and a deleted folder is not made again until the person turns it on
1197
+ in Obsidian's settings or runs
1198
+ `atelier obsidian plugin on --scope ID`. Plugin files are never removed from
1199
+ a vault. See [obsidian-plugin.md](obsidian-plugin.md).
1200
+ - Login items: the tests run the launchd and systemd managers' own code
1201
+ against stand-ins that answer as launchctl and systemctl do, and never
1202
+ against a real session. What `launchctl print-disabled` says for an item
1203
+ switched off in System Settings, and the exit statuses read from
1204
+ `launchctl` and `systemctl`, are the documented ones and are not yet
1205
+ confirmed on a host. A unit whose program cannot be loaded at all (the
1206
+ project's package was removed) is restarted by its manager once a minute,
1207
+ and each attempt adds a few lines to `state/service/login-item.log`, which
1208
+ nothing bounds: `service unit --remove` or `uninstall` ends it. An upgrade
1209
+ in the moment between a service's start and the first reading of its release
1210
+ is not noticed until the next change.
472
1211
  - Acceptance: a schema-valid receipt closes no gate, the package proof closes
473
1212
  no gate, and no adopter acceptance is recorded in this repository.
474
1213
 
@@ -482,3 +1221,9 @@ The test suite proves each oracle can fail:
482
1221
  - `createReceiptValidatorForOracleTests` accepts a rule table with one rule
483
1222
  switched off; every negative case that depends on that rule then passes,
484
1223
  and even that closes nothing.
1224
+ - `openScopeForOracleTests` with an `OPENING_PRIMITIVES.keptByApp` that
1225
+ never holds: an open with Obsidian running without the vault reports the
1226
+ publisher conflict it could have cleared, and the first-open oracle fails.
1227
+ - `createMaintenanceEngineForOracleTests` with an
1228
+ `ENGINE_PRIMITIVES.isRetryDue` that never holds: a refused view waits for
1229
+ the full reconciliation, and the retry oracle fails.