@mnstry/atelier 0.2.0-alpha.1 → 0.2.0-alpha.11

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 (414) hide show
  1. package/CHANGELOG.md +413 -0
  2. package/README.md +402 -205
  3. package/SECURITY.md +6 -1
  4. package/contracts/atelier-adoption-policy.v1.schema.json +49 -0
  5. package/contracts/atelier-coauthor.v1.schema.json +163 -0
  6. package/contracts/atelier-decision-request.v1.schema.json +476 -0
  7. package/contracts/atelier-decision-result.v1.schema.json +500 -0
  8. package/contracts/atelier-guide.v1.schema.json +227 -0
  9. package/contracts/atelier-intake.v1.schema.json +151 -0
  10. package/contracts/atelier-migration.v2.schema.json +61 -0
  11. package/contracts/atelier-obsidian-acceptance-receipt.v1.schema.json +211 -0
  12. package/contracts/atelier-obsidian-apply-policy.v1.schema.json +298 -0
  13. package/contracts/atelier-obsidian-corpus-profile.v1.schema.json +98 -0
  14. package/contracts/atelier-obsidian-edit-operation.v1.schema.json +138 -0
  15. package/contracts/atelier-obsidian-ext-settings.v1.schema.json +287 -0
  16. package/contracts/atelier-obsidian-generation-manifest.v1.schema.json +353 -0
  17. package/contracts/atelier-obsidian-proposal-receipt.v1.schema.json +110 -0
  18. package/contracts/atelier-obsidian-publication-journal.v1.schema.json +167 -0
  19. package/contracts/atelier-obsidian-scope.v1.schema.json +264 -0
  20. package/contracts/atelier-obsidian-service-state.v1.schema.json +149 -0
  21. package/contracts/atelier-obsidian-source-snapshot.v1.schema.json +179 -0
  22. package/contracts/atelier-pane-presentation.v1.schema.json +76 -0
  23. package/contracts/atelier-presentation.v1.schema.json +1293 -0
  24. package/contracts/atelier-repository-observation.v1.schema.json +163 -0
  25. package/contracts/atelier-review.v1.schema.json +1601 -0
  26. package/contracts/atelier-skill-steward.v1.schema.json +692 -0
  27. package/contracts/atelier-upgrade-plan.v2.schema.json +398 -0
  28. package/contracts/atelier-upgrade-receipt.v1.schema.json +99 -0
  29. package/contracts/atelier-vault-publication.v1.schema.json +58 -0
  30. package/contracts/public-api-baseline.json +57 -0
  31. package/docs/assurance-controls.md +49 -0
  32. package/docs/atelier-runtime.md +28 -2
  33. package/docs/atelier-sync.md +172 -0
  34. package/docs/blocks/audience-visibility.md +9 -0
  35. package/docs/blocks/claims.md +61 -0
  36. package/docs/blocks/conformance-admission.md +15 -0
  37. package/docs/blocks/will-not-do.md +18 -0
  38. package/docs/coauthor-session.md +57 -0
  39. package/docs/connected-composition.md +75 -0
  40. package/docs/continuity.md +3 -4
  41. package/docs/decisions.md +167 -0
  42. package/docs/design.md +167 -0
  43. package/docs/exact-upgrades.md +159 -0
  44. package/docs/extension-pack-lifecycle.md +33 -0
  45. package/docs/guided-upgrades.md +99 -0
  46. package/docs/install.md +99 -16
  47. package/docs/intake-and-guides.md +65 -0
  48. package/docs/integration-and-review-plan.md +409 -0
  49. package/docs/integration-contract-decisions.md +22 -0
  50. package/docs/knowledge-graph.md +8 -4
  51. package/docs/local-review.md +84 -0
  52. package/docs/local-services.md +328 -0
  53. package/docs/local-state.md +107 -0
  54. package/docs/obsidian-contract.md +698 -0
  55. package/docs/obsidian.md +484 -0
  56. package/docs/portable-presentation.md +328 -0
  57. package/docs/presentation-consumer-boundaries.md +75 -0
  58. package/docs/presentation-review-disposition.md +47 -0
  59. package/docs/project-command-inventory.md +38 -0
  60. package/docs/project-options.md +53 -0
  61. package/docs/release-engineering.md +174 -12
  62. package/docs/repo-boundary-guard.md +12 -2
  63. package/docs/review-portability.md +55 -0
  64. package/docs/skill-steward.md +156 -0
  65. package/docs/upgrade.md +155 -5
  66. package/docs/vault-service.md +292 -0
  67. package/fixtures/atelier-coauthor/invalid/authority.v1.json +5 -0
  68. package/fixtures/atelier-coauthor/valid/config.v1.json +12 -0
  69. package/fixtures/atelier-guide/invalid/authority.v1.json +11 -0
  70. package/fixtures/atelier-guide/valid/capability.v1.json +10 -0
  71. package/fixtures/atelier-guide/valid/consent.v1.json +11 -0
  72. package/fixtures/atelier-guide/valid/engagement.v1.json +7 -0
  73. package/fixtures/atelier-guide/valid/offer.v1.json +10 -0
  74. package/fixtures/atelier-intake/invalid/authority.v1.json +9 -0
  75. package/fixtures/atelier-intake/valid/attempt.v1.json +8 -0
  76. package/fixtures/atelier-intake/valid/completion.v1.json +9 -0
  77. package/fixtures/atelier-intake/valid/source.v1.json +7 -0
  78. package/fixtures/atelier-repository-observation/invalid/complete-with-blocker.v1.json +18 -0
  79. package/fixtures/atelier-repository-observation/valid/complete-local.v1.json +48 -0
  80. package/fixtures/atelier-review/invalid/authenticated-identity.v1.json +22 -0
  81. package/fixtures/atelier-review/valid/bound.v1.json +229 -0
  82. package/fixtures/atelier-review/valid/bundle.v1.json +267 -0
  83. package/fixtures/atelier-review/valid/contribution.v1.json +22 -0
  84. package/fixtures/atelier-review/valid/lifecycle.v1.json +14 -0
  85. package/fixtures/atelier-skill-steward/audit/invalid/source-mutation.v1.json +26 -0
  86. package/fixtures/atelier-skill-steward/audit/valid/clean.v1.json +26 -0
  87. package/fixtures/atelier-skill-steward/candidates/invalid/workflow-key.v1.json +37 -0
  88. package/fixtures/atelier-skill-steward/candidates/valid/create.v1.json +37 -0
  89. package/fixtures/atelier-skill-steward/lock/invalid/bad-digest.v1.json +18 -0
  90. package/fixtures/atelier-skill-steward/lock/valid/installed.v1.json +23 -0
  91. package/fixtures/atelier-skill-steward/plan/invalid/delete-action.v1.json +33 -0
  92. package/fixtures/atelier-skill-steward/plan/valid/add.v1.json +33 -0
  93. package/fixtures/atelier-upgrade-transaction/adoption-policy/invalid/example.json +12 -0
  94. package/fixtures/atelier-upgrade-transaction/adoption-policy/valid/example.json +11 -0
  95. package/fixtures/atelier-upgrade-transaction/migration/invalid/example.json +20 -0
  96. package/fixtures/atelier-upgrade-transaction/migration/valid/example.json +19 -0
  97. package/fixtures/atelier-upgrade-transaction/upgrade-plan/invalid/example.json +58 -0
  98. package/fixtures/atelier-upgrade-transaction/upgrade-plan/valid/example.json +57 -0
  99. package/fixtures/atelier-upgrade-transaction/upgrade-receipt/invalid/example.json +15 -0
  100. package/fixtures/atelier-upgrade-transaction/upgrade-receipt/valid/example.json +14 -0
  101. package/fixtures/atelier-vault-publication/invalid/publication.json +5 -0
  102. package/fixtures/atelier-vault-publication/valid/publication.json +11 -0
  103. package/fixtures/decisions/request/invalid/unknown-authority.v1.json +61 -0
  104. package/fixtures/decisions/request/valid/triage.v1.json +60 -0
  105. package/fixtures/decisions/result/invalid/execution-authority.v1.json +50 -0
  106. package/fixtures/decisions/result/valid/abstained.v1.json +23 -0
  107. package/fixtures/decisions/result/valid/assessed.v1.json +50 -0
  108. package/fixtures/obsidian/acceptance/ap05-extra-notes.json +14 -0
  109. package/fixtures/obsidian/acceptance/receipts/G07.valid.v1.json +63 -0
  110. package/fixtures/obsidian/acceptance/receipts/G13.valid.v1.json +69 -0
  111. package/fixtures/obsidian/acceptance/receipts/G14.valid.v1.json +62 -0
  112. package/fixtures/obsidian/acceptance/receipts/G15.valid.v1.json +56 -0
  113. package/fixtures/obsidian/acceptance/receipts/G16.valid.v1.json +73 -0
  114. package/fixtures/obsidian/acceptance/receipts/G17.valid.v1.json +75 -0
  115. package/fixtures/obsidian/acceptance/receipts/G18.valid.v1.json +64 -0
  116. package/fixtures/obsidian/acceptance/service-sentinels.json +10 -0
  117. package/fixtures/obsidian/contracts/acceptance-receipt/invalid/duplicate-evidence-name.v1.json +39 -0
  118. package/fixtures/obsidian/contracts/acceptance-receipt/invalid/evidence-by-absolute-path.v1.json +34 -0
  119. package/fixtures/obsidian/contracts/acceptance-receipt/invalid/missing-candidate-tree.v1.json +33 -0
  120. package/fixtures/obsidian/contracts/acceptance-receipt/invalid/no-evidence.v1.json +28 -0
  121. package/fixtures/obsidian/contracts/acceptance-receipt/invalid/unknown-outcome.v1.json +34 -0
  122. package/fixtures/obsidian/contracts/acceptance-receipt/invalid/unknown-top-level-field.v1.json +35 -0
  123. package/fixtures/obsidian/contracts/acceptance-receipt/valid/node-gate.v1.json +34 -0
  124. package/fixtures/obsidian/contracts/apply-policy/invalid/automatic-without-edit-class.v1.json +27 -0
  125. package/fixtures/obsidian/contracts/apply-policy/invalid/conflict-overwrite.v1.json +29 -0
  126. package/fixtures/obsidian/contracts/apply-policy/invalid/missing-policy-digest.v1.json +28 -0
  127. package/fixtures/obsidian/contracts/apply-policy/invalid/unbounded-batch.v1.json +29 -0
  128. package/fixtures/obsidian/contracts/apply-policy/invalid/unimplemented-edit-class.v1.json +30 -0
  129. package/fixtures/obsidian/contracts/apply-policy/invalid/unknown-mode.v1.json +29 -0
  130. package/fixtures/obsidian/contracts/apply-policy/invalid/unknown-selector-operator.v1.json +22 -0
  131. package/fixtures/obsidian/contracts/apply-policy/invalid/unknown-top-level-field.v1.json +30 -0
  132. package/fixtures/obsidian/contracts/apply-policy/valid/automatic-scoped.v1.json +29 -0
  133. package/fixtures/obsidian/contracts/apply-policy/valid/manual-default.v1.json +20 -0
  134. package/fixtures/obsidian/contracts/corpus-profile/invalid/absolute-path-in-ext.v1.json +30 -0
  135. package/fixtures/obsidian/contracts/corpus-profile/invalid/absolute-repository-root.v1.json +27 -0
  136. package/fixtures/obsidian/contracts/corpus-profile/invalid/duplicate-repository-id.v1.json +27 -0
  137. package/fixtures/obsidian/contracts/corpus-profile/invalid/overlapping-managed-roots.v1.json +27 -0
  138. package/fixtures/obsidian/contracts/corpus-profile/invalid/unknown-enrollment.v1.json +27 -0
  139. package/fixtures/obsidian/contracts/corpus-profile/invalid/unknown-top-level-field.v1.json +28 -0
  140. package/fixtures/obsidian/contracts/corpus-profile/valid/three-repositories.v1.json +27 -0
  141. package/fixtures/obsidian/contracts/edit-operation/invalid/absolute-recovery-ref.v1.json +21 -0
  142. package/fixtures/obsidian/contracts/edit-operation/invalid/inline-observed-bytes.v1.json +22 -0
  143. package/fixtures/obsidian/contracts/edit-operation/invalid/missing-base-digest.v1.json +20 -0
  144. package/fixtures/obsidian/contracts/edit-operation/invalid/short-idempotency-key.v1.json +21 -0
  145. package/fixtures/obsidian/contracts/edit-operation/invalid/unknown-kind.v1.json +21 -0
  146. package/fixtures/obsidian/contracts/edit-operation/valid/pending-body-replacement.v1.json +21 -0
  147. package/fixtures/obsidian/contracts/ext-settings/invalid/absolute-path-prefix.v1.json +28 -0
  148. package/fixtures/obsidian/contracts/ext-settings/invalid/apply-policy-in-portable-settings.v1.json +30 -0
  149. package/fixtures/obsidian/contracts/ext-settings/invalid/duplicate-scope-id.v1.json +27 -0
  150. package/fixtures/obsidian/contracts/ext-settings/invalid/machine-local-vault-path.v1.json +28 -0
  151. package/fixtures/obsidian/contracts/ext-settings/invalid/service-port-in-portable-settings.v1.json +28 -0
  152. package/fixtures/obsidian/contracts/ext-settings/invalid/unknown-default-scope.v1.json +27 -0
  153. package/fixtures/obsidian/contracts/ext-settings/valid/disabled.v1.json +5 -0
  154. package/fixtures/obsidian/contracts/ext-settings/valid/enabled-with-scopes.v1.json +27 -0
  155. package/fixtures/obsidian/contracts/generation-manifest/invalid/absolute-note-path.v1.json +114 -0
  156. package/fixtures/obsidian/contracts/generation-manifest/invalid/complete-with-unwritten-notes.v1.json +114 -0
  157. package/fixtures/obsidian/contracts/generation-manifest/invalid/derived-link-claimed-declared.v1.json +114 -0
  158. package/fixtures/obsidian/contracts/generation-manifest/invalid/duplicate-note-identity.v1.json +114 -0
  159. package/fixtures/obsidian/contracts/generation-manifest/invalid/in-scope-endpoint-missing.v1.json +114 -0
  160. package/fixtures/obsidian/contracts/generation-manifest/invalid/title-only-note-path.v1.json +114 -0
  161. package/fixtures/obsidian/contracts/generation-manifest/invalid/unknown-note-field.v1.json +115 -0
  162. package/fixtures/obsidian/contracts/generation-manifest/invalid/unknown-relation-type.v1.json +114 -0
  163. package/fixtures/obsidian/contracts/generation-manifest/invalid/withheld-endpoint.v1.json +114 -0
  164. package/fixtures/obsidian/contracts/generation-manifest/valid/two-notes.v1.json +114 -0
  165. package/fixtures/obsidian/contracts/oracles/scope-cases.json +412 -0
  166. package/fixtures/obsidian/contracts/proposal-receipt/invalid/accepted-without-proposal-id.v1.json +12 -0
  167. package/fixtures/obsidian/contracts/proposal-receipt/invalid/missing-adapter-operation-id.v1.json +11 -0
  168. package/fixtures/obsidian/contracts/proposal-receipt/invalid/unknown-backpressure-outcome.v1.json +12 -0
  169. package/fixtures/obsidian/contracts/proposal-receipt/invalid/unknown-top-level-field.v1.json +13 -0
  170. package/fixtures/obsidian/contracts/proposal-receipt/valid/accepted-new.v1.json +12 -0
  171. package/fixtures/obsidian/contracts/proposal-receipt/valid/deferred.v1.json +12 -0
  172. package/fixtures/obsidian/contracts/publication-journal/invalid/absolute-recovery-ref.v1.json +47 -0
  173. package/fixtures/obsidian/contracts/publication-journal/invalid/duplicate-sequence.v1.json +47 -0
  174. package/fixtures/obsidian/contracts/publication-journal/invalid/empty-protocol-id.v1.json +47 -0
  175. package/fixtures/obsidian/contracts/publication-journal/invalid/missing-protocol-id.v1.json +46 -0
  176. package/fixtures/obsidian/contracts/publication-journal/invalid/unknown-entry-field.v1.json +48 -0
  177. package/fixtures/obsidian/contracts/publication-journal/invalid/unknown-state.v1.json +47 -0
  178. package/fixtures/obsidian/contracts/publication-journal/valid/committed.v1.json +47 -0
  179. package/fixtures/obsidian/contracts/publication-journal/valid/updating-after-restart.v1.json +22 -0
  180. package/fixtures/obsidian/contracts/scope/invalid/absolute-path-prefix.v1.json +15 -0
  181. package/fixtures/obsidian/contracts/scope/invalid/duplicate-ids.v1.json +18 -0
  182. package/fixtures/obsidian/contracts/scope/invalid/expansion-without-node-budget.v1.json +40 -0
  183. package/fixtures/obsidian/contracts/scope/invalid/full-mode-with-subset.v1.json +8 -0
  184. package/fixtures/obsidian/contracts/scope/invalid/unknown-mode.v1.json +41 -0
  185. package/fixtures/obsidian/contracts/scope/invalid/unknown-selector-key.v1.json +15 -0
  186. package/fixtures/obsidian/contracts/scope/invalid/unknown-selector-operator.v1.json +14 -0
  187. package/fixtures/obsidian/contracts/scope/invalid/unknown-top-level-field.v1.json +42 -0
  188. package/fixtures/obsidian/contracts/scope/valid/explicit-empty.v1.json +8 -0
  189. package/fixtures/obsidian/contracts/scope/valid/focus.v1.json +11 -0
  190. package/fixtures/obsidian/contracts/scope/valid/full.v1.json +8 -0
  191. package/fixtures/obsidian/contracts/scope/valid/scoped-set-expression.v1.json +41 -0
  192. package/fixtures/obsidian/contracts/service-state/invalid/hostname-instead-of-literal-loopback.v1.json +23 -0
  193. package/fixtures/obsidian/contracts/service-state/invalid/missing-consent.v1.json +18 -0
  194. package/fixtures/obsidian/contracts/service-state/invalid/privileged-port.v1.json +23 -0
  195. package/fixtures/obsidian/contracts/service-state/invalid/unknown-top-level-field.v1.json +24 -0
  196. package/fixtures/obsidian/contracts/service-state/invalid/wildcard-bind.v1.json +23 -0
  197. package/fixtures/obsidian/contracts/service-state/valid/healthy.v1.json +23 -0
  198. package/fixtures/obsidian/contracts/source-snapshot/invalid/absolute-file-path.v1.json +48 -0
  199. package/fixtures/obsidian/contracts/source-snapshot/invalid/duplicate-repository-id.v1.json +48 -0
  200. package/fixtures/obsidian/contracts/source-snapshot/invalid/mixed-read.v1.json +48 -0
  201. package/fixtures/obsidian/contracts/source-snapshot/invalid/parent-traversal-path.v1.json +48 -0
  202. package/fixtures/obsidian/contracts/source-snapshot/invalid/unknown-file-field.v1.json +49 -0
  203. package/fixtures/obsidian/contracts/source-snapshot/valid/two-repositories.v1.json +48 -0
  204. package/fixtures/obsidian/edits/workspace.json +65 -0
  205. package/fixtures/obsidian/materialization/expected-full.json +99 -0
  206. package/fixtures/obsidian/materialization/expected-scoped.json +39 -0
  207. package/fixtures/obsidian/materialization/workspace.json +54 -0
  208. package/fixtures/obsidian/proposals/expected-shapes.json +11 -0
  209. package/fixtures/projects/sample-workspace/content/source.html.kg.json +4 -1
  210. package/fixtures/projects/source-formats-workspace/content/data.json.kg.json +4 -1
  211. package/fixtures/projects/source-formats-workspace/content/logo.png.kg.json +4 -1
  212. package/fixtures/projects/source-formats-workspace/content/metrics.csv.kg.json +4 -1
  213. package/fixtures/projects/source-formats-workspace/content/pipeline.yaml.kg.json +4 -1
  214. package/fixtures/ui/presentation/pane.v1.json +7 -0
  215. package/fixtures/ui/presentation/reference.v1.json +293 -0
  216. package/package.json +86 -10
  217. package/skills/claude/atelier-guided-coauthor/SKILL.md +49 -0
  218. package/skills/claude/atelier-guided-upgrade/SKILL.md +97 -0
  219. package/skills/claude/atelier-local-service/SKILL.md +47 -0
  220. package/skills/claude/atelier-open/SKILL.md +11 -5
  221. package/skills/claude/atelier-public-boundary/SKILL.md +31 -0
  222. package/skills/claude/atelier-skill-steward/SKILL.md +62 -0
  223. package/skills/claude/mnstry-readiness/SKILL.md +5 -0
  224. package/skills/codex/atelier-guided-coauthor/SKILL.md +49 -0
  225. package/skills/codex/atelier-guided-upgrade/SKILL.md +97 -0
  226. package/skills/codex/atelier-local-service/SKILL.md +47 -0
  227. package/skills/codex/atelier-open/SKILL.md +11 -5
  228. package/skills/codex/atelier-public-boundary/SKILL.md +31 -0
  229. package/skills/codex/atelier-skill-steward/SKILL.md +62 -0
  230. package/skills/codex/mnstry-readiness/SKILL.md +5 -0
  231. package/src/access/preview-enforcer.mjs +84 -0
  232. package/src/boundary/content-rules.mjs +283 -20
  233. package/src/boundary/policy.mjs +213 -91
  234. package/src/cli/execute-command.mjs +36 -0
  235. package/src/cli/project-options.mjs +58 -0
  236. package/src/cli/run.mjs +78 -10
  237. package/src/coauthor/session.mjs +161 -0
  238. package/src/coauthor/store.mjs +173 -0
  239. package/src/collaboration/event-ledger.mjs +348 -0
  240. package/src/collaboration/index.mjs +17 -0
  241. package/src/collaboration/inspection-bundle.mjs +223 -0
  242. package/src/collaboration/proposals.mjs +265 -65
  243. package/src/collaboration/review-contracts.mjs +25 -0
  244. package/src/collaboration/review-store.mjs +305 -0
  245. package/src/commands/attestation.mjs +20 -6
  246. package/src/commands/coauthor.mjs +26 -0
  247. package/src/commands/config.mjs +6 -1
  248. package/src/commands/disclosure.mjs +133 -0
  249. package/src/commands/distribution.mjs +2 -1
  250. package/src/commands/extension-pack.mjs +17 -9
  251. package/src/commands/init.mjs +46 -6
  252. package/src/commands/obsidian.mjs +371 -0
  253. package/src/commands/review.mjs +147 -0
  254. package/src/commands/server.mjs +1 -4
  255. package/src/commands/setup.mjs +51 -14
  256. package/src/commands/skills.mjs +199 -0
  257. package/src/commands/sync.mjs +107 -0
  258. package/src/composition/registry.mjs +27 -0
  259. package/src/composition/wire.mjs +122 -0
  260. package/src/contracts/corpus.mjs +35 -0
  261. package/src/decisions/contracts.d.mts +96 -0
  262. package/src/decisions/contracts.mjs +321 -0
  263. package/src/disclosure/content-scan.mjs +206 -0
  264. package/src/egress/check.mjs +7 -38
  265. package/src/egress/forbidden-egress.mjs +32 -18
  266. package/src/export/atelier-export-contract.mjs +16 -1
  267. package/src/extension-packs/lifecycle.mjs +154 -0
  268. package/src/graph/graph.mjs +176 -316
  269. package/src/graph/knowledge-graph.mjs +643 -43
  270. package/src/guides/contracts.mjs +54 -0
  271. package/src/harness/context-client.mjs +9 -1
  272. package/src/index.mjs +41 -0
  273. package/src/intake/store.mjs +119 -0
  274. package/src/preview/channel.mjs +89 -0
  275. package/src/project/config.mjs +112 -52
  276. package/src/project/durable-state.mjs +161 -0
  277. package/src/project/file-class.mjs +180 -0
  278. package/src/project/package-root.mjs +10 -0
  279. package/src/project/path-match.mjs +38 -15
  280. package/src/project/private-state.mjs +120 -0
  281. package/src/projection/obsidian/contracts.mjs +571 -0
  282. package/src/projection/obsidian/edits/align.mjs +169 -0
  283. package/src/projection/obsidian/edits/apply.mjs +889 -0
  284. package/src/projection/obsidian/edits/arbitrate.mjs +414 -0
  285. package/src/projection/obsidian/edits/contribution.mjs +108 -0
  286. package/src/projection/obsidian/edits/index.mjs +19 -0
  287. package/src/projection/obsidian/edits/object-identity.mjs +61 -0
  288. package/src/projection/obsidian/edits/object-store.mjs +517 -0
  289. package/src/projection/obsidian/edits/observe.mjs +123 -0
  290. package/src/projection/obsidian/edits/policy.mjs +123 -0
  291. package/src/projection/obsidian/edits/regions.mjs +648 -0
  292. package/src/projection/obsidian/materialize/byte-lens.mjs +98 -0
  293. package/src/projection/obsidian/materialize/index.mjs +7 -0
  294. package/src/projection/obsidian/materialize/path-registry.mjs +105 -0
  295. package/src/projection/obsidian/materialize/prepare-view.mjs +746 -0
  296. package/src/projection/obsidian/materialize/settings.mjs +57 -0
  297. package/src/projection/obsidian/materialize/stage.mjs +27 -0
  298. package/src/projection/obsidian/proposals/adapter.mjs +572 -0
  299. package/src/projection/obsidian/proposals/backpressure.mjs +113 -0
  300. package/src/projection/obsidian/proposals/contribution.mjs +55 -0
  301. package/src/projection/obsidian/proposals/index.mjs +23 -0
  302. package/src/projection/obsidian/proposals/observation.mjs +216 -0
  303. package/src/projection/obsidian/proposals/queue.mjs +277 -0
  304. package/src/projection/obsidian/proposals/router.mjs +168 -0
  305. package/src/projection/obsidian/publication/bridge-script.mjs +266 -0
  306. package/src/projection/obsidian/publication/exchange.mjs +117 -0
  307. package/src/projection/obsidian/publication/index.mjs +5 -0
  308. package/src/projection/obsidian/publication/publisher.mjs +588 -0
  309. package/src/projection/obsidian/publication/test-seam.mjs +16 -0
  310. package/src/projection/obsidian/publication/transport.mjs +212 -0
  311. package/src/projection/obsidian/recovery/index.mjs +6 -0
  312. package/src/projection/obsidian/recovery/journal.mjs +134 -0
  313. package/src/projection/obsidian/recovery/late-writer.mjs +46 -0
  314. package/src/projection/obsidian/recovery/restart.mjs +316 -0
  315. package/src/projection/obsidian/recovery/store.mjs +234 -0
  316. package/src/projection/obsidian/selection-ui/conflict-view.mjs +91 -0
  317. package/src/projection/obsidian/selection-ui/contribution.mjs +141 -0
  318. package/src/projection/obsidian/selection-ui/focus.mjs +82 -0
  319. package/src/projection/obsidian/selection-ui/index.mjs +12 -0
  320. package/src/projection/obsidian/selection-ui/policy-setup.mjs +107 -0
  321. package/src/projection/obsidian/selection-ui/receipt.mjs +173 -0
  322. package/src/projection/obsidian/selection-ui/selection-state.mjs +122 -0
  323. package/src/projection/obsidian/selection-ui/selection.mjs +109 -0
  324. package/src/projection/project.mjs +28 -19
  325. package/src/readiness-protocols/evidence.mjs +282 -0
  326. package/src/readiness-protocols/runtime.mjs +4 -3
  327. package/src/readiness-protocols/source-read.mjs +39 -0
  328. package/src/runtime/git-adapter.mjs +189 -0
  329. package/src/runtime/local-state.mjs +439 -0
  330. package/src/runtime/obsidian/app-capability.mjs +138 -0
  331. package/src/runtime/obsidian/app-production-seams.mjs +107 -0
  332. package/src/runtime/obsidian/contributions/proposal-adapter.mjs +4 -0
  333. package/src/runtime/obsidian/contributions/selection-ui.mjs +4 -0
  334. package/src/runtime/obsidian/contributions/source-apply.mjs +4 -0
  335. package/src/runtime/obsidian/contributions.mjs +27 -0
  336. package/src/runtime/obsidian/documents.mjs +37 -0
  337. package/src/runtime/obsidian/enablement.mjs +53 -0
  338. package/src/runtime/obsidian/engine.mjs +532 -0
  339. package/src/runtime/obsidian/errors.mjs +14 -0
  340. package/src/runtime/obsidian/extension-points.mjs +103 -0
  341. package/src/runtime/obsidian/index.mjs +34 -0
  342. package/src/runtime/obsidian/lifecycle.mjs +258 -0
  343. package/src/runtime/obsidian/machine-settings.mjs +260 -0
  344. package/src/runtime/obsidian/observation.mjs +158 -0
  345. package/src/runtime/obsidian/opening.mjs +280 -0
  346. package/src/runtime/obsidian/pending-edits.mjs +141 -0
  347. package/src/runtime/obsidian/pipeline.mjs +122 -0
  348. package/src/runtime/obsidian/private-lock.mjs +156 -0
  349. package/src/runtime/obsidian/process-identity.mjs +58 -0
  350. package/src/runtime/obsidian/service-client.mjs +66 -0
  351. package/src/runtime/obsidian/service-entry-path.mjs +9 -0
  352. package/src/runtime/obsidian/service-main.mjs +93 -0
  353. package/src/runtime/obsidian/service-record.mjs +169 -0
  354. package/src/runtime/obsidian/service-server.mjs +121 -0
  355. package/src/runtime/obsidian/service.mjs +216 -0
  356. package/src/runtime/obsidian/startup-adapters.mjs +87 -0
  357. package/src/runtime/obsidian/state-store.mjs +180 -0
  358. package/src/runtime/obsidian/sync-notice.mjs +33 -0
  359. package/src/runtime/obsidian/tick-loop.mjs +84 -0
  360. package/src/runtime/obsidian/watchers.mjs +36 -0
  361. package/src/runtime/repository-observation.mjs +491 -0
  362. package/src/runtime/supervisor.mjs +803 -0
  363. package/src/server/local-sidecar.mjs +125 -59
  364. package/src/server/security.mjs +89 -4
  365. package/src/server/server.mjs +4 -2
  366. package/src/skills/steward.mjs +780 -0
  367. package/src/support/feedback-report.mjs +4 -3
  368. package/src/ui/presentation/browser.mjs +278 -0
  369. package/src/ui/presentation/contract.mjs +116 -0
  370. package/src/ui/presentation/index.mjs +7 -0
  371. package/src/ui/presentation/native.mjs +153 -0
  372. package/src/ui/presentation/proof.mjs +57 -0
  373. package/src/ui/presentation/schema-check.mjs +30 -0
  374. package/src/ui/presentation/schema.generated.mjs +1371 -0
  375. package/src/ui/presentation/state.mjs +43 -0
  376. package/src/ui/presentation/styles.mjs +67 -0
  377. package/src/ui/presentation/tokens.mjs +74 -0
  378. package/src/ui/presentation/web.mjs +85 -0
  379. package/src/ui/review-page.mjs +32 -0
  380. package/src/upgrade/explanation.mjs +33 -0
  381. package/src/upgrade/provenance.mjs +338 -0
  382. package/src/upgrade/transaction-files.mjs +103 -0
  383. package/src/upgrade/transaction.mjs +475 -0
  384. package/src/upgrade/upgrade.mjs +65 -29
  385. package/src/vault/hosts.mjs +21 -0
  386. package/src/vault/identity.mjs +19 -0
  387. package/src/vault/index.mjs +7 -0
  388. package/src/vault/interface.mjs +11 -0
  389. package/src/vault/metadata.mjs +31 -0
  390. package/src/vault/privacy.mjs +148 -0
  391. package/src/vault/probe.mjs +89 -0
  392. package/src/vault/service.mjs +131 -0
  393. package/src/vault/source.mjs +45 -0
  394. package/src/vault/storage.mjs +23 -0
  395. package/templates/distribution-workspace/.claude/launch.json +6 -2
  396. package/templates/distribution-workspace/gitignore +1 -0
  397. package/templates/distribution-workspace/project/README.md +2 -2
  398. package/templates/external-project-workspace/README.md +45 -0
  399. package/templates/external-project-workspace/answers.example.json +4 -0
  400. package/templates/external-project-workspace/atelier.project.json +40 -0
  401. package/templates/external-project-workspace/boundary-policy.v1.json +60 -0
  402. package/templates/external-project-workspace/gitignore +8 -0
  403. package/templates/external-project-workspace/pack-lifecycle.json +14 -0
  404. package/templates/external-project-workspace/packs/protocols/contract-gate.v1.json +109 -0
  405. package/templates/external-project-workspace/packs/sample-pack.v1.json +48 -0
  406. package/templates/external-project-workspace/repo-access.v1.json +9 -0
  407. package/templates/external-project-workspace/source/README.md +11 -0
  408. package/templates/launch.json +6 -2
  409. package/templates/private-domain-workspace/.claude/launch.json +6 -2
  410. package/templates/private-domain-workspace/domain/README.md +2 -2
  411. package/templates/private-domain-workspace/gitignore +1 -0
  412. package/templates/shared-project-workspace/.claude/launch.json +6 -2
  413. package/templates/shared-project-workspace/gitignore +1 -0
  414. package/templates/shared-project-workspace/project/README.md +2 -2
@@ -0,0 +1,484 @@
1
+ # Obsidian projection: selection, apply policy and acceptance receipts
2
+
3
+ This document describes the selection binding, the apply policy setup, the
4
+ conflict-state view and the acceptance receipt validator under
5
+ `src/projection/obsidian/selection-ui/`. It states what each one does on this
6
+ machine and what it does not. The contract itself is in
7
+ [obsidian-contract.md](obsidian-contract.md).
8
+
9
+ Nothing in this module starts the app, runs `obsidian-cli`, publishes a vault
10
+ or writes a source file. The test suite installs a spawn guard that throws if
11
+ anything tries.
12
+
13
+ ## What is real and what is not
14
+
15
+ Real, on this machine, today:
16
+
17
+ - A declared view (`full`, `scoped` or `focus`, with an optional bounded
18
+ expansion) resolves to an exact scope document, the exact selected set, and
19
+ the note paths a vault of this workspace gives those notes.
20
+ - A focus resolves to a Graph "Search files" query naming every selected note
21
+ by its exact path, plus the payload of a core Bookmark that retains the
22
+ query.
23
+ - The resolved selection persists in Atelier's private workspace state and
24
+ reads back exactly.
25
+ - A manual or automatic apply policy is built, digested, validated and
26
+ installed through the runtime's installer; revocation is durable and is
27
+ re-read before every queued dispatch.
28
+ - A read-only view of the arbitration state answers which objects are
29
+ conflicted, pending, settled or unreadable and what a person does next.
30
+ - A receipt document for one of the gates G07 and G13 to G18 is checked for
31
+ shape and required fields.
32
+
33
+ Not real, and not claimed:
34
+
35
+ - Whether the installed app applies the focus query as built, indexes the
36
+ vault, or shows the graph the way the receipt says. That is acceptance
37
+ evidence from a running app (AP-01, AP-02), and nothing here produces it.
38
+ - Whether any gate is closed. `validateAcceptanceReceipt` answers
39
+ `gateClosed: false` on every input; its result is labelled
40
+ `schema-validation-only`.
41
+ - Any change to the files the app's UI owns under `.obsidian/`. Atelier never
42
+ writes `workspace.json`, `graph.json` or `bookmarks.json`.
43
+
44
+ ## Selection
45
+
46
+ `resolveSelection({ canonicalSnapshot, profile, scope, allowEmpty, pathRegistry })`
47
+ calls the contract's `selectScope` unchanged and adds no selector, default or
48
+ fallback. The answer carries:
49
+
50
+ - `scope`: the exact scope document (`atelier-obsidian-scope/v1`). The only
51
+ members filled in are the two expansion defaults the contract allows,
52
+ `direction: outgoing` and `order: canonical-id`. A budget is never
53
+ defaulted; an expansion without `depth` and `maxNodes` refuses with
54
+ `missing-expansion-budget`, and a budget out of range refuses with
55
+ `invalid-expansion`.
56
+ - `nodes`, `edges`, `outsideSelectionEdges`, `vaultNodes`, `vaultEdges`,
57
+ `truncated`, `unresolvedIds`, `diagnostics`: `selectScope`'s answer as is.
58
+ - `notePaths`: one vault-relative path per selected node, allocated by the
59
+ workspace path registry when the caller passes the persisted one, or by a
60
+ fresh allocation otherwise. Both are deterministic for the same inputs.
61
+ - `empty` and `emptyReason` (`explicit-empty` or `no-visible-members`). An
62
+ empty selection is refused with `selection-empty` unless the caller passes
63
+ `allowEmpty: true`; it is then reported empty, never widened.
64
+ - `focus`: `null` unless the mode is `focus`.
65
+
66
+ A withheld identity and an absent one are one answer, `unresolvedIds`, and
67
+ neither falls back to the whole corpus.
68
+
69
+ ### Focus
70
+
71
+ A focus keeps the full vault on disk and filters the native Graph view. The
72
+ query is
73
+
74
+ ```
75
+ path:"<note path>" OR path:"<note path>" ...
76
+ ```
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
82
+ path that carries a control character, a C1 control, or the U+2028 or U+2029
83
+ line separator cannot be a search term and refuses with
84
+ `focus-path-unrepresentable`; so does an absolute path, a backslash path or a
85
+ `..` segment. A focus with no selected note refuses with
86
+ `focus-selection-empty` even when the caller allows empty results: a filter
87
+ naming nothing would show the whole vault or nothing, and neither was asked
88
+ for.
89
+
90
+ The bookmark payload is `{ type: "graph", title: "Atelier focus <scope-id>",
91
+ options: { search: <query> } }`, the shape of a core-Bookmark graph entry.
92
+ The app assigns the creation time when the bookmark is made there. Only the
93
+ `graph` type is produced: core Bookmarks retain graph views, not local-graph
94
+ views.
95
+
96
+ Both values are applied by a person, an agent or the acceptance procedure in
97
+ the app. Atelier hands them over and writes nothing under `.obsidian/`.
98
+
99
+ ### Where the selector persists
100
+
101
+ `writeSelectionState` keeps the resolved selection at
102
+
103
+ ```
104
+ <data>/obsidian/<workspace-id>/state/selection/<scope-id>.json
105
+ ```
106
+
107
+ beside the maintenance and settings documents the runtime keeps there,
108
+ through the same private-state primitives (owner-only mode, atomic replace,
109
+ no symlink following) and the same guard that refuses private state inside an
110
+ enrolled repository. `assertWritableSelectionPath` refuses any target outside
111
+ `state/selection`, any target under `vaults/`, and any path with a `.obsidian`
112
+ segment. A rewrite that would change only the timestamp is not a change and
113
+ is not written. A stored document of another workspace, or one with a member
114
+ the shape does not know, refuses on read with `invalid-selection-state`.
115
+
116
+ ## Apply policy setup
117
+
118
+ `runPolicySetup({ action, input, workspace, repositoryRoots, now })` takes a
119
+ structured request and answers a structured document
120
+ (`atelier-obsidian-policy-setup/v1`, `ok: true|false`; a refusal is carried
121
+ as `refusal: { code, message, detail }`).
122
+
123
+ - `create`: builds `atelier-obsidian-apply-policy/v1` from
124
+ `{ policyId, mode, actor, selector, allowedEditClasses?, maxBatchSize?,
125
+ retryBudget?, version? }`, computes the canonical digest, validates the
126
+ document against the frozen contract and installs it through the runtime's
127
+ installer. `mode` is `manual` or `automatic`. Only an edit class with an
128
+ apply implementation can be allowed (`body-replacement` today); any other
129
+ refuses with `unimplemented-edit-class`. The conflict disposition is `hold`,
130
+ the only one that exists. Without an explicit `version`, the next revision
131
+ of an installed identity is one above it; an equal or lower version refuses
132
+ with `policy-version-not-newer` and installs nothing.
133
+ - `show`: the installed policy, the reference to it in the machine settings,
134
+ the maintenance mode, and whether an automatic apply would be authorized
135
+ right now.
136
+ - `revoke`: the stored policy is rewritten `revoked` first, then the
137
+ maintenance mode goes back to `manual`. Every later authorization read
138
+ denies with `apply-policy-revoked`, even when the mode is switched back to
139
+ automatic by hand.
140
+
141
+ Installing a policy never switches the maintenance mode. That is the person's
142
+ separate `mode set automatic`, which itself checks that an active automatic
143
+ policy is installed.
144
+
145
+ ### Revocation wins
146
+
147
+ The dispatch loop reads authorization from disk immediately before each
148
+ queued edit. A revocation that lands while one edit is being applied stops
149
+ the next one in the same batch: it is never dispatched, it stays `queued`,
150
+ and after a restart nothing is dispatched at all because the revocation is on
151
+ disk. `dispatchGate(workspace)` exposes that read in the form a caller sees.
152
+
153
+ ## Conflict view
154
+
155
+ `conflictView({ objects, edits, scopeId })` is a pure function of the object
156
+ entries (the object store's `list()`) and the pending edit records.
157
+ `readConflictView` reads both from the workspace's private state. The answer
158
+ (`atelier-obsidian-conflict-view/v1`) lists, per object, its identity, state
159
+ (`conflicted`, `pending`, `settled`, `empty`, `inconsistent`, `unreadable`),
160
+ source digest, whether a lease is held, whether an apply was started whose
161
+ outcome is not recorded, its operations with their states and reasons, the
162
+ conflicted operation keys, the open edits of the views that feed it, whether
163
+ a person is needed, and what happens next. No note text, title or path leaves
164
+ the view. The view decides nothing, chooses no winner and appends no event: a
165
+ conflict is resolved by a new operation that names the ones it resolves.
166
+
167
+ ## Acceptance receipt validation
168
+
169
+ `validateAcceptanceReceipt(receipt, { gate })` checks a document against the
170
+ `acceptance-receipt` schema and then against the rules of the named gate. The
171
+ answer carries `label: "schema-validation-only"`, `schemaValid`,
172
+ `requirementsMet`, `valid`, `outcome`, `evidenceType`, the list of
173
+ `missing` entries (`{ code, pointer, message }`), and always
174
+ `gateClosed: false` with `closes: "nothing"`. A failed or blocked receipt is
175
+ a valid receipt of its outcome; validity is not a pass.
176
+
177
+ A gate is closed by its owner inspecting the actual app, host or adopter
178
+ evidence and recording that in the parent matrix. A document that passes here
179
+ is an input to that inspection, never its result.
180
+
181
+ ### Required fields per gate
182
+
183
+ Every gate requires: `candidate.commit` (not the zero placeholder),
184
+ `candidate.treeDigest`, exact `environment.os.{name,version}`,
185
+ `environment.app.{name,version}` and `environment.cli.version` (a placeholder
186
+ such as `latest`, `unknown`, `n/a` or a blank refuses), every `evidence[]`
187
+ entry hashed with a positive `byteLength`, and `host.id` and `operator.id`
188
+ under `ext["mnstry.atelier.obsidian"]`. The extension is a closed shape:
189
+ `host`, `operator`, `evidenceRoles`, `acceptance`, `wallClock`, `dataset`.
190
+
191
+ | Gate | Procedure | Evidence type | Evidence roles (each names a hashed entry) | Also required |
192
+ | --- | --- | --- | --- | --- |
193
+ | G07 | AP-01 | `real-app` | `cli-link-inspection`, `app-observation` | human acceptance |
194
+ | G13 | AP-02 | `real-app` | `on-disk-membership`, `app-index-membership`, `graph-filter-observation` | human acceptance |
195
+ | G14 | AP-03 | `host` | `source-refresh-trace`, `dropped-event-recovery`, `sleep-wake-clock` | wall clock; no acceptance |
196
+ | G15 | AP-03 | `host` | `ownership-health`, `terminal-closure` | wall clock; no acceptance |
197
+ | G16 | AP-04 | `real-app` | `dataset-manifest`, `resource-samples`, `app-indexing-timings`, `warm-update-latencies` | wall clock; dataset (nodes, edges, fixture digest); no acceptance |
198
+ | G17 | AP-05 | `real-app` | `multi-vault-edit-trace`, `manual-apply-trace`, `automatic-apply-trace`, `uninstall-retention` | human acceptance |
199
+ | G18 | AP-06 | `human` | `tarball-audit`, `adopter-acceptance` | `candidate.tarballDigest`; adopter acceptance |
200
+
201
+ `procedureId` is the gate's procedure or a variant of it (`AP-02:run-3`); a
202
+ bare name of another procedure refuses. An acceptance is
203
+ `{ kind: human|adopter, actor, recordedAt, evidenceName }` and names its own
204
+ hashed evidence entry; an adopter acceptance recorded by the operator refuses
205
+ with `acceptance-not-separate`. A gate that records automation and host
206
+ evidence (G14, G15, G16) refuses an acceptance with
207
+ `acceptance-not-expected`. `receiptRequirementsFor(gate)` answers this table
208
+ as data.
209
+
210
+ Synthetic, schema-valid receipts for each gate are under
211
+ `fixtures/obsidian/acceptance/receipts/`. They name no real host, person or
212
+ machine path, and validating them closes nothing.
213
+
214
+ ## The command operations
215
+
216
+ `createSelectionContribution()` is a contribution `{ id, register({ operations }) }`
217
+ for the `obsidian` command's operation registry, the extension point the
218
+ runtime lays out. It registers:
219
+
220
+ - `selection resolve ID | persist ID [allow-empty] | show ID | list`
221
+ - `conflicts [ID]`
222
+ - `apply-policy create FILE | show | revoke`
223
+
224
+ The built-in `scope`, `policy` and `mode` names are reserved and untouched.
225
+ The production loader reads regular `.mjs` modules from
226
+ `src/runtime/obsidian/contributions/`, and that directory ships three one-line
227
+ modules: `source-apply.mjs` (`apply`), `proposal-adapter.mjs` (`proposals`)
228
+ and `selection-ui.mjs` (`selection`, `conflicts`, `apply-policy`). The shipped
229
+ `atelier obsidian` command loads them and dispatches every registered
230
+ operation with the discipline of the built-ins: noninteractive, one JSON
231
+ document under `--json`, exit 2 with a typed code for a refusal or a usage
232
+ error, exit 3 when the operation ran and its answer is not success.
233
+ `atelier obsidian status` lists the registered operations under
234
+ `operations`; `atelier obsidian --help` names them.
235
+
236
+ A manual apply on a real workspace, from the shipped command:
237
+
238
+ ```sh
239
+ atelier obsidian apply list --project atelier.project.json --json
240
+ atelier obsidian apply show EDIT --project atelier.project.json --json
241
+ atelier obsidian apply run EDIT --actor ID --project atelier.project.json --json
242
+ ```
243
+
244
+ `--actor` is an option of `apply run` only; every other operation refuses it
245
+ as a usage error. `apply run` exits 0 when the edit was applied and 3 when it
246
+ ran and the answer is anything else; nothing is staged or committed either
247
+ way. A contribution registers which shared options it takes by naming them
248
+ under `options`; one that names none refuses `--actor` before it runs.
249
+
250
+ ## Package entry points
251
+
252
+ The published package declares these subpaths. Each is the module named, and
253
+ nothing under `scripts/obsidian/` ships.
254
+
255
+ | Subpath | Module |
256
+ | --- | --- |
257
+ | `@mnstry/atelier/obsidian` | `src/runtime/obsidian/index.mjs`: enablement, machine settings, engine, service lifecycle, production seams, app qualification |
258
+ | `@mnstry/atelier/obsidian/contracts` | `src/projection/obsidian/contracts.mjs`: contract validation, `selectScope`, note paths |
259
+ | `@mnstry/atelier/obsidian/materialize` | `prepareView`, settings, path registry |
260
+ | `@mnstry/atelier/obsidian/publication` | `publishView`, editor adapters, the exchange |
261
+ | `@mnstry/atelier/obsidian/recovery` | recovery store, journals, late-writer recheck |
262
+ | `@mnstry/atelier/obsidian/edits` | edit observation, arbitration, apply policy, source apply |
263
+ | `@mnstry/atelier/obsidian/proposals` | proposal queue, router and adapter |
264
+ | `@mnstry/atelier/obsidian/selection` | this document's module, `src/projection/obsidian/selection-ui/index.mjs` |
265
+
266
+ Every `contracts/atelier-obsidian-*.v1.schema.json` is exported under its own
267
+ path. The package root (`@mnstry/atelier`) exports nothing of the projection.
268
+ Names ending `ForOracleTests` are mutation controls for the test suite, not a
269
+ supported API. The whole surface is alpha and may change between prereleases.
270
+ What the tarball must and must not carry, and the package proof, are in
271
+ [release-engineering.md](release-engineering.md#obsidian-package-contents).
272
+
273
+ ## First vault: what to expect
274
+
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.
278
+
279
+ 1. Enable the projection in the project configuration. The member
280
+ `ext["mnstry.atelier.obsidian"]` is an `atelier-obsidian-ext-settings/v1`
281
+ document: `enabled: true`, `scopes` (each with a `scopeId`, a `mode` of
282
+ `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.
286
+ 2. Set the private machine settings. They live outside every repository and
287
+ 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.
291
+ - `atelier obsidian mode set manual` keeps every queued edit waiting for a
292
+ person. `mode set automatic` is refused until an active automatic policy
293
+ is installed.
294
+ - `atelier obsidian policy digest FILE` prints the digest the policy file
295
+ has to carry; `policy install FILE` installs it and leaves the mode
296
+ unchanged; `policy revoke` returns the mode to `manual` and stops every
297
+ 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).
311
+
312
+ `open` and `status` answer with a freshness state, not a promise. `current`
313
+ means the vault is the present generation, verified by read-back, and the app
314
+ has it open. `updating` means maintenance is publishing or has not finished.
315
+ `stale-readable` means a last good vault exists but is not proven current;
316
+ `open --allow-stale` opens it as it is. `not-prepared` means no generation has
317
+ been published yet. `held-for-your-edit` means a note you edited is preserved
318
+ and the view is not republished over it. The remaining outcomes (`indexing`,
319
+ `publisher-conflict`, `app-missing`, `app-version-unsupported`,
320
+ `app-cli-unavailable`, `launch-failed`, `service-unavailable`, `busy`,
321
+ `disabled`) each name what to do next, and exit code 3 says the answer is not
322
+ success.
323
+
324
+ Edits made in the vault are preserved before anything is republished. A body
325
+ replacement is held as a pending edit; under manual mode it reaches its source
326
+ file only through `atelier obsidian apply run EDIT [--actor ID]`, after
327
+ `apply list` and `apply show EDIT` named it. An applied edit changes the source
328
+ file and nothing is staged or committed. Under automatic mode the engine
329
+ applies body replacements itself under the installed policy, and revocation is
330
+ read again before every one. An edit that is not a body replacement, such as a
331
+ new or changed link to another note or an edited front matter, becomes a
332
+ copy-only proposal in the owning repository's proposal store; `atelier obsidian
333
+ proposals list` and `proposals show OPERATION` read them, and nothing applies
334
+ one. A body edit to a note whose source uses CRLF line endings is also a
335
+ proposal, never an apply, because the app normalizes line endings on save.
336
+
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).
344
+
345
+ ### First publication while Obsidian is running
346
+
347
+ The publisher writes into a vault only when it can coordinate with every
348
+ Obsidian that may hold it, or when the process table shows, positively, that
349
+ none runs. Otherwise it stops, and the view reports `publisher-conflict` with
350
+ reason `editor-uncoordinated`. That reason has several causes, and the report
351
+ does not say which: Obsidian runs without this vault open, or with it open but
352
+ its command line did not answer; the process table could not be read, or on
353
+ 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.
373
+
374
+ ## Known limits
375
+
376
+ These are the limits known at this release. None is hidden behind a skipped
377
+ test reported as a pass.
378
+
379
+ - Windows: no atomic file exchange is known, so the publisher refuses with
380
+ `exchange-unsupported-platform` and publishes nothing, and source apply
381
+ refuses for the same reason. Selection, policy setup, `prepareView` and
382
+ receipt validation work. The tests that publish are skipped there and say
383
+ why.
384
+ - Linux: the exchange primitive was proven in a container on aarch64. The
385
+ real-app suite (the publication interleavings and procedures AP-01 to AP-05)
386
+ has not been run on Linux. x86_64 has not been run on any system.
387
+ - macOS: the exchange is a raw system call reached through the system perl. The
388
+ perl binary is used only when uid 0 owns it and neither group nor others can
389
+ write it; otherwise publication refuses with
390
+ `exchange-interpreter-untrusted`. A machine without the stock perl cannot
391
+ publish.
392
+ - App version: `MINIMUM_APP_VERSION` in
393
+ `src/runtime/obsidian/app-capability.mjs` is 1.13.7, the only version the
394
+ publication protocol was proven on. An older, unreadable or unknown version
395
+ is `app-version-unsupported`. There is a floor and no ceiling: a newer app
396
+ is admitted although the protocol depends on the view's undocumented
397
+ `lastSavedData` field, and the protocol cases have not been re-run on any
398
+ later release. See the "App capability floor" row of
399
+ [obsidian-contract.md](obsidian-contract.md). An app that was not running
400
+ when an adapter was qualified had no version to check, and neither had an
401
+ app that was not found. Such an answer is never reused, and the adapter
402
+ built on it does not coordinate with any app it then finds running, and
403
+ asks it nothing. That publication stops as `publisher-conflict` with reason
404
+ `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.
407
+ - Install location (macOS): the app is found only at
408
+ `/Applications/Obsidian.app`, and its command-line tool only inside it. An
409
+ Obsidian installed elsewhere, or a renamed bundle, is `app-missing`.
410
+ Nothing is published through it, publication waits while it runs (the
411
+ process probe still sees it), and `open` answers `app-missing` and cannot
412
+ start it. With it quit, the view is published on the path with no app.
413
+ - No vault open: while Obsidian runs with no vault open, its command-line tool
414
+ answers every command, `version` included, with `Vault not found.`. The
415
+ version floor cannot be checked then, so the app does not qualify:
416
+ `app-version-unsupported` with reason `no-vault-open`, and nothing is
417
+ published through the app. `status` (under `service.app`) and `open` say what
418
+ to do: open any vault in Obsidian, or quit Obsidian. With the app quit,
419
+ 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.
423
+ - Detecting the app: the process table is read with `ps -A -o comm=`
424
+ (`pid=,comm=` on Linux), and the app is recognised by the executable a
425
+ process runs, never by its arguments, so a path argument that contains an
426
+ `obsidian` directory (the maintenance service itself, anything under the
427
+ private data root) is not the app. A `ps` that has not answered within five
428
+ seconds is killed and the reading is `unknown`, which is treated as a
429
+ running app.
430
+ - macOS: the executable `Obsidian` and the bundle's `Obsidian Helper`
431
+ processes count, in any directory. An app started through a differently
432
+ named link shows that name for its main process; its `Obsidian Helper`
433
+ processes still count.
434
+ - Linux: a process named `obsidian` counts, which covers the .deb, snap,
435
+ AppImage and Flatpak builds. Linux names a process after the path it was
436
+ started through, so for a name that is not recognised the probe reads
437
+ the executable `/proc/<pid>/exe` resolves to: an app started through a
438
+ link or launcher named `obs`, or a child started through
439
+ `/proc/self/exe` whose executable is readable, still counts. A process
440
+ whose executable this user may not read (another user's, or one of this
441
+ user's that hides it from tracing, such as ssh-agent) or that exited
442
+ meanwhile is judged by its name alone. Any other read failure, or a
443
+ table in which no executable resolves at all, is `unknown`. A
444
+ distribution that runs the app under a system Electron shows only
445
+ `electron`: the table is then `unknown`, so publication through the
446
+ app-free path waits until no such process runs.
447
+ - The command-line tool (`obsidian-cli`) is a client of the app and is not
448
+ counted. An app packaged under another executable name, an app on
449
+ another machine and an app that starts after a reading are not seen; see
450
+ "The path with no app" in [obsidian-contract.md](obsidian-contract.md).
451
+ - The app-free path is live: before 0.2.0-alpha.10 the probe always found
452
+ Atelier's own service, so publication never took the path with no app.
453
+ From 0.2.0-alpha.10 it does, whenever the process table shows, positively,
454
+ that no Obsidian runs. It applies only then. The table is read at path
455
+ selection, again immediately before the first note, and again whenever two
456
+ seconds have passed. An app that starts between two readings is not seen
457
+ for at most two seconds plus one note's publication, and in that window a
458
+ note is exchanged with no editor check. Bytes on disk stay protected by the
459
+ on-disk comparison and the exchange. An unsaved buffer in the newly started
460
+ app does not: the app applies its own external-modification merge, which
461
+ can drop overlapping edits. Once a reading shows an app, every remaining
462
+ note refuses. To avoid the window, do not start Obsidian while a view is
463
+ being published with the app closed.
464
+ - CRLF sources: the editor normalizes line endings when it saves, so a body
465
+ edit made in the vault to a note whose source uses CRLF is more than a body
466
+ replacement to the edit lens. It is preserved and becomes a proposal; it is
467
+ never applied to the source, in manual or automatic mode. Edit such a note
468
+ in its repository.
469
+ - An edit the byte lens cannot turn into source bytes (a new or changed link
470
+ to another note of the vault, an edited front matter) becomes a copy-only
471
+ proposal. No operation applies one.
472
+ - Acceptance: a schema-valid receipt closes no gate, the package proof closes
473
+ no gate, and no adopter acceptance is recorded in this repository.
474
+
475
+ ## Mutation controls
476
+
477
+ The test suite proves each oracle can fail:
478
+
479
+ - `createFocusQueryBuilderForOracleTests` accepts a builder with escaping,
480
+ quoting or joining switched off; the escaping oracle fails on exactly the
481
+ cases that depend on the switched-off step.
482
+ - `createReceiptValidatorForOracleTests` accepts a rule table with one rule
483
+ switched off; every negative case that depends on that rule then passes,
484
+ and even that closes nothing.