@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.
- package/CHANGELOG.md +413 -0
- package/README.md +402 -205
- package/SECURITY.md +6 -1
- package/contracts/atelier-adoption-policy.v1.schema.json +49 -0
- package/contracts/atelier-coauthor.v1.schema.json +163 -0
- package/contracts/atelier-decision-request.v1.schema.json +476 -0
- package/contracts/atelier-decision-result.v1.schema.json +500 -0
- package/contracts/atelier-guide.v1.schema.json +227 -0
- package/contracts/atelier-intake.v1.schema.json +151 -0
- package/contracts/atelier-migration.v2.schema.json +61 -0
- package/contracts/atelier-obsidian-acceptance-receipt.v1.schema.json +211 -0
- package/contracts/atelier-obsidian-apply-policy.v1.schema.json +298 -0
- package/contracts/atelier-obsidian-corpus-profile.v1.schema.json +98 -0
- package/contracts/atelier-obsidian-edit-operation.v1.schema.json +138 -0
- package/contracts/atelier-obsidian-ext-settings.v1.schema.json +287 -0
- package/contracts/atelier-obsidian-generation-manifest.v1.schema.json +353 -0
- package/contracts/atelier-obsidian-proposal-receipt.v1.schema.json +110 -0
- package/contracts/atelier-obsidian-publication-journal.v1.schema.json +167 -0
- package/contracts/atelier-obsidian-scope.v1.schema.json +264 -0
- package/contracts/atelier-obsidian-service-state.v1.schema.json +149 -0
- package/contracts/atelier-obsidian-source-snapshot.v1.schema.json +179 -0
- package/contracts/atelier-pane-presentation.v1.schema.json +76 -0
- package/contracts/atelier-presentation.v1.schema.json +1293 -0
- package/contracts/atelier-repository-observation.v1.schema.json +163 -0
- package/contracts/atelier-review.v1.schema.json +1601 -0
- package/contracts/atelier-skill-steward.v1.schema.json +692 -0
- package/contracts/atelier-upgrade-plan.v2.schema.json +398 -0
- package/contracts/atelier-upgrade-receipt.v1.schema.json +99 -0
- package/contracts/atelier-vault-publication.v1.schema.json +58 -0
- package/contracts/public-api-baseline.json +57 -0
- package/docs/assurance-controls.md +49 -0
- package/docs/atelier-runtime.md +28 -2
- package/docs/atelier-sync.md +172 -0
- package/docs/blocks/audience-visibility.md +9 -0
- package/docs/blocks/claims.md +61 -0
- package/docs/blocks/conformance-admission.md +15 -0
- package/docs/blocks/will-not-do.md +18 -0
- package/docs/coauthor-session.md +57 -0
- package/docs/connected-composition.md +75 -0
- package/docs/continuity.md +3 -4
- package/docs/decisions.md +167 -0
- package/docs/design.md +167 -0
- package/docs/exact-upgrades.md +159 -0
- package/docs/extension-pack-lifecycle.md +33 -0
- package/docs/guided-upgrades.md +99 -0
- package/docs/install.md +99 -16
- package/docs/intake-and-guides.md +65 -0
- package/docs/integration-and-review-plan.md +409 -0
- package/docs/integration-contract-decisions.md +22 -0
- package/docs/knowledge-graph.md +8 -4
- package/docs/local-review.md +84 -0
- package/docs/local-services.md +328 -0
- package/docs/local-state.md +107 -0
- package/docs/obsidian-contract.md +698 -0
- package/docs/obsidian.md +484 -0
- package/docs/portable-presentation.md +328 -0
- package/docs/presentation-consumer-boundaries.md +75 -0
- package/docs/presentation-review-disposition.md +47 -0
- package/docs/project-command-inventory.md +38 -0
- package/docs/project-options.md +53 -0
- package/docs/release-engineering.md +174 -12
- package/docs/repo-boundary-guard.md +12 -2
- package/docs/review-portability.md +55 -0
- package/docs/skill-steward.md +156 -0
- package/docs/upgrade.md +155 -5
- package/docs/vault-service.md +292 -0
- package/fixtures/atelier-coauthor/invalid/authority.v1.json +5 -0
- package/fixtures/atelier-coauthor/valid/config.v1.json +12 -0
- package/fixtures/atelier-guide/invalid/authority.v1.json +11 -0
- package/fixtures/atelier-guide/valid/capability.v1.json +10 -0
- package/fixtures/atelier-guide/valid/consent.v1.json +11 -0
- package/fixtures/atelier-guide/valid/engagement.v1.json +7 -0
- package/fixtures/atelier-guide/valid/offer.v1.json +10 -0
- package/fixtures/atelier-intake/invalid/authority.v1.json +9 -0
- package/fixtures/atelier-intake/valid/attempt.v1.json +8 -0
- package/fixtures/atelier-intake/valid/completion.v1.json +9 -0
- package/fixtures/atelier-intake/valid/source.v1.json +7 -0
- package/fixtures/atelier-repository-observation/invalid/complete-with-blocker.v1.json +18 -0
- package/fixtures/atelier-repository-observation/valid/complete-local.v1.json +48 -0
- package/fixtures/atelier-review/invalid/authenticated-identity.v1.json +22 -0
- package/fixtures/atelier-review/valid/bound.v1.json +229 -0
- package/fixtures/atelier-review/valid/bundle.v1.json +267 -0
- package/fixtures/atelier-review/valid/contribution.v1.json +22 -0
- package/fixtures/atelier-review/valid/lifecycle.v1.json +14 -0
- package/fixtures/atelier-skill-steward/audit/invalid/source-mutation.v1.json +26 -0
- package/fixtures/atelier-skill-steward/audit/valid/clean.v1.json +26 -0
- package/fixtures/atelier-skill-steward/candidates/invalid/workflow-key.v1.json +37 -0
- package/fixtures/atelier-skill-steward/candidates/valid/create.v1.json +37 -0
- package/fixtures/atelier-skill-steward/lock/invalid/bad-digest.v1.json +18 -0
- package/fixtures/atelier-skill-steward/lock/valid/installed.v1.json +23 -0
- package/fixtures/atelier-skill-steward/plan/invalid/delete-action.v1.json +33 -0
- package/fixtures/atelier-skill-steward/plan/valid/add.v1.json +33 -0
- package/fixtures/atelier-upgrade-transaction/adoption-policy/invalid/example.json +12 -0
- package/fixtures/atelier-upgrade-transaction/adoption-policy/valid/example.json +11 -0
- package/fixtures/atelier-upgrade-transaction/migration/invalid/example.json +20 -0
- package/fixtures/atelier-upgrade-transaction/migration/valid/example.json +19 -0
- package/fixtures/atelier-upgrade-transaction/upgrade-plan/invalid/example.json +58 -0
- package/fixtures/atelier-upgrade-transaction/upgrade-plan/valid/example.json +57 -0
- package/fixtures/atelier-upgrade-transaction/upgrade-receipt/invalid/example.json +15 -0
- package/fixtures/atelier-upgrade-transaction/upgrade-receipt/valid/example.json +14 -0
- package/fixtures/atelier-vault-publication/invalid/publication.json +5 -0
- package/fixtures/atelier-vault-publication/valid/publication.json +11 -0
- package/fixtures/decisions/request/invalid/unknown-authority.v1.json +61 -0
- package/fixtures/decisions/request/valid/triage.v1.json +60 -0
- package/fixtures/decisions/result/invalid/execution-authority.v1.json +50 -0
- package/fixtures/decisions/result/valid/abstained.v1.json +23 -0
- package/fixtures/decisions/result/valid/assessed.v1.json +50 -0
- package/fixtures/obsidian/acceptance/ap05-extra-notes.json +14 -0
- package/fixtures/obsidian/acceptance/receipts/G07.valid.v1.json +63 -0
- package/fixtures/obsidian/acceptance/receipts/G13.valid.v1.json +69 -0
- package/fixtures/obsidian/acceptance/receipts/G14.valid.v1.json +62 -0
- package/fixtures/obsidian/acceptance/receipts/G15.valid.v1.json +56 -0
- package/fixtures/obsidian/acceptance/receipts/G16.valid.v1.json +73 -0
- package/fixtures/obsidian/acceptance/receipts/G17.valid.v1.json +75 -0
- package/fixtures/obsidian/acceptance/receipts/G18.valid.v1.json +64 -0
- package/fixtures/obsidian/acceptance/service-sentinels.json +10 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/duplicate-evidence-name.v1.json +39 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/evidence-by-absolute-path.v1.json +34 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/missing-candidate-tree.v1.json +33 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/no-evidence.v1.json +28 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/unknown-outcome.v1.json +34 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/unknown-top-level-field.v1.json +35 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/valid/node-gate.v1.json +34 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/automatic-without-edit-class.v1.json +27 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/conflict-overwrite.v1.json +29 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/missing-policy-digest.v1.json +28 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/unbounded-batch.v1.json +29 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/unimplemented-edit-class.v1.json +30 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/unknown-mode.v1.json +29 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/unknown-selector-operator.v1.json +22 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/unknown-top-level-field.v1.json +30 -0
- package/fixtures/obsidian/contracts/apply-policy/valid/automatic-scoped.v1.json +29 -0
- package/fixtures/obsidian/contracts/apply-policy/valid/manual-default.v1.json +20 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/absolute-path-in-ext.v1.json +30 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/absolute-repository-root.v1.json +27 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/duplicate-repository-id.v1.json +27 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/overlapping-managed-roots.v1.json +27 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/unknown-enrollment.v1.json +27 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/unknown-top-level-field.v1.json +28 -0
- package/fixtures/obsidian/contracts/corpus-profile/valid/three-repositories.v1.json +27 -0
- package/fixtures/obsidian/contracts/edit-operation/invalid/absolute-recovery-ref.v1.json +21 -0
- package/fixtures/obsidian/contracts/edit-operation/invalid/inline-observed-bytes.v1.json +22 -0
- package/fixtures/obsidian/contracts/edit-operation/invalid/missing-base-digest.v1.json +20 -0
- package/fixtures/obsidian/contracts/edit-operation/invalid/short-idempotency-key.v1.json +21 -0
- package/fixtures/obsidian/contracts/edit-operation/invalid/unknown-kind.v1.json +21 -0
- package/fixtures/obsidian/contracts/edit-operation/valid/pending-body-replacement.v1.json +21 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/absolute-path-prefix.v1.json +28 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/apply-policy-in-portable-settings.v1.json +30 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/duplicate-scope-id.v1.json +27 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/machine-local-vault-path.v1.json +28 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/service-port-in-portable-settings.v1.json +28 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/unknown-default-scope.v1.json +27 -0
- package/fixtures/obsidian/contracts/ext-settings/valid/disabled.v1.json +5 -0
- package/fixtures/obsidian/contracts/ext-settings/valid/enabled-with-scopes.v1.json +27 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/absolute-note-path.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/complete-with-unwritten-notes.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/derived-link-claimed-declared.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/duplicate-note-identity.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/in-scope-endpoint-missing.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/title-only-note-path.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/unknown-note-field.v1.json +115 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/unknown-relation-type.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/withheld-endpoint.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/valid/two-notes.v1.json +114 -0
- package/fixtures/obsidian/contracts/oracles/scope-cases.json +412 -0
- package/fixtures/obsidian/contracts/proposal-receipt/invalid/accepted-without-proposal-id.v1.json +12 -0
- package/fixtures/obsidian/contracts/proposal-receipt/invalid/missing-adapter-operation-id.v1.json +11 -0
- package/fixtures/obsidian/contracts/proposal-receipt/invalid/unknown-backpressure-outcome.v1.json +12 -0
- package/fixtures/obsidian/contracts/proposal-receipt/invalid/unknown-top-level-field.v1.json +13 -0
- package/fixtures/obsidian/contracts/proposal-receipt/valid/accepted-new.v1.json +12 -0
- package/fixtures/obsidian/contracts/proposal-receipt/valid/deferred.v1.json +12 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/absolute-recovery-ref.v1.json +47 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/duplicate-sequence.v1.json +47 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/empty-protocol-id.v1.json +47 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/missing-protocol-id.v1.json +46 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/unknown-entry-field.v1.json +48 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/unknown-state.v1.json +47 -0
- package/fixtures/obsidian/contracts/publication-journal/valid/committed.v1.json +47 -0
- package/fixtures/obsidian/contracts/publication-journal/valid/updating-after-restart.v1.json +22 -0
- package/fixtures/obsidian/contracts/scope/invalid/absolute-path-prefix.v1.json +15 -0
- package/fixtures/obsidian/contracts/scope/invalid/duplicate-ids.v1.json +18 -0
- package/fixtures/obsidian/contracts/scope/invalid/expansion-without-node-budget.v1.json +40 -0
- package/fixtures/obsidian/contracts/scope/invalid/full-mode-with-subset.v1.json +8 -0
- package/fixtures/obsidian/contracts/scope/invalid/unknown-mode.v1.json +41 -0
- package/fixtures/obsidian/contracts/scope/invalid/unknown-selector-key.v1.json +15 -0
- package/fixtures/obsidian/contracts/scope/invalid/unknown-selector-operator.v1.json +14 -0
- package/fixtures/obsidian/contracts/scope/invalid/unknown-top-level-field.v1.json +42 -0
- package/fixtures/obsidian/contracts/scope/valid/explicit-empty.v1.json +8 -0
- package/fixtures/obsidian/contracts/scope/valid/focus.v1.json +11 -0
- package/fixtures/obsidian/contracts/scope/valid/full.v1.json +8 -0
- package/fixtures/obsidian/contracts/scope/valid/scoped-set-expression.v1.json +41 -0
- package/fixtures/obsidian/contracts/service-state/invalid/hostname-instead-of-literal-loopback.v1.json +23 -0
- package/fixtures/obsidian/contracts/service-state/invalid/missing-consent.v1.json +18 -0
- package/fixtures/obsidian/contracts/service-state/invalid/privileged-port.v1.json +23 -0
- package/fixtures/obsidian/contracts/service-state/invalid/unknown-top-level-field.v1.json +24 -0
- package/fixtures/obsidian/contracts/service-state/invalid/wildcard-bind.v1.json +23 -0
- package/fixtures/obsidian/contracts/service-state/valid/healthy.v1.json +23 -0
- package/fixtures/obsidian/contracts/source-snapshot/invalid/absolute-file-path.v1.json +48 -0
- package/fixtures/obsidian/contracts/source-snapshot/invalid/duplicate-repository-id.v1.json +48 -0
- package/fixtures/obsidian/contracts/source-snapshot/invalid/mixed-read.v1.json +48 -0
- package/fixtures/obsidian/contracts/source-snapshot/invalid/parent-traversal-path.v1.json +48 -0
- package/fixtures/obsidian/contracts/source-snapshot/invalid/unknown-file-field.v1.json +49 -0
- package/fixtures/obsidian/contracts/source-snapshot/valid/two-repositories.v1.json +48 -0
- package/fixtures/obsidian/edits/workspace.json +65 -0
- package/fixtures/obsidian/materialization/expected-full.json +99 -0
- package/fixtures/obsidian/materialization/expected-scoped.json +39 -0
- package/fixtures/obsidian/materialization/workspace.json +54 -0
- package/fixtures/obsidian/proposals/expected-shapes.json +11 -0
- package/fixtures/projects/sample-workspace/content/source.html.kg.json +4 -1
- package/fixtures/projects/source-formats-workspace/content/data.json.kg.json +4 -1
- package/fixtures/projects/source-formats-workspace/content/logo.png.kg.json +4 -1
- package/fixtures/projects/source-formats-workspace/content/metrics.csv.kg.json +4 -1
- package/fixtures/projects/source-formats-workspace/content/pipeline.yaml.kg.json +4 -1
- package/fixtures/ui/presentation/pane.v1.json +7 -0
- package/fixtures/ui/presentation/reference.v1.json +293 -0
- package/package.json +86 -10
- package/skills/claude/atelier-guided-coauthor/SKILL.md +49 -0
- package/skills/claude/atelier-guided-upgrade/SKILL.md +97 -0
- package/skills/claude/atelier-local-service/SKILL.md +47 -0
- package/skills/claude/atelier-open/SKILL.md +11 -5
- package/skills/claude/atelier-public-boundary/SKILL.md +31 -0
- package/skills/claude/atelier-skill-steward/SKILL.md +62 -0
- package/skills/claude/mnstry-readiness/SKILL.md +5 -0
- package/skills/codex/atelier-guided-coauthor/SKILL.md +49 -0
- package/skills/codex/atelier-guided-upgrade/SKILL.md +97 -0
- package/skills/codex/atelier-local-service/SKILL.md +47 -0
- package/skills/codex/atelier-open/SKILL.md +11 -5
- package/skills/codex/atelier-public-boundary/SKILL.md +31 -0
- package/skills/codex/atelier-skill-steward/SKILL.md +62 -0
- package/skills/codex/mnstry-readiness/SKILL.md +5 -0
- package/src/access/preview-enforcer.mjs +84 -0
- package/src/boundary/content-rules.mjs +283 -20
- package/src/boundary/policy.mjs +213 -91
- package/src/cli/execute-command.mjs +36 -0
- package/src/cli/project-options.mjs +58 -0
- package/src/cli/run.mjs +78 -10
- package/src/coauthor/session.mjs +161 -0
- package/src/coauthor/store.mjs +173 -0
- package/src/collaboration/event-ledger.mjs +348 -0
- package/src/collaboration/index.mjs +17 -0
- package/src/collaboration/inspection-bundle.mjs +223 -0
- package/src/collaboration/proposals.mjs +265 -65
- package/src/collaboration/review-contracts.mjs +25 -0
- package/src/collaboration/review-store.mjs +305 -0
- package/src/commands/attestation.mjs +20 -6
- package/src/commands/coauthor.mjs +26 -0
- package/src/commands/config.mjs +6 -1
- package/src/commands/disclosure.mjs +133 -0
- package/src/commands/distribution.mjs +2 -1
- package/src/commands/extension-pack.mjs +17 -9
- package/src/commands/init.mjs +46 -6
- package/src/commands/obsidian.mjs +371 -0
- package/src/commands/review.mjs +147 -0
- package/src/commands/server.mjs +1 -4
- package/src/commands/setup.mjs +51 -14
- package/src/commands/skills.mjs +199 -0
- package/src/commands/sync.mjs +107 -0
- package/src/composition/registry.mjs +27 -0
- package/src/composition/wire.mjs +122 -0
- package/src/contracts/corpus.mjs +35 -0
- package/src/decisions/contracts.d.mts +96 -0
- package/src/decisions/contracts.mjs +321 -0
- package/src/disclosure/content-scan.mjs +206 -0
- package/src/egress/check.mjs +7 -38
- package/src/egress/forbidden-egress.mjs +32 -18
- package/src/export/atelier-export-contract.mjs +16 -1
- package/src/extension-packs/lifecycle.mjs +154 -0
- package/src/graph/graph.mjs +176 -316
- package/src/graph/knowledge-graph.mjs +643 -43
- package/src/guides/contracts.mjs +54 -0
- package/src/harness/context-client.mjs +9 -1
- package/src/index.mjs +41 -0
- package/src/intake/store.mjs +119 -0
- package/src/preview/channel.mjs +89 -0
- package/src/project/config.mjs +112 -52
- package/src/project/durable-state.mjs +161 -0
- package/src/project/file-class.mjs +180 -0
- package/src/project/package-root.mjs +10 -0
- package/src/project/path-match.mjs +38 -15
- package/src/project/private-state.mjs +120 -0
- package/src/projection/obsidian/contracts.mjs +571 -0
- package/src/projection/obsidian/edits/align.mjs +169 -0
- package/src/projection/obsidian/edits/apply.mjs +889 -0
- package/src/projection/obsidian/edits/arbitrate.mjs +414 -0
- package/src/projection/obsidian/edits/contribution.mjs +108 -0
- package/src/projection/obsidian/edits/index.mjs +19 -0
- package/src/projection/obsidian/edits/object-identity.mjs +61 -0
- package/src/projection/obsidian/edits/object-store.mjs +517 -0
- package/src/projection/obsidian/edits/observe.mjs +123 -0
- package/src/projection/obsidian/edits/policy.mjs +123 -0
- package/src/projection/obsidian/edits/regions.mjs +648 -0
- package/src/projection/obsidian/materialize/byte-lens.mjs +98 -0
- package/src/projection/obsidian/materialize/index.mjs +7 -0
- package/src/projection/obsidian/materialize/path-registry.mjs +105 -0
- package/src/projection/obsidian/materialize/prepare-view.mjs +746 -0
- package/src/projection/obsidian/materialize/settings.mjs +57 -0
- package/src/projection/obsidian/materialize/stage.mjs +27 -0
- package/src/projection/obsidian/proposals/adapter.mjs +572 -0
- package/src/projection/obsidian/proposals/backpressure.mjs +113 -0
- package/src/projection/obsidian/proposals/contribution.mjs +55 -0
- package/src/projection/obsidian/proposals/index.mjs +23 -0
- package/src/projection/obsidian/proposals/observation.mjs +216 -0
- package/src/projection/obsidian/proposals/queue.mjs +277 -0
- package/src/projection/obsidian/proposals/router.mjs +168 -0
- package/src/projection/obsidian/publication/bridge-script.mjs +266 -0
- package/src/projection/obsidian/publication/exchange.mjs +117 -0
- package/src/projection/obsidian/publication/index.mjs +5 -0
- package/src/projection/obsidian/publication/publisher.mjs +588 -0
- package/src/projection/obsidian/publication/test-seam.mjs +16 -0
- package/src/projection/obsidian/publication/transport.mjs +212 -0
- package/src/projection/obsidian/recovery/index.mjs +6 -0
- package/src/projection/obsidian/recovery/journal.mjs +134 -0
- package/src/projection/obsidian/recovery/late-writer.mjs +46 -0
- package/src/projection/obsidian/recovery/restart.mjs +316 -0
- package/src/projection/obsidian/recovery/store.mjs +234 -0
- package/src/projection/obsidian/selection-ui/conflict-view.mjs +91 -0
- package/src/projection/obsidian/selection-ui/contribution.mjs +141 -0
- package/src/projection/obsidian/selection-ui/focus.mjs +82 -0
- package/src/projection/obsidian/selection-ui/index.mjs +12 -0
- package/src/projection/obsidian/selection-ui/policy-setup.mjs +107 -0
- package/src/projection/obsidian/selection-ui/receipt.mjs +173 -0
- package/src/projection/obsidian/selection-ui/selection-state.mjs +122 -0
- package/src/projection/obsidian/selection-ui/selection.mjs +109 -0
- package/src/projection/project.mjs +28 -19
- package/src/readiness-protocols/evidence.mjs +282 -0
- package/src/readiness-protocols/runtime.mjs +4 -3
- package/src/readiness-protocols/source-read.mjs +39 -0
- package/src/runtime/git-adapter.mjs +189 -0
- package/src/runtime/local-state.mjs +439 -0
- package/src/runtime/obsidian/app-capability.mjs +138 -0
- package/src/runtime/obsidian/app-production-seams.mjs +107 -0
- package/src/runtime/obsidian/contributions/proposal-adapter.mjs +4 -0
- package/src/runtime/obsidian/contributions/selection-ui.mjs +4 -0
- package/src/runtime/obsidian/contributions/source-apply.mjs +4 -0
- package/src/runtime/obsidian/contributions.mjs +27 -0
- package/src/runtime/obsidian/documents.mjs +37 -0
- package/src/runtime/obsidian/enablement.mjs +53 -0
- package/src/runtime/obsidian/engine.mjs +532 -0
- package/src/runtime/obsidian/errors.mjs +14 -0
- package/src/runtime/obsidian/extension-points.mjs +103 -0
- package/src/runtime/obsidian/index.mjs +34 -0
- package/src/runtime/obsidian/lifecycle.mjs +258 -0
- package/src/runtime/obsidian/machine-settings.mjs +260 -0
- package/src/runtime/obsidian/observation.mjs +158 -0
- package/src/runtime/obsidian/opening.mjs +280 -0
- package/src/runtime/obsidian/pending-edits.mjs +141 -0
- package/src/runtime/obsidian/pipeline.mjs +122 -0
- package/src/runtime/obsidian/private-lock.mjs +156 -0
- package/src/runtime/obsidian/process-identity.mjs +58 -0
- package/src/runtime/obsidian/service-client.mjs +66 -0
- package/src/runtime/obsidian/service-entry-path.mjs +9 -0
- package/src/runtime/obsidian/service-main.mjs +93 -0
- package/src/runtime/obsidian/service-record.mjs +169 -0
- package/src/runtime/obsidian/service-server.mjs +121 -0
- package/src/runtime/obsidian/service.mjs +216 -0
- package/src/runtime/obsidian/startup-adapters.mjs +87 -0
- package/src/runtime/obsidian/state-store.mjs +180 -0
- package/src/runtime/obsidian/sync-notice.mjs +33 -0
- package/src/runtime/obsidian/tick-loop.mjs +84 -0
- package/src/runtime/obsidian/watchers.mjs +36 -0
- package/src/runtime/repository-observation.mjs +491 -0
- package/src/runtime/supervisor.mjs +803 -0
- package/src/server/local-sidecar.mjs +125 -59
- package/src/server/security.mjs +89 -4
- package/src/server/server.mjs +4 -2
- package/src/skills/steward.mjs +780 -0
- package/src/support/feedback-report.mjs +4 -3
- package/src/ui/presentation/browser.mjs +278 -0
- package/src/ui/presentation/contract.mjs +116 -0
- package/src/ui/presentation/index.mjs +7 -0
- package/src/ui/presentation/native.mjs +153 -0
- package/src/ui/presentation/proof.mjs +57 -0
- package/src/ui/presentation/schema-check.mjs +30 -0
- package/src/ui/presentation/schema.generated.mjs +1371 -0
- package/src/ui/presentation/state.mjs +43 -0
- package/src/ui/presentation/styles.mjs +67 -0
- package/src/ui/presentation/tokens.mjs +74 -0
- package/src/ui/presentation/web.mjs +85 -0
- package/src/ui/review-page.mjs +32 -0
- package/src/upgrade/explanation.mjs +33 -0
- package/src/upgrade/provenance.mjs +338 -0
- package/src/upgrade/transaction-files.mjs +103 -0
- package/src/upgrade/transaction.mjs +475 -0
- package/src/upgrade/upgrade.mjs +65 -29
- package/src/vault/hosts.mjs +21 -0
- package/src/vault/identity.mjs +19 -0
- package/src/vault/index.mjs +7 -0
- package/src/vault/interface.mjs +11 -0
- package/src/vault/metadata.mjs +31 -0
- package/src/vault/privacy.mjs +148 -0
- package/src/vault/probe.mjs +89 -0
- package/src/vault/service.mjs +131 -0
- package/src/vault/source.mjs +45 -0
- package/src/vault/storage.mjs +23 -0
- package/templates/distribution-workspace/.claude/launch.json +6 -2
- package/templates/distribution-workspace/gitignore +1 -0
- package/templates/distribution-workspace/project/README.md +2 -2
- package/templates/external-project-workspace/README.md +45 -0
- package/templates/external-project-workspace/answers.example.json +4 -0
- package/templates/external-project-workspace/atelier.project.json +40 -0
- package/templates/external-project-workspace/boundary-policy.v1.json +60 -0
- package/templates/external-project-workspace/gitignore +8 -0
- package/templates/external-project-workspace/pack-lifecycle.json +14 -0
- package/templates/external-project-workspace/packs/protocols/contract-gate.v1.json +109 -0
- package/templates/external-project-workspace/packs/sample-pack.v1.json +48 -0
- package/templates/external-project-workspace/repo-access.v1.json +9 -0
- package/templates/external-project-workspace/source/README.md +11 -0
- package/templates/launch.json +6 -2
- package/templates/private-domain-workspace/.claude/launch.json +6 -2
- package/templates/private-domain-workspace/domain/README.md +2 -2
- package/templates/private-domain-workspace/gitignore +1 -0
- package/templates/shared-project-workspace/.claude/launch.json +6 -2
- package/templates/shared-project-workspace/gitignore +1 -0
- package/templates/shared-project-workspace/project/README.md +2 -2
package/docs/obsidian.md
ADDED
|
@@ -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.
|