@mnstry/atelier 0.2.0-alpha.6 → 0.2.0-alpha.8
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 +176 -0
- package/README.md +59 -15
- 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-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/docs/assurance-controls.md +9 -1
- package/docs/atelier-sync.md +5 -4
- package/docs/blocks/claims.md +15 -10
- package/docs/blocks/will-not-do.md +4 -1
- package/docs/coauthor-session.md +57 -0
- package/docs/connected-composition.md +75 -0
- package/docs/decisions.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 +65 -9
- 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/local-review.md +84 -0
- package/docs/local-services.md +227 -0
- package/docs/local-state.md +107 -0
- package/docs/obsidian-contract.md +687 -0
- package/docs/obsidian.md +387 -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 +88 -0
- package/docs/review-portability.md +55 -0
- package/docs/skill-steward.md +156 -0
- package/docs/upgrade.md +55 -2
- 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-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/ui/presentation/pane.v1.json +7 -0
- package/fixtures/ui/presentation/reference.v1.json +293 -0
- package/package.json +57 -6
- package/skills/claude/atelier-guided-coauthor/SKILL.md +49 -0
- package/skills/claude/atelier-guided-upgrade/SKILL.md +97 -0
- package/skills/claude/atelier-open/SKILL.md +5 -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-open/SKILL.md +5 -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/policy.mjs +57 -25
- package/src/cli/project-options.mjs +58 -0
- package/src/cli/run.mjs +43 -3
- package/src/coauthor/session.mjs +161 -0
- package/src/coauthor/store.mjs +173 -0
- package/src/collaboration/event-ledger.mjs +16 -33
- package/src/collaboration/inspection-bundle.mjs +223 -0
- package/src/collaboration/review-contracts.mjs +25 -0
- package/src/collaboration/review-store.mjs +305 -0
- package/src/commands/coauthor.mjs +26 -0
- package/src/commands/config.mjs +6 -1
- package/src/commands/extension-pack.mjs +15 -8
- package/src/commands/init.mjs +44 -5
- package/src/commands/obsidian.mjs +368 -0
- package/src/commands/review.mjs +147 -0
- package/src/commands/setup.mjs +51 -14
- package/src/commands/skills.mjs +199 -0
- package/src/commands/sync.mjs +8 -1
- package/src/composition/registry.mjs +27 -0
- package/src/composition/wire.mjs +122 -0
- package/src/contracts/corpus.mjs +29 -0
- package/src/decisions/contracts.d.mts +96 -0
- package/src/decisions/contracts.mjs +321 -0
- package/src/disclosure/content-scan.mjs +13 -0
- package/src/export/atelier-export-contract.mjs +16 -1
- package/src/extension-packs/lifecycle.mjs +154 -0
- package/src/graph/graph.mjs +64 -2
- package/src/graph/knowledge-graph.mjs +548 -24
- package/src/guides/contracts.mjs +54 -0
- package/src/intake/store.mjs +119 -0
- package/src/preview/channel.mjs +89 -0
- package/src/project/config.mjs +26 -27
- package/src/project/durable-state.mjs +161 -0
- package/src/project/file-class.mjs +166 -0
- package/src/project/private-state.mjs +11 -1
- 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 +139 -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/obsidian/app-capability.mjs +110 -0
- package/src/runtime/obsidian/app-production-seams.mjs +96 -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 +243 -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-main.mjs +89 -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/supervisor.mjs +16 -1
- package/src/server/local-sidecar.mjs +44 -0
- package/src/server/server.mjs +1 -0
- package/src/skills/steward.mjs +780 -0
- 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 +63 -28
- 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
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Local source review
|
|
2
|
+
|
|
3
|
+
The optional review workspace helps a reader inspect claims, ask a question or
|
|
4
|
+
propose a correction, and retain that contribution. It records **locally asserted
|
|
5
|
+
identity**. A typed name is not an authenticated approver. Acceptance does not
|
|
6
|
+
edit source, publish content, grant access or admit a runtime.
|
|
7
|
+
|
|
8
|
+
Start with `atelier init --template external-project --target NEW_DIRECTORY`.
|
|
9
|
+
Use a destination whose ancestors are real directories, not symlinks. The
|
|
10
|
+
starter refuses existing destinations and does not initialize Git or install
|
|
11
|
+
hooks. Initialize its adapter and synthetic source as separate repositories when
|
|
12
|
+
ready. An established repository can replace the synthetic source through
|
|
13
|
+
`--repo-path source=PATH`; this changes location, never read authority.
|
|
14
|
+
|
|
15
|
+
From the adapter, run `atelier graph`, then `atelier project`, followed by:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
atelier review packs
|
|
19
|
+
atelier review run sample.readiness:contract-gate --answers answers.example.json
|
|
20
|
+
atelier dev --review
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Open `/review` on the loopback address printed by the foreground sidecar. Enter
|
|
24
|
+
repository `source` and path `README.md`, copy an exact passage, type a question
|
|
25
|
+
or correction, and choose **Save response**. Save is confirmed only after the
|
|
26
|
+
ledger acknowledges the write. On contention, stale content or service failure,
|
|
27
|
+
the wording remains in the form and the page does not claim success. Retry an
|
|
28
|
+
unchanged contribution after restoring service. A changed draft is a new request.
|
|
29
|
+
Unsaved browser drafts are not durable across tab closure.
|
|
30
|
+
|
|
31
|
+
To resume, save a reading position using the exact passage. Close the page,
|
|
32
|
+
reopen it, enter the same asserted name and choose the recorded **Resume** button.
|
|
33
|
+
The response history remains visible. A position is restored only against the
|
|
34
|
+
same document digest. After a source edit, earlier responses retain their exact
|
|
35
|
+
wording, anchor and original digest; explicitly select a passage in the new
|
|
36
|
+
revision and contribute a new response. Reading and scrolling never imply agreement.
|
|
37
|
+
|
|
38
|
+
Enter the returned run ID to review individual subject/predicate/object claims.
|
|
39
|
+
Each shows its ID, mapped answer fields, source-reference resolution, source
|
|
40
|
+
identity and a bounded excerpt. References in this first cut resolve exact graph
|
|
41
|
+
node IDs. Free text, URLs and other reference schemes remain unresolved; they
|
|
42
|
+
are never silently treated as source evidence. A linked source establishes
|
|
43
|
+
which bytes were read, not whether they logically support the proposed claim.
|
|
44
|
+
Read the document and record your judgment. Missing mapped sources or required
|
|
45
|
+
answers prevent acceptance. Rejection and revision remain available against
|
|
46
|
+
current evidence, so missing evidence can be discussed honestly.
|
|
47
|
+
|
|
48
|
+
Accept/reject/revise requires a reason, request ID and expected version. Every
|
|
49
|
+
contribution is immutable. Subsequent versions link through the same claim/run
|
|
50
|
+
identity and target version; proposed successor wording is retained without
|
|
51
|
+
rewriting the original claim. A later rejection supersedes an earlier acceptance
|
|
52
|
+
for current handoff. An identical retry returns the prior record. Another
|
|
53
|
+
reader's intervening contribution refuses a stale version; reopen the run before
|
|
54
|
+
making a new decision. Previous history remains visible rather than being
|
|
55
|
+
collapsed into a single latest-state approval.
|
|
56
|
+
|
|
57
|
+
**Inspect owner handoff**, or `atelier review handoff REQUEST_ID`, displays the
|
|
58
|
+
proposed relationship, rationale, affected IDs and required evidence digest.
|
|
59
|
+
The source owner decides the concrete edit in its own workflow. Source changes
|
|
60
|
+
remain unapplied and promotion remains unestablished. No owner-application receipt
|
|
61
|
+
or publication authorization is fabricated. Changed source/configuration,
|
|
62
|
+
policy, pack or evaluator inputs make current handoff ineligible.
|
|
63
|
+
|
|
64
|
+
The legacy readiness score and `ready`-shaped packet fields retain their existing
|
|
65
|
+
contract meanings: input completion and draft preparation. They are not evidence
|
|
66
|
+
confidence, independent rule execution, human acceptance or runtime readiness.
|
|
67
|
+
New claims omit the former arbitrary numeric confidence. Bound snapshots store
|
|
68
|
+
protocol content, normalized answers, pack identity, declared source byte digests,
|
|
69
|
+
policy identity, and the shipped source/schema evaluator inventory. Dependency
|
|
70
|
+
bytes and upstream publisher authentication need separate installation proof.
|
|
71
|
+
Historic v1 runs without snapshots remain available through the existing readiness
|
|
72
|
+
commands, but cannot be accepted as current evidence through this review surface.
|
|
73
|
+
|
|
74
|
+
The existing sidecar supplies loopback host/origin checks, session nonce checks
|
|
75
|
+
and private-file protection. `--review` is opt-in. The new ledgers live separately
|
|
76
|
+
from legacy proposals under `.atelier-local/review/`. Each record occupies its own
|
|
77
|
+
immutable event aggregate, so existing compaction retains the complete audit
|
|
78
|
+
history. Per-target versions are checked while holding the ledger write lock.
|
|
79
|
+
Local files and locally asserted identities are not protection from an operator
|
|
80
|
+
who controls that same account and its filesystem.
|
|
81
|
+
|
|
82
|
+
Synthetic installed and browser checks establish software behavior. An unfamiliar
|
|
83
|
+
reader's useful question or correction, assistance required, and actual adopter
|
|
84
|
+
acceptance still need a human observation; automated tests cannot supply it.
|
package/docs/local-services.md
CHANGED
|
@@ -99,3 +99,230 @@ A consumer implementation is not complete until tests prove:
|
|
|
99
99
|
|
|
100
100
|
Use synthetic fixtures for public Atelier tests. Tenant-specific proof remains
|
|
101
101
|
in the tenant repository.
|
|
102
|
+
|
|
103
|
+
## The Obsidian maintenance service
|
|
104
|
+
|
|
105
|
+
The continuous maintenance of Obsidian views (`src/runtime/obsidian/`) is a
|
|
106
|
+
consumer of this contract that ships with Atelier. It keeps one service per
|
|
107
|
+
enabled workspace: a process that ticks the maintenance engine on an interval
|
|
108
|
+
and answers on one loopback port. Its lifecycle is `startService`,
|
|
109
|
+
`serviceStatus` and `stopService` in `src/runtime/obsidian/lifecycle.mjs`.
|
|
110
|
+
|
|
111
|
+
### Identity record
|
|
112
|
+
|
|
113
|
+
Each workspace has one record, `state/service/runtime.json`, under the
|
|
114
|
+
workspace's private state root in the machine-private data directory. It is
|
|
115
|
+
never inside a repository or a vault, its directory is owner-only, the file is
|
|
116
|
+
mode 0600, and it is replaced atomically. It satisfies
|
|
117
|
+
`contracts/atelier-obsidian-service-state.v1.schema.json`:
|
|
118
|
+
|
|
119
|
+
- the service name, derived from the workspace identity;
|
|
120
|
+
- the literal loopback host, `127.0.0.1` or `::1`, never a hostname and never
|
|
121
|
+
a wildcard address, and the selected port;
|
|
122
|
+
- the executable identity: the path of the service entry module and the
|
|
123
|
+
SHA-256 digest of its bytes;
|
|
124
|
+
- a random runtime identifier and the PID;
|
|
125
|
+
- the state location, a health description, and the startup consent (who
|
|
126
|
+
granted it, when, and whether it covers the service only or the service and
|
|
127
|
+
operating-system startup);
|
|
128
|
+
- in its private part, the random bearer of that one runtime. The bearer exists
|
|
129
|
+
nowhere else, and nothing that `start`, `status` or `stop` returns carries it.
|
|
130
|
+
|
|
131
|
+
The record is validated on every read. A record that does not validate, or
|
|
132
|
+
that names another workspace, service or state location, makes `start`,
|
|
133
|
+
`status` and `stop` refuse with `invalid-service-record`. It is never repaired,
|
|
134
|
+
adopted or removed automatically; a person inspects it and removes it.
|
|
135
|
+
|
|
136
|
+
The port is a machine-specific value. `start` takes an explicit port, or
|
|
137
|
+
selects a free loopback port once and records it beside the consent in
|
|
138
|
+
`state/service/settings.json`, so later starts, `status` and any installed
|
|
139
|
+
unit name the same one. The first start of a workspace refuses without an
|
|
140
|
+
explicit consent that names its actor.
|
|
141
|
+
|
|
142
|
+
### Health fields and fixed operations
|
|
143
|
+
|
|
144
|
+
| Operation | Method and path | Credential | Answer |
|
|
145
|
+
| --- | --- | --- | --- |
|
|
146
|
+
| Health | `GET /health` | none | service name, workspace identity, runtime identifier, PID, loopback host and port, executable digest, start time, status |
|
|
147
|
+
| Status | `GET /status` | bearer | the health fields, loop state, last tick, last error code, and per-view freshness with held notes counted, not named |
|
|
148
|
+
| Tick now | `POST /tick` | bearer | the state of the tick that ran |
|
|
149
|
+
| Stop | `POST /stop` | bearer | an acknowledgement naming the runtime and PID, then the service ends |
|
|
150
|
+
|
|
151
|
+
Health carries no path, no note title, no source name and no withheld
|
|
152
|
+
identity. There is no other operation: no file serving, no command, no
|
|
153
|
+
evaluation, and no route that takes a path or code from a request. The service
|
|
154
|
+
opens no outbound connection, has no telemetry and depends on nothing remote.
|
|
155
|
+
|
|
156
|
+
The listener refuses a request before looking its operation up when:
|
|
157
|
+
|
|
158
|
+
- `Host` is not exactly the literal loopback authority it is bound to, which
|
|
159
|
+
also refuses a name that resolves to loopback;
|
|
160
|
+
- `Sec-Fetch-Site` is present and is neither `none` nor `same-origin`, or
|
|
161
|
+
`Origin` is present and is not the listener itself;
|
|
162
|
+
- the path is not one of the four, exactly and without a query, or the method
|
|
163
|
+
is not that path's method.
|
|
164
|
+
|
|
165
|
+
Status, tick and stop additionally refuse without the bearer of the running
|
|
166
|
+
runtime. A `POST` payload is a JSON object of at most 1 KiB that names the
|
|
167
|
+
runtime identifier it is meant for, so a request aimed at an earlier runtime on
|
|
168
|
+
the same port does nothing.
|
|
169
|
+
|
|
170
|
+
### Status values and refusal cases
|
|
171
|
+
|
|
172
|
+
| Status | Meaning | `start` | `stop` |
|
|
173
|
+
| --- | --- | --- | --- |
|
|
174
|
+
| `healthy` | record and health agree on service name, workspace, runtime identifier, PID, executable digest and loopback address, and the PID is alive | reports already running | asks that runtime to stop |
|
|
175
|
+
| `busy` | the recorded port accepts a connection and does not answer health in time, the recorded PID is alive, the recorded entry module still has the recorded digest, and that PID's command line names that entry module (and the recorded runtime identifier when it names one) | reports it as running and busy; starts nothing | refuses with a retry hint; nothing is signalled |
|
|
176
|
+
| `stopped` | no record and nothing listens | starts | nothing to do |
|
|
177
|
+
| `occupied` | something answers on the port without that proof: another program, a silent listener that is not provably ours, or a health answer with any field different | refuses; never takes over | refuses |
|
|
178
|
+
| `stale-record` | the recorded address is closed and the recorded PID is gone | starts; the new service replaces the record once it listens | refuses; nothing is proven to stop |
|
|
179
|
+
| `pid-not-ours` | the recorded address is closed and a process has the recorded PID (the number was reused, or a process outlived its listener) | starts; that PID is never signalled | refuses; that PID is never signalled |
|
|
180
|
+
|
|
181
|
+
`start` is serialized per workspace, spawns the service without a shell, and
|
|
182
|
+
detaches it only when asked for a service that survives the launching command.
|
|
183
|
+
It waits for health to echo the runtime identifier it generated and the PID of
|
|
184
|
+
the child it created. If that proof never arrives it stops only that child, by
|
|
185
|
+
its process handle, and reports the private operational log,
|
|
186
|
+
`state/service/service.log`.
|
|
187
|
+
|
|
188
|
+
A tick is largely synchronous, so a healthy service in a long tick may not
|
|
189
|
+
answer health within the deadline. That is `busy`, not `occupied`: it is
|
|
190
|
+
never adopted, never stopped and never started over, and a `start` whose own
|
|
191
|
+
child went straight into a long first tick reports it as started and busy
|
|
192
|
+
instead of ending it. The command line of another process is read from
|
|
193
|
+
`/proc/<pid>/cmdline` on Linux and from `/bin/ps` on macOS. It is not
|
|
194
|
+
established on Windows, where a silent service still reads as `occupied`.
|
|
195
|
+
|
|
196
|
+
`stop` sends the stop operation to the proven runtime only, waits for that PID
|
|
197
|
+
to end and removes only the generated record. It never looks a process up by
|
|
198
|
+
port, name or pattern. Ending a proven runtime that ignores the request is
|
|
199
|
+
opt-in and is proven again immediately before the signal.
|
|
200
|
+
|
|
201
|
+
The service itself refuses to start beside a runtime of the same workspace
|
|
202
|
+
that proves itself, and ends cleanly when its record no longer names it. Two
|
|
203
|
+
engines can never tick one workspace together: each tick holds a private
|
|
204
|
+
per-workspace lock, and an engine that finds it held writes nothing and
|
|
205
|
+
reports busy.
|
|
206
|
+
|
|
207
|
+
### What stop and a hard end leave intact
|
|
208
|
+
|
|
209
|
+
On `stop` the tick in flight finishes, within a grace period. Drafts in a
|
|
210
|
+
vault, pending edits, recovery copies, staging and the last good view are left
|
|
211
|
+
exactly as they are. The service never sweeps or deletes anything under
|
|
212
|
+
`staging/` or `recovery/`: not at start, on a tick, at shutdown, after a hard
|
|
213
|
+
kill or when it finds a stale record. A publication cut short by a hard kill
|
|
214
|
+
is settled by the publisher's own journaled restart recovery on a later tick.
|
|
215
|
+
|
|
216
|
+
A tick that fails for a reason nobody typed (a full disk, a crash inside a
|
|
217
|
+
step) does not end the service. Its error code is kept in
|
|
218
|
+
`state/service/last-error.json`, its message only in the private log; the
|
|
219
|
+
delay before the next attempt doubles up to a ceiling, and the next tick
|
|
220
|
+
proceeds.
|
|
221
|
+
|
|
222
|
+
### Cases that need a person
|
|
223
|
+
|
|
224
|
+
- a record that does not validate, or that belongs to another workspace;
|
|
225
|
+
- an `occupied` port: decide what owns it, or select another port;
|
|
226
|
+
- an engine lock whose holder is a live process that recorded no health
|
|
227
|
+
address, a lock written on another machine, an unreadable ticket, or an
|
|
228
|
+
unknown file in the lock directory. A lock is taken from a holder only with
|
|
229
|
+
proof that its process is gone, or that it was a service whose recorded
|
|
230
|
+
address is closed or answers as another runtime or PID. An address that
|
|
231
|
+
accepts a connection and does not answer in time proves nothing.
|
|
232
|
+
`inspectPrivateGenerationLock` reports the holder without changing anything.
|
|
233
|
+
|
|
234
|
+
### Operating-system startup
|
|
235
|
+
|
|
236
|
+
`buildStartupAdapter` returns the text of a launchd user agent (macOS) or a
|
|
237
|
+
systemd user unit (Linux) from the values it is given. It writes no file,
|
|
238
|
+
installs nothing, runs no service manager and looks nothing up on the machine.
|
|
239
|
+
Windows startup has not been qualified and refuses with
|
|
240
|
+
`startup-platform-unqualified`. Installing a unit is a separate system change
|
|
241
|
+
that needs explicit user authorization; the service refuses to run with
|
|
242
|
+
`--startup` unless the recorded consent covers startup.
|
|
243
|
+
|
|
244
|
+
The service entry refuses to run without an explicitly selected editor
|
|
245
|
+
adapter. Public Atelier tests start only a test entry whose adapter reports
|
|
246
|
+
that no app runs, in temporary directories, on ephemeral loopback ports.
|
|
247
|
+
|
|
248
|
+
### The `obsidian` command
|
|
249
|
+
|
|
250
|
+
`atelier obsidian <operation>` is the noninteractive surface over all of the
|
|
251
|
+
above. With `--json` it prints exactly one JSON document, for a refusal too.
|
|
252
|
+
Exit codes: 0 done, 1 an error nobody typed, 2 a typed refusal or a usage
|
|
253
|
+
error, 3 the operation ran and its answer is not success.
|
|
254
|
+
|
|
255
|
+
| Operation | What it does | Writes |
|
|
256
|
+
| --- | --- | --- |
|
|
257
|
+
| `status` | enablement, machine settings, proven service state, per-view freshness, whether an apply operation exists | nothing |
|
|
258
|
+
| `scope list`, `scope show ID` | the declared views | nothing |
|
|
259
|
+
| `audience show`, `audience set A,B`, `audience clear` | the audiences this machine lets into a view; none by default, which publishes an empty view. A change invalidates every view at the next tick | private machine settings |
|
|
260
|
+
| `mode show`, `mode set manual\|automatic` | `automatic` refuses without an installed, matching, active automatic policy | private machine settings |
|
|
261
|
+
| `policy show`, `policy install FILE`, `policy revoke` | `install` validates against the apply-policy contract and stores the policy owner-only beside the machine settings, never in a project, a repository or a note. `revoke` marks the stored policy revoked, which the engine reads before its very next dispatch, and returns the mode to manual | private machine settings |
|
|
262
|
+
| `service start`, `service status`, `service stop` | `startService`, `serviceStatus`, `stopService`. The first start needs `--consent-actor ID` | what the lifecycle writes |
|
|
263
|
+
| `service unit --print` | the text `buildStartupAdapter` returns. Installing a unit is not offered | nothing |
|
|
264
|
+
| `open [--scope ID]` | starts or reconnects the owned service, asks it for a tick, reads the view back, qualifies the installed app, has the vault opened | what the service writes |
|
|
265
|
+
|
|
266
|
+
Reaching the installed app or the operating system is never a default.
|
|
267
|
+
`open`, `service start` and `service unit` refuse with
|
|
268
|
+
`app-adapter-not-selected` unless `--adapter=obsidian-cli` is given, the same
|
|
269
|
+
explicit rule the service entry has, and the modules that talk to an app are
|
|
270
|
+
loaded only after that check. Tests pass their own seams and a guard in the
|
|
271
|
+
test file throws if anything tries to start the app, its command-line tool, an
|
|
272
|
+
operating-system opener or a service manager.
|
|
273
|
+
|
|
274
|
+
`open` answers one typed outcome, each with a one-line explanation and a next
|
|
275
|
+
step. Only `current` is success:
|
|
276
|
+
|
|
277
|
+
| Outcome | Meaning |
|
|
278
|
+
| --- | --- |
|
|
279
|
+
| `current` | the proven service ticked after the request; the view's persisted freshness is `current`; read back independently, the trusted generation is the prepared one and every note has the bytes it was published with; the app meets the minimum version, was asked to open this vault, answers for exactly this vault and has finished reading it |
|
|
280
|
+
| `updating` | a publication is under way, a tick outlasted the wait, or a note differs from the trusted generation and has not been looked at yet |
|
|
281
|
+
| `held-for-your-edit` | an edited note is preserved and held |
|
|
282
|
+
| `stale-readable` | a last good vault exists and reads back, but is not proven to be the present generation |
|
|
283
|
+
| `not-prepared` | no generation of this view has been published |
|
|
284
|
+
| `publisher-conflict` | another publisher or an uncoordinated editor holds the vault |
|
|
285
|
+
| `app-missing`, `app-cli-unavailable`, `app-version-unsupported` | no installation; no command-line capability; below the minimum version, or a version that cannot be read |
|
|
286
|
+
| `launch-failed` | the operating system refused, or the app never answered for this vault |
|
|
287
|
+
| `indexing` | the app answers for this vault and has not finished reading it |
|
|
288
|
+
| `service-unavailable` | the service could not be started or is not provably ours (`occupied`, no consent yet, a start that never proved ownership) |
|
|
289
|
+
| `busy` | the service is ours and in a long tick |
|
|
290
|
+
| `disabled` | the integration is off or not declared |
|
|
291
|
+
|
|
292
|
+
`apply-unavailable` is not an opening outcome. It is reported beside pending
|
|
293
|
+
edits, by `status`, `open`, `mode` and the placeholder `apply` operation, for
|
|
294
|
+
as long as no apply operation is registered. `status` does not repeat a
|
|
295
|
+
persisted `current` while no healthy service proves it: opening a vault
|
|
296
|
+
directly bypasses this command, so an old check is reported as
|
|
297
|
+
`stale-readable` with the reason.
|
|
298
|
+
|
|
299
|
+
The minimum app version is `MINIMUM_APP_VERSION` in
|
|
300
|
+
`src/runtime/obsidian/app-capability.mjs`: 1.13.7, the only version the
|
|
301
|
+
publication protocol was proven on. A prerelease ranks below its release and a
|
|
302
|
+
version that cannot be parsed never passes. The service entry constructs the
|
|
303
|
+
CLI editor adapter only through `createQualifiedAdapterFactory`: for a running
|
|
304
|
+
app below the floor, an unreadable version or a missing command-line
|
|
305
|
+
capability the factory refuses, the engine records that code as the view's
|
|
306
|
+
freshness reason and nothing is published. An app that is positively not
|
|
307
|
+
running needs no version, because nothing is published through it.
|
|
308
|
+
|
|
309
|
+
The production probe and launcher (`app-production-seams.mjs`) are written
|
|
310
|
+
against the documented command-line interface and have not been exercised
|
|
311
|
+
against a running app in this repository's tests, by design. They are to be
|
|
312
|
+
qualified on an isolated host before they are relied on.
|
|
313
|
+
|
|
314
|
+
`atelier sync` does not maintain a vault and does not try. When the enrolled
|
|
315
|
+
project enables the integration, `sync status` gains one member,
|
|
316
|
+
`obsidianMaintenance`, that repeats what maintenance last persisted and points
|
|
317
|
+
at `atelier obsidian status`; it starts no service, ticks no engine and writes
|
|
318
|
+
nothing. With the settings absent or switched off its output is unchanged.
|
|
319
|
+
|
|
320
|
+
### Registering later work
|
|
321
|
+
|
|
322
|
+
An apply operation, a proposal adapter and further `obsidian` sub-operations
|
|
323
|
+
are contributions: a module in `src/runtime/obsidian/contributions/` whose
|
|
324
|
+
default export is `{ id, register({ extensions, operations }) }`. The command
|
|
325
|
+
and the service entry both load that directory, so an apply operation
|
|
326
|
+
registered there is the one the service dispatches to and the one `status`
|
|
327
|
+
reports. No dispatch file is edited. A built-in operation name cannot be
|
|
328
|
+
taken, except the placeholder `apply`.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Durable local state
|
|
2
|
+
|
|
3
|
+
`@mnstry/atelier/local-state` provides filesystem mechanisms for local adapters.
|
|
4
|
+
The adapter owns paths, schema, content, transport, authentication, state limits,
|
|
5
|
+
and user-facing recovery. These helpers do not create a service or publish data.
|
|
6
|
+
|
|
7
|
+
- `publishPrivateFile(path, bytes)` stages and syncs bytes, publishes with an
|
|
8
|
+
atomic non-overwriting hard link, syncs the directory, and verifies readback.
|
|
9
|
+
An identical existing file is an idempotent success. A differing file refuses.
|
|
10
|
+
- `acquirePrivateLock(path)` returns an idempotent release function. Callers must
|
|
11
|
+
release in `finally`, after all asynchronous work has finished.
|
|
12
|
+
- `withPrivateLock(path, operation)` is for **synchronous** operations only.
|
|
13
|
+
- `createVerifiedFileSequence({directory, initial, apply})` reads immutable
|
|
14
|
+
records in filename order. `initial()` creates reducer state and
|
|
15
|
+
`apply(text, previous, oneBasedIndex, filename)` validates each record and
|
|
16
|
+
returns the next state. The adapter validates names, chain, schema and limits.
|
|
17
|
+
Unchanged prefixes reuse their validated result; file identity and timestamps
|
|
18
|
+
are checked on each call. Startup verifies the complete history.
|
|
19
|
+
- `isPendingPrivateWrite(name)` recognizes incomplete staging names. They are
|
|
20
|
+
never committed records. Preserve them for diagnosis after interruption.
|
|
21
|
+
- `syncPrivateDirectory(path)` flushes a directory where supported. Windows
|
|
22
|
+
directory durability requires target-filesystem qualification; synced file
|
|
23
|
+
contents alone do not prove survival of power loss.
|
|
24
|
+
|
|
25
|
+
Use an ignored private directory whose ancestors have already been validated.
|
|
26
|
+
Leaves refuse symlinks. These are local integrity mechanisms, not a sandbox
|
|
27
|
+
against an actor able to rewrite the same user's files or filesystem metadata.
|
|
28
|
+
Do not run old and new writer implementations concurrently against one store.
|
|
29
|
+
|
|
30
|
+
## Recovery and compatibility
|
|
31
|
+
|
|
32
|
+
Locks use atomic numbered ownership tickets. A definitely absent local process
|
|
33
|
+
can be succeeded without deleting its ticket. Contending recoverers compete for
|
|
34
|
+
the same next number. Live, reused, foreign-host and unverifiable process IDs
|
|
35
|
+
block. No elapsed timeout grants ownership. The nonce binds release to the
|
|
36
|
+
exact ticket. Legacy PID-only lock records can be bypassed only when the PID is
|
|
37
|
+
definitely absent; unidentified legacy locks require operator diagnosis.
|
|
38
|
+
|
|
39
|
+
Process interruptions can leave staging files or dead-owner tickets. Retain
|
|
40
|
+
these as evidence. Never clear an entire private-state directory to recover a
|
|
41
|
+
single interrupted operation. A corrupted committed record is still an error;
|
|
42
|
+
the reader does not silently skip or truncate it.
|
|
43
|
+
|
|
44
|
+
The collaboration ledger retains its existing bounded NDJSON format but writes
|
|
45
|
+
each complete validated successor atomically. Coauthor and intake reuse the
|
|
46
|
+
same ownership protocol. Immutable intake blobs and coauthor values use atomic
|
|
47
|
+
publication. Existing history is not rewritten by these changes.
|
|
48
|
+
|
|
49
|
+
Adapters should bind retries to the same operation identity and exact input,
|
|
50
|
+
retain newer edits while an earlier save is pending, measure limits in UTF-8
|
|
51
|
+
bytes, and verify runtime build identity before declaring an existing service
|
|
52
|
+
current. Opening a previously verified artifact must not require optional graph
|
|
53
|
+
or projection generation to succeed.
|
|
54
|
+
|
|
55
|
+
## Evidence boundaries
|
|
56
|
+
|
|
57
|
+
Local tests inject interruption before publication, verify the previous ledger
|
|
58
|
+
remains readable, restart after an owner exits without releasing, refuse live
|
|
59
|
+
and unknown owners, verify binary readback, and exercise cache invalidation.
|
|
60
|
+
These are process-interruption tests, not physical power-loss qualification.
|
|
61
|
+
No cache promises constant memory or constant metadata work for unbounded
|
|
62
|
+
history. A different checkpoint format needs its own migration and replay proof.
|
|
63
|
+
|
|
64
|
+
## Ownership diagnostics and offline recovery
|
|
65
|
+
|
|
66
|
+
`inspectPrivateLock(path)` is read-only and reports availability, reason, newest
|
|
67
|
+
filename and generation. It does not authorize overriding an unresolved owner.
|
|
68
|
+
Only the newest ticket is checked for process liveness. Its predecessor was
|
|
69
|
+
adjudicated when the successor was published. Release now retains the ticket and
|
|
70
|
+
publishes a matching `.released` file; without that marker, removing a successor
|
|
71
|
+
would expose an obsolete PID again. A matching release proves operation completion
|
|
72
|
+
even if a later host name or PID changes. An unresolved newest ticket on another
|
|
73
|
+
host, a reused PID, or an empty legacy lock still blocks with a specific diagnostic.
|
|
74
|
+
A successful successor binds the old legacy lock's exact bytes and supersedes that
|
|
75
|
+
liveness decision. A newly created or changed legacy lock must be adjudicated again.
|
|
76
|
+
|
|
77
|
+
If the newest owner cannot be established, stop all writers and verify that no
|
|
78
|
+
other machine or process can write this store. Preserve a complete private backup,
|
|
79
|
+
including records and both the legacy lock and ownership directory. With exclusive
|
|
80
|
+
offline custody, move the lock file and its whole `.owners` directory together to
|
|
81
|
+
a uniquely named recovery directory outside the live store. Do not edit individual
|
|
82
|
+
tickets, forge release markers, remove event records or bypass a live owner. Run
|
|
83
|
+
the adapter's history verification before reopening. If exclusive custody cannot
|
|
84
|
+
be established, remain blocked. Do not mix this protocol with an older writer.
|
|
85
|
+
|
|
86
|
+
Ownership tickets and releases accumulate; adapters must monitor their size and
|
|
87
|
+
perform any retirement under the same offline recovery procedure. Automatic
|
|
88
|
+
age-based cleanup cannot establish whether a writer or operation is finished.
|
|
89
|
+
|
|
90
|
+
## Filesystem and adapter contract
|
|
91
|
+
|
|
92
|
+
Publication requires same-directory hard links and atomic non-overwrite creation.
|
|
93
|
+
Unsupported filesystems must fail; there is no copy/overwrite fallback. Qualify the
|
|
94
|
+
actual target filesystem with a disposable write, exact readback and reopening
|
|
95
|
+
before moving a store there. This does not prove physical power-loss durability.
|
|
96
|
+
Incomplete `.atelier-write-<uuid>.tmp` residue is uncommitted. Preserve it until
|
|
97
|
+
writers are stopped and diagnosis is complete; never reap it by age during writes.
|
|
98
|
+
|
|
99
|
+
`createVerifiedFileSequence` accepts an optional `ignoreFiles` array of exact
|
|
100
|
+
adapter-owned metadata filenames. Those entries must still be regular files.
|
|
101
|
+
All other names reach the adapter validator; unknown or corrupt records must not
|
|
102
|
+
be hidden by a suffix wildcard. Staging names, ownership tickets/release markers
|
|
103
|
+
and synchronous `withPrivateLock` semantics are compatibility obligations of this
|
|
104
|
+
export. Consumers should use the exported helpers rather than infer these formats.
|
|
105
|
+
Locking is non-reentrant. Async callers must acquire/release explicitly in `finally`.
|
|
106
|
+
A release error after a committed operation still reports uncertainty; reconcile
|
|
107
|
+
an operation ID before writing again rather than treating it as evidence of no save.
|