@mnstry/atelier 0.2.0-alpha.12 → 0.2.0-alpha.14
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 +894 -0
- package/README.md +159 -6
- package/contracts/atelier-adoption-policy.v2.schema.json +55 -0
- package/contracts/atelier-architecture.v1.schema.json +258 -0
- package/contracts/atelier-boundary-delegations.v1.schema.json +161 -0
- package/contracts/atelier-boundary-owner-keys.v1.schema.json +98 -0
- package/contracts/atelier-build.v1.schema.json +1076 -0
- package/contracts/atelier-capability.v1.schema.json +1252 -0
- package/contracts/atelier-coordination.v1.schema.json +1050 -0
- package/contracts/atelier-evidence-navigation.v1.schema.json +603 -0
- package/contracts/atelier-harness.v1.schema.json +234 -0
- package/contracts/atelier-ingestion.v1.schema.json +806 -0
- package/contracts/atelier-inquiry.v1.schema.json +1772 -0
- package/contracts/atelier-instruction-adoption.v1.schema.json +490 -0
- package/contracts/atelier-interaction.v1.schema.json +553 -0
- package/contracts/atelier-judgment.v1.schema.json +690 -0
- package/contracts/atelier-knowledge.v1.schema.json +1512 -0
- package/contracts/atelier-learning.v1.schema.json +1095 -0
- package/contracts/atelier-migration.v3.schema.json +64 -0
- package/contracts/atelier-personal-workspace-manifest.v1.schema.json +87 -0
- package/contracts/atelier-personal-workspace-overlay.v1.schema.json +299 -0
- package/contracts/atelier-template-adoption.v1.schema.json +223 -0
- package/contracts/atelier-template-profile.v1.schema.json +1168 -0
- package/contracts/atelier-trackable.v1.schema.json +781 -0
- package/contracts/atelier-upgrade-plan.v3.schema.json +532 -0
- package/docs/architecture.md +89 -0
- package/docs/assurance-controls.md +2 -2
- package/docs/blocks/capability-stewardship.md +17 -0
- package/docs/blocks/claims.md +16 -3
- package/docs/blocks/inquiry-harnesses.md +19 -0
- package/docs/blocks/learning-harnesses.md +17 -0
- package/docs/capability-stewardship.md +267 -0
- package/docs/coordination.md +51 -0
- package/docs/decision-practices.md +111 -0
- package/docs/decisions.md +29 -0
- package/docs/evidence-navigation.md +237 -0
- package/docs/exact-upgrades.md +3 -2
- package/docs/ingestion.md +181 -0
- package/docs/inquiry-harnesses.md +210 -0
- package/docs/install.md +80 -3
- package/docs/intake-and-guides.md +5 -0
- package/docs/integration-contract-decisions.md +24 -2
- package/docs/interaction-and-reflection.md +72 -0
- package/docs/knowledge-graph.md +20 -0
- package/docs/knowledge-health/api.md +30 -0
- package/docs/knowledge-health/assessment/api.md +96 -0
- package/docs/knowledge-health/assessment/capabilities.md +40 -0
- package/docs/knowledge-health/assessment/evaluation.md +27 -0
- package/docs/knowledge-health/assessment/facilitator.md +30 -0
- package/docs/knowledge-health/assessment/quickstart.md +61 -0
- package/docs/knowledge-health/assessment/troubleshooting.md +23 -0
- package/docs/knowledge-health/devday/facilitator.md +16 -0
- package/docs/knowledge-health/devday/participant-response.md +22 -0
- package/docs/knowledge-health/devday/public-default.md +107 -0
- package/docs/knowledge-health/devday/quickstart.md +18 -0
- package/docs/knowledge-health/devday/support.md +22 -0
- package/docs/knowledge-health/exercise.md +17 -0
- package/docs/knowledge-health/facilitator.md +20 -0
- package/docs/knowledge-health/quickstart.md +94 -0
- package/docs/knowledge-health/readme.md +15 -0
- package/docs/knowledge-health/support.md +34 -0
- package/docs/knowledge-setup.md +248 -0
- package/docs/knowledge-workspace.md +264 -0
- package/docs/learning-harnesses.md +237 -0
- package/docs/learning.md +215 -0
- package/docs/local-services.md +223 -19
- package/docs/obsidian-contract.md +246 -15
- package/docs/obsidian-plugin.md +252 -7
- package/docs/obsidian.md +874 -52
- package/docs/optional-ingestion.md +83 -0
- package/docs/personal-workspace.md +266 -0
- package/docs/practical-judgment.md +92 -0
- package/docs/progressive-ingestion-acceptance.md +89 -0
- package/docs/progressive-ingestion.md +235 -0
- package/docs/project-command-inventory.md +1 -0
- package/docs/release-engineering.md +50 -0
- package/docs/repo-boundary-guard.md +120 -3
- package/docs/research-starter-quickstart.md +66 -0
- package/docs/research-starter.md +114 -0
- package/docs/semantic-ingestion.md +198 -0
- package/docs/semantic-operation.md +318 -0
- package/docs/skill-steward.md +6 -0
- package/docs/template-profile.md +164 -0
- package/docs/template-upgrades.md +114 -0
- package/docs/trackables.md +111 -0
- package/docs/upgrade.md +219 -2
- package/fixtures/atelier-architecture/binding/invalid/document.json +19 -0
- package/fixtures/atelier-architecture/binding/valid/document.json +19 -0
- package/fixtures/atelier-architecture/catalog/invalid/document.json +290 -0
- package/fixtures/atelier-architecture/catalog/valid/document.json +343 -0
- package/fixtures/atelier-build-contract/attempt/invalid/document.json +20 -0
- package/fixtures/atelier-build-contract/attempt/valid/document.json +19 -0
- package/fixtures/atelier-build-contract/candidate/invalid/document.json +24 -0
- package/fixtures/atelier-build-contract/candidate/valid/document.json +23 -0
- package/fixtures/atelier-build-contract/decision/invalid/document.json +31 -0
- package/fixtures/atelier-build-contract/decision/valid/document.json +30 -0
- package/fixtures/atelier-build-contract/delivery/invalid/document.json +18 -0
- package/fixtures/atelier-build-contract/delivery/valid/document.json +17 -0
- package/fixtures/atelier-build-contract/gate/invalid/document.json +22 -0
- package/fixtures/atelier-build-contract/gate/valid/document.json +21 -0
- package/fixtures/atelier-build-contract/ledger/invalid/document.json +270 -0
- package/fixtures/atelier-build-contract/ledger/valid/document.json +269 -0
- package/fixtures/atelier-build-contract/objective/invalid/document.json +73 -0
- package/fixtures/atelier-build-contract/objective/valid/document.json +72 -0
- package/fixtures/atelier-build-contract/progress/invalid/document.json +18 -0
- package/fixtures/atelier-build-contract/progress/valid/document.json +17 -0
- package/fixtures/atelier-capability-contract/adoption/invalid/document.json +71 -0
- package/fixtures/atelier-capability-contract/adoption/valid/document.json +70 -0
- package/fixtures/atelier-capability-contract/event/invalid/document.json +18 -0
- package/fixtures/atelier-capability-contract/event/valid/document.json +17 -0
- package/fixtures/atelier-capability-contract/journal/invalid/document.json +382 -0
- package/fixtures/atelier-capability-contract/journal/valid/document.json +381 -0
- package/fixtures/atelier-capability-contract/notice/invalid/document.json +8 -0
- package/fixtures/atelier-capability-contract/notice/valid/document.json +7 -0
- package/fixtures/atelier-capability-contract/package/invalid/document.json +59 -0
- package/fixtures/atelier-capability-contract/package/valid/document.json +58 -0
- package/fixtures/atelier-capability-contract/plan/invalid/document.json +107 -0
- package/fixtures/atelier-capability-contract/plan/valid/document.json +106 -0
- package/fixtures/atelier-capability-contract/release/invalid/document.json +85 -0
- package/fixtures/atelier-capability-contract/release/valid/document.json +84 -0
- package/fixtures/atelier-capability-contract/state/invalid/document.json +316 -0
- package/fixtures/atelier-capability-contract/state/valid/document.json +315 -0
- package/fixtures/atelier-coordination/dependency/invalid/document.json +37 -0
- package/fixtures/atelier-coordination/dependency/valid/document.json +36 -0
- package/fixtures/atelier-coordination/directive/invalid/document.json +25 -0
- package/fixtures/atelier-coordination/directive/valid/document.json +24 -0
- package/fixtures/atelier-coordination/disposition/invalid/document.json +34 -0
- package/fixtures/atelier-coordination/disposition/valid/document.json +33 -0
- package/fixtures/atelier-coordination/outcome/invalid/document.json +39 -0
- package/fixtures/atelier-coordination/outcome/valid/document.json +38 -0
- package/fixtures/atelier-coordination/recovery/invalid/document.json +34 -0
- package/fixtures/atelier-coordination/recovery/valid/document.json +33 -0
- package/fixtures/atelier-harness-contract/handoff/invalid/document.json +25 -0
- package/fixtures/atelier-harness-contract/handoff/valid/document.json +24 -0
- package/fixtures/atelier-ingestion/invalid/authority.json +19 -0
- package/fixtures/atelier-ingestion/semantic-baseline.json +26 -0
- package/fixtures/atelier-ingestion/semantic-operation.json +75 -0
- package/fixtures/atelier-ingestion/valid/plan-input.json +18 -0
- package/fixtures/atelier-inquiry-contract/assessment/invalid/document.json +41 -0
- package/fixtures/atelier-inquiry-contract/assessment/valid/document.json +40 -0
- package/fixtures/atelier-inquiry-contract/bundle/invalid/document.json +51 -0
- package/fixtures/atelier-inquiry-contract/bundle/valid/document.json +50 -0
- package/fixtures/atelier-inquiry-contract/campaign/invalid/document.json +17 -0
- package/fixtures/atelier-inquiry-contract/campaign/valid/document.json +16 -0
- package/fixtures/atelier-inquiry-contract/decision/invalid/document.json +22 -0
- package/fixtures/atelier-inquiry-contract/decision/valid/document.json +21 -0
- package/fixtures/atelier-inquiry-contract/feedback/invalid/document.json +19 -0
- package/fixtures/atelier-inquiry-contract/feedback/valid/document.json +18 -0
- package/fixtures/atelier-inquiry-contract/handoff/invalid/document.json +92 -0
- package/fixtures/atelier-inquiry-contract/handoff/valid/document.json +91 -0
- package/fixtures/atelier-inquiry-contract/hypothesis/invalid/document.json +20 -0
- package/fixtures/atelier-inquiry-contract/hypothesis/valid/document.json +19 -0
- package/fixtures/atelier-inquiry-contract/ledger/invalid/document.json +680 -0
- package/fixtures/atelier-inquiry-contract/ledger/valid/document.json +679 -0
- package/fixtures/atelier-inquiry-contract/legacy-assessment/invalid/document.json +19 -0
- package/fixtures/atelier-inquiry-contract/legacy-assessment/valid/document.json +18 -0
- package/fixtures/atelier-inquiry-contract/request/invalid/document.json +50 -0
- package/fixtures/atelier-inquiry-contract/request/valid/document.json +49 -0
- package/fixtures/atelier-inquiry-contract/source/invalid/document.json +18 -0
- package/fixtures/atelier-inquiry-contract/source/valid/document.json +17 -0
- package/fixtures/atelier-inquiry-contract/withdrawal/invalid/document.json +16 -0
- package/fixtures/atelier-inquiry-contract/withdrawal/valid/document.json +15 -0
- package/fixtures/atelier-instruction-adoption/context-receipt/invalid/document.json +18 -0
- package/fixtures/atelier-instruction-adoption/context-receipt/valid/document.json +17 -0
- package/fixtures/atelier-instruction-adoption/plan/invalid/document.json +63 -0
- package/fixtures/atelier-instruction-adoption/plan/valid/document.json +62 -0
- package/fixtures/atelier-instruction-adoption/receipt/invalid/document.json +22 -0
- package/fixtures/atelier-instruction-adoption/receipt/valid/document.json +21 -0
- package/fixtures/atelier-instruction-adoption/state/invalid/document.json +30 -0
- package/fixtures/atelier-instruction-adoption/state/valid/document.json +29 -0
- package/fixtures/atelier-interaction/act/invalid/document.json +25 -0
- package/fixtures/atelier-interaction/act/valid/document.json +24 -0
- package/fixtures/atelier-interaction/assessment/invalid/document.json +32 -0
- package/fixtures/atelier-interaction/assessment/valid/document.json +31 -0
- package/fixtures/atelier-interaction/observation/invalid/document.json +22 -0
- package/fixtures/atelier-interaction/observation/valid/document.json +21 -0
- package/fixtures/atelier-interaction/policy/invalid/document.json +20 -0
- package/fixtures/atelier-interaction/policy/valid/document.json +19 -0
- package/fixtures/atelier-judgment/case/invalid/document.json +76 -0
- package/fixtures/atelier-judgment/case/valid/document.json +75 -0
- package/fixtures/atelier-judgment/decision/invalid/document.json +38 -0
- package/fixtures/atelier-judgment/decision/valid/document.json +37 -0
- package/fixtures/atelier-judgment/native-decision.md +3 -0
- package/fixtures/atelier-judgment/practice-reconsideration/definition.json +92 -0
- package/fixtures/atelier-judgment/practice-reconsideration/scenario.json +435 -0
- package/fixtures/atelier-knowledge-contract/activation/invalid/document.json +26 -0
- package/fixtures/atelier-knowledge-contract/activation/valid/document.json +25 -0
- package/fixtures/atelier-knowledge-contract/contribution/invalid/document.json +34 -0
- package/fixtures/atelier-knowledge-contract/contribution/valid/document.json +33 -0
- package/fixtures/atelier-knowledge-contract/domain/invalid/document.json +41 -0
- package/fixtures/atelier-knowledge-contract/domain/valid/document.json +40 -0
- package/fixtures/atelier-knowledge-contract/domain-revision/invalid/document.json +46 -0
- package/fixtures/atelier-knowledge-contract/domain-revision/valid/document.json +45 -0
- package/fixtures/atelier-knowledge-contract/evaluation/invalid/document.json +21 -0
- package/fixtures/atelier-knowledge-contract/evaluation/valid/document.json +20 -0
- package/fixtures/atelier-knowledge-contract/ledger/invalid/document.json +318 -0
- package/fixtures/atelier-knowledge-contract/ledger/valid/document.json +317 -0
- package/fixtures/atelier-knowledge-contract/relation/invalid/document.json +25 -0
- package/fixtures/atelier-knowledge-contract/relation/valid/document.json +24 -0
- package/fixtures/atelier-knowledge-contract/review/invalid/document.json +18 -0
- package/fixtures/atelier-knowledge-contract/review/valid/document.json +17 -0
- package/fixtures/atelier-knowledge-contract/withdrawal/invalid/document.json +16 -0
- package/fixtures/atelier-knowledge-contract/withdrawal/valid/document.json +15 -0
- package/fixtures/atelier-learning/invalid/authority.json +14 -0
- package/fixtures/atelier-learning/valid/capture.json +13 -0
- package/fixtures/atelier-personal-workspace-contract/manifest/invalid/authority-key.json +17 -0
- package/fixtures/atelier-personal-workspace-contract/manifest/invalid/nonempty-ext.json +25 -0
- package/fixtures/atelier-personal-workspace-contract/manifest/invalid/wrong-schema.json +22 -0
- package/fixtures/atelier-personal-workspace-contract/manifest/valid/manifest.json +22 -0
- package/fixtures/atelier-personal-workspace-contract/overlay/invalid/authority-key.json +25 -0
- package/fixtures/atelier-personal-workspace-contract/overlay/invalid/unknown-top-level-field.json +27 -0
- package/fixtures/atelier-personal-workspace-contract/overlay/invalid/wrong-schema.json +24 -0
- package/fixtures/atelier-trackable/definition/invalid/document.json +15 -0
- package/fixtures/atelier-trackable/definition/valid/document.json +14 -0
- package/fixtures/atelier-trackable/request/invalid/document.json +23 -0
- package/fixtures/atelier-trackable/request/valid/document.json +22 -0
- package/fixtures/boundary-delegations/invalid/authority-key.v1.json +30 -0
- package/fixtures/boundary-delegations/invalid/push-operation.v1.json +28 -0
- package/fixtures/boundary-delegations/invalid/unsigned.v1.json +25 -0
- package/fixtures/boundary-delegations/valid/empty.v1.json +4 -0
- package/fixtures/boundary-delegations/valid/signed-operator.v1.json +30 -0
- package/fixtures/boundary-owner-keys/invalid/wrong-algorithm.v1.json +15 -0
- package/fixtures/boundary-owner-keys/invalid/wrong-curve.v1.json +15 -0
- package/fixtures/boundary-owner-keys/valid/one-owner.v1.json +17 -0
- package/fixtures/capability-packages/adoption.example.json +70 -0
- package/fixtures/capability-packages/evidence-review/capability-package.json +58 -0
- package/fixtures/capability-packages/evidence-review/capability-release.json +84 -0
- package/fixtures/capability-packages/evidence-review/evaluation.md +15 -0
- package/fixtures/capability-packages/evidence-review/skills/evidence-review/SKILL.md +22 -0
- package/fixtures/capability-packages/evidence-review/skills/evidence-review/references/ledger.md +7 -0
- package/fixtures/capability-packages/research/capability-package.json +88 -0
- package/fixtures/capability-packages/research/capability-release.json +134 -0
- package/fixtures/capability-packages/research/evaluation.md +15 -0
- package/fixtures/capability-packages/research/skills/research-integrate/SKILL.md +29 -0
- package/fixtures/capability-packages/research/skills/research-integrate/references/integration.md +10 -0
- package/fixtures/capability-packages/research/skills/research-plan/SKILL.md +27 -0
- package/fixtures/capability-packages/research/skills/research-plan/references/inquiries.md +26 -0
- package/fixtures/capability-packages/research/skills/research-synthesize/SKILL.md +23 -0
- package/fixtures/capability-packages/research/skills/research-synthesize/references/synthesis.md +12 -0
- package/fixtures/evidence-navigation/invalid/duplicate-operation.json +16 -0
- package/fixtures/evidence-navigation/invalid/instruction-kind.json +36 -0
- package/fixtures/evidence-navigation/invalid/invalid-digest.json +36 -0
- package/fixtures/evidence-navigation/invalid/invalid-time.json +36 -0
- package/fixtures/evidence-navigation/invalid/missing-attribution.json +35 -0
- package/fixtures/evidence-navigation/invalid/reserved-extension.json +19 -0
- package/fixtures/evidence-navigation/invalid/unattributed-acceptance.json +36 -0
- package/fixtures/evidence-navigation/invalid/uncoerced-version.json +16 -0
- package/fixtures/evidence-navigation/invalid/unknown-currency.json +17 -0
- package/fixtures/evidence-navigation/invalid/unknown-selector-field.json +37 -0
- package/fixtures/evidence-navigation/invalid/unsupported-operation.json +15 -0
- package/fixtures/evidence-navigation/valid/claim.json +36 -0
- package/fixtures/evidence-navigation/valid/host.json +16 -0
- package/fixtures/evidence-navigation/valid/profile.json +16 -0
- package/fixtures/evidence-navigation/valid/snapshot.json +17 -0
- package/fixtures/harness-packages/build-harness/capability-package.json +61 -0
- package/fixtures/harness-packages/build-harness/capability-release.json +82 -0
- package/fixtures/harness-packages/build-harness/evaluation.md +3 -0
- package/fixtures/harness-packages/build-harness/skills/atelier-build-harness/SKILL.md +59 -0
- package/fixtures/harness-packages/knowledge-harness/capability-package.json +61 -0
- package/fixtures/harness-packages/knowledge-harness/capability-release.json +82 -0
- package/fixtures/harness-packages/knowledge-harness/evaluation.md +3 -0
- package/fixtures/harness-packages/knowledge-harness/skills/atelier-knowledge-harness/SKILL.md +60 -0
- package/fixtures/harnesses/learning-cycle.json +780 -0
- package/fixtures/inquiry/workshop.json +675 -0
- package/fixtures/inquiry-packages/discovery-harness/capability-package.json +61 -0
- package/fixtures/inquiry-packages/discovery-harness/capability-release.json +82 -0
- package/fixtures/inquiry-packages/discovery-harness/evaluation.md +3 -0
- package/fixtures/inquiry-packages/discovery-harness/skills/atelier-discovery-harness/SKILL.md +48 -0
- package/fixtures/inquiry-packages/research-harness/capability-package.json +61 -0
- package/fixtures/inquiry-packages/research-harness/capability-release.json +82 -0
- package/fixtures/inquiry-packages/research-harness/evaluation.md +3 -0
- package/fixtures/inquiry-packages/research-harness/skills/atelier-research-harness/SKILL.md +43 -0
- package/fixtures/knowledge-health/calibration-suite.json +26 -0
- package/fixtures/knowledge-health/calibration-trial.json +37 -0
- package/fixtures/knowledge-health/corrected/checklist.md +18 -0
- package/fixtures/knowledge-health/devday/participant-record.json +19 -0
- package/fixtures/knowledge-health/expected-summary.json +27 -0
- package/fixtures/knowledge-health/workshop/README.md +20 -0
- package/fixtures/knowledge-health/workshop/atelier.project.json +7 -0
- package/fixtures/knowledge-health/workshop/knowledge-plan.json +65 -0
- package/fixtures/knowledge-health/workshop/records/checklist.md +18 -0
- package/fixtures/knowledge-health/workshop/records/workshop.md +18 -0
- package/fixtures/knowledge-health/workshop/repo-access.v1.json +5 -0
- package/fixtures/knowledge-health/workspace/atelier.project.json +18 -0
- package/fixtures/knowledge-health/workspace/knowledge-plan.json +74 -0
- package/fixtures/knowledge-health/workspace/records/checklist.md +18 -0
- package/fixtures/knowledge-health/workspace/records/pilot.md +16 -0
- package/fixtures/knowledge-health/workspace/repo-access.v1.json +9 -0
- package/fixtures/personal-workspace/overlay.json +24 -0
- package/fixtures/templates/local-library.v1.json +76 -0
- package/fixtures/templates/upgrade-contracts/adoption-policy.v2/invalid.json +16 -0
- package/fixtures/templates/upgrade-contracts/adoption-policy.v2/valid.json +13 -0
- package/fixtures/templates/upgrade-contracts/migration.v3/invalid.json +24 -0
- package/fixtures/templates/upgrade-contracts/migration.v3/valid.json +21 -0
- package/fixtures/templates/upgrade-contracts/template-adoption.v1/invalid.json +60 -0
- package/fixtures/templates/upgrade-contracts/template-adoption.v1/valid.json +57 -0
- package/fixtures/templates/upgrade-contracts/upgrade-plan.v3/invalid.json +85 -0
- package/fixtures/templates/upgrade-contracts/upgrade-plan.v3/valid.json +82 -0
- package/package.json +60 -5
- package/plugins/obsidian/main.js +57 -8
- package/plugins/obsidian/manifest.json +1 -1
- package/skills/claude/atelier-build-harness/SKILL.md +59 -0
- package/skills/claude/atelier-coordination/SKILL.md +32 -0
- package/skills/claude/atelier-discovery-harness/SKILL.md +48 -0
- package/skills/claude/atelier-guided-upgrade/SKILL.md +29 -0
- package/skills/claude/atelier-interaction/SKILL.md +34 -0
- package/skills/claude/atelier-knowledge-harness/SKILL.md +60 -0
- package/skills/claude/atelier-knowledge-setup/SKILL.md +72 -0
- package/skills/claude/atelier-practical-judgment/SKILL.md +64 -0
- package/skills/claude/atelier-research-harness/SKILL.md +43 -0
- package/skills/claude/atelier-skill-steward/SKILL.md +53 -0
- package/skills/claude/atelier-trackables/SKILL.md +40 -0
- package/skills/codex/atelier-build-harness/SKILL.md +59 -0
- package/skills/codex/atelier-coordination/SKILL.md +32 -0
- package/skills/codex/atelier-discovery-harness/SKILL.md +48 -0
- package/skills/codex/atelier-guided-upgrade/SKILL.md +29 -0
- package/skills/codex/atelier-interaction/SKILL.md +34 -0
- package/skills/codex/atelier-knowledge-harness/SKILL.md +60 -0
- package/skills/codex/atelier-knowledge-setup/SKILL.md +72 -0
- package/skills/codex/atelier-practical-judgment/SKILL.md +64 -0
- package/skills/codex/atelier-research-harness/SKILL.md +43 -0
- package/skills/codex/atelier-skill-steward/SKILL.md +53 -0
- package/skills/codex/atelier-trackables/SKILL.md +40 -0
- package/src/architecture/catalog.mjs +355 -0
- package/src/architecture/index.mjs +93 -0
- package/src/boundary/content-rules.mjs +536 -69
- package/src/boundary/delegation-command.mjs +158 -0
- package/src/boundary/delegation.mjs +396 -0
- package/src/boundary/policy.mjs +185 -64
- package/src/build/git.mjs +18 -0
- package/src/build/index.mjs +3 -0
- package/src/build/ledger.mjs +88 -0
- package/src/capabilities/adoption.mjs +381 -0
- package/src/capabilities/evidence.mjs +140 -0
- package/src/capabilities/files.mjs +124 -0
- package/src/capabilities/index.mjs +4 -0
- package/src/capabilities/instructions.mjs +250 -0
- package/src/capabilities/package.mjs +136 -0
- package/src/cli/command-failure.mjs +36 -0
- package/src/cli/execute-command.mjs +16 -2
- package/src/cli/run.mjs +66 -9
- package/src/coauthor/store.mjs +43 -2
- package/src/collaboration/proposals.mjs +189 -0
- package/src/commands/architecture.mjs +27 -0
- package/src/commands/boundary.mjs +3 -0
- package/src/commands/capability.mjs +73 -0
- package/src/commands/enroll.mjs +3 -0
- package/src/commands/harness.mjs +79 -0
- package/src/commands/ingest.mjs +39 -0
- package/src/commands/init.mjs +4 -1
- package/src/commands/inquiry.mjs +66 -0
- package/src/commands/knowledge-semantic.mjs +128 -0
- package/src/commands/knowledge.mjs +119 -0
- package/src/commands/learn.mjs +61 -0
- package/src/commands/obsidian.mjs +772 -65
- package/src/commands/practice.mjs +24 -0
- package/src/commands/setup.mjs +22 -2
- package/src/commands/trackable.mjs +19 -0
- package/src/contracts/corpus.mjs +30 -0
- package/src/coordination/index.mjs +110 -0
- package/src/evidence-navigation/contracts.mjs +60 -0
- package/src/evidence-navigation/evaluate.mjs +69 -0
- package/src/evidence-navigation/generation.mjs +22 -0
- package/src/evidence-navigation/index.mjs +2 -0
- package/src/evidence-navigation/local.mjs +167 -0
- package/src/evidence-navigation/schema.generated.mjs +604 -0
- package/src/evidence-navigation/types.mjs +12 -0
- package/src/export/atelier-export-contract.mjs +35 -8
- package/src/graph/enroll.mjs +248 -0
- package/src/graph/graph.mjs +30 -6
- package/src/graph/knowledge-graph.mjs +77 -9
- package/src/harnesses/contracts.mjs +41 -0
- package/src/harnesses/exchange.mjs +154 -0
- package/src/harnesses/feedback.mjs +16 -0
- package/src/harnesses/history.mjs +37 -0
- package/src/harnesses/index.mjs +4 -0
- package/src/harnesses/store.mjs +29 -0
- package/src/ingestion/contracts.mjs +41 -0
- package/src/ingestion/evaluation.mjs +119 -0
- package/src/ingestion/optional/composition/index.mjs +171 -0
- package/src/ingestion/optional/evaluation/index.mjs +219 -0
- package/src/ingestion/optional/graphify/composition-binding.mjs +60 -0
- package/src/ingestion/optional/graphify/composition_receiving.py +185 -0
- package/src/ingestion/optional/graphify/graphify_supplier.py +938 -0
- package/src/ingestion/optional/graphify/json_bridge.py +163 -0
- package/src/ingestion/optional/graphify/native-closure-pins.json +275 -0
- package/src/ingestion/optional/graphify/native-pins.json +306 -0
- package/src/ingestion/optional/graphify/query_receiving.py +209 -0
- package/src/ingestion/optional/index.mjs +9 -0
- package/src/ingestion/optional/jev/bridge.mjs +166 -0
- package/src/ingestion/optional/jev/composition-binding.mjs +116 -0
- package/src/ingestion/optional/jev/judgments.mjs +174 -0
- package/src/ingestion/optional/jev/native-translation.mjs +32 -0
- package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/__init__.py +7 -0
- package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/adapter.py +966 -0
- package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/artifacts.py +101 -0
- package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/projection.py +146 -0
- package/src/ingestion/optional/lightrag/composition-binding.mjs +67 -0
- package/src/ingestion/optional/vanilla/composition-binding.mjs +102 -0
- package/src/ingestion/optional/vanilla/index.mjs +218 -0
- package/src/ingestion/optional/vanilla/native-normalization.mjs +187 -0
- package/src/ingestion/processors.mjs +218 -0
- package/src/ingestion/semantic.mjs +203 -0
- package/src/ingestion/store.mjs +372 -0
- package/src/inquiry/beliefs.mjs +32 -0
- package/src/inquiry/contracts.mjs +23 -0
- package/src/inquiry/index.mjs +5 -0
- package/src/inquiry/ledger.mjs +135 -0
- package/src/inquiry/lenses.mjs +15 -0
- package/src/inquiry/projection.mjs +77 -0
- package/src/inquiry/store.mjs +35 -0
- package/src/intake/read-scope.mjs +12 -0
- package/src/intake/store.mjs +69 -6
- package/src/interaction/index.mjs +91 -0
- package/src/judgment/index.mjs +82 -0
- package/src/judgment/practice-evaluation.mjs +70 -0
- package/src/judgment/practice.mjs +144 -0
- package/src/knowledge/context.mjs +217 -0
- package/src/knowledge/index.mjs +5 -0
- package/src/knowledge/ingestion.mjs +79 -0
- package/src/knowledge/intake.mjs +33 -0
- package/src/knowledge/ledger.mjs +110 -0
- package/src/knowledge/participatory/guidance.mjs +36 -0
- package/src/knowledge/participatory/guided-contribution.mjs +44 -0
- package/src/knowledge/participatory/index.mjs +2 -0
- package/src/knowledge/participatory/installed-workshop.mjs +108 -0
- package/src/knowledge/plan.mjs +67 -0
- package/src/knowledge/projection.mjs +43 -0
- package/src/knowledge/semantic-operation.mjs +567 -0
- package/src/knowledge/sessions.mjs +467 -0
- package/src/knowledge/workspace.mjs +295 -0
- package/src/knowledge-health/assessment.mjs +165 -0
- package/src/knowledge-health/capabilities.mjs +350 -0
- package/src/knowledge-health/context.mjs +62 -0
- package/src/knowledge-health/example.mjs +47 -0
- package/src/knowledge-health/index.mjs +10 -0
- package/src/knowledge-health/owner-join.mjs +88 -0
- package/src/knowledge-health/proof/common.mjs +24 -0
- package/src/knowledge-health/proof/installed-check.mjs +100 -0
- package/src/knowledge-health/proof/native-readback.mjs +37 -0
- package/src/knowledge-health/proof/rehearse.mjs +68 -0
- package/src/knowledge-health/proof/validate-participant.mjs +17 -0
- package/src/knowledge-health/proof/verify-native-capture.mjs +14 -0
- package/src/knowledge-health/proof/workshop-check.mjs +36 -0
- package/src/knowledge-health/rule-identity.mjs +49 -0
- package/src/knowledge-health/workshop-composition.mjs +19 -0
- package/src/knowledge-health/workshop-profile.mjs +176 -0
- package/src/knowledge-health/workshop.mjs +99 -0
- package/src/learning/adapters.mjs +68 -0
- package/src/learning/assistance.mjs +127 -0
- package/src/learning/contracts.mjs +65 -0
- package/src/learning/store.mjs +272 -0
- package/src/personal-workspace/index.mjs +391 -0
- package/src/personal-workspace/selection.mjs +401 -0
- package/src/project/config.mjs +8 -0
- package/src/project/durable-state.mjs +3 -1
- package/src/project/file-class.mjs +1 -1
- package/src/project/path-match.mjs +2 -1
- package/src/projection/obsidian/edits/apply.mjs +55 -16
- package/src/projection/obsidian/materialize/prepare-view.mjs +87 -5
- package/src/projection/obsidian/personal-workspace.mjs +519 -0
- package/src/projection/obsidian/proposals/adapter.mjs +79 -17
- package/src/projection/obsidian/proposals/observation.mjs +50 -4
- package/src/projection/obsidian/publication/publisher.mjs +214 -18
- package/src/projection/obsidian/publication/vault-list.mjs +4 -3
- package/src/projection/obsidian/recovery/index.mjs +4 -1
- package/src/projection/obsidian/recovery/store.mjs +160 -7
- package/src/projection/obsidian/selection-ui/contribution.mjs +6 -5
- package/src/projection/project.mjs +3 -2
- package/src/readiness/readiness.mjs +10 -3
- package/src/reflection/index.mjs +56 -0
- package/src/runtime/obsidian/app-production-seams.mjs +48 -16
- package/src/runtime/obsidian/app-registration.mjs +175 -28
- package/src/runtime/obsidian/app-restart.mjs +154 -0
- package/src/runtime/obsidian/engine.mjs +437 -56
- package/src/runtime/obsidian/extension-points.mjs +1 -1
- package/src/runtime/obsidian/index.mjs +10 -7
- package/src/runtime/obsidian/launch-plan.mjs +7 -4
- package/src/runtime/obsidian/lifecycle.mjs +108 -23
- package/src/runtime/obsidian/login-item.mjs +335 -0
- package/src/runtime/obsidian/machine-settings.mjs +119 -12
- package/src/runtime/obsidian/observation.mjs +36 -4
- package/src/runtime/obsidian/opening.mjs +173 -38
- package/src/runtime/obsidian/personal-selection.mjs +125 -0
- package/src/runtime/obsidian/pipeline.mjs +115 -6
- package/src/runtime/obsidian/plugin-channel.mjs +11 -3
- package/src/runtime/obsidian/plugin-choice.mjs +16 -4
- package/src/runtime/obsidian/plugin-drift.mjs +59 -11
- package/src/runtime/obsidian/plugin-presence.mjs +14 -1
- package/src/runtime/obsidian/private-lock.mjs +39 -4
- package/src/runtime/obsidian/process-identity.mjs +20 -10
- package/src/runtime/obsidian/project-views.mjs +282 -0
- package/src/runtime/obsidian/questions.mjs +48 -0
- package/src/runtime/obsidian/release-watch.mjs +56 -0
- package/src/runtime/obsidian/service-main.mjs +148 -19
- package/src/runtime/obsidian/service-manager-production.mjs +61 -0
- package/src/runtime/obsidian/service-managers.mjs +275 -0
- package/src/runtime/obsidian/service-record.mjs +87 -23
- package/src/runtime/obsidian/service.mjs +102 -9
- package/src/runtime/obsidian/startup-adapters.mjs +59 -21
- package/src/runtime/obsidian/unheld-evidence.mjs +84 -0
- package/src/runtime/obsidian/vault-location.mjs +300 -0
- package/src/runtime/obsidian/view-counts.mjs +34 -0
- package/src/runtime/obsidian/view-permission.mjs +334 -0
- package/src/server/local-sidecar.mjs +169 -2
- package/src/server/server.mjs +28 -2
- package/src/template-bindings/project.mjs +92 -0
- package/src/templates/profile.mjs +225 -0
- package/src/trackables/domain.mjs +175 -0
- package/src/trackables/store.mjs +114 -0
- package/src/ui/control-system.mjs +201 -0
- package/src/ui/html-primitives.mjs +3 -0
- package/src/ui/knowledge-page.mjs +1388 -0
- package/src/upgrade/closure-diagnostics.mjs +135 -0
- package/src/upgrade/closure.mjs +649 -0
- package/src/upgrade/explanation.mjs +1 -1
- package/src/upgrade/template-participant.mjs +171 -0
- package/src/upgrade/transaction.mjs +72 -25
- package/src/upgrade/upgrade.mjs +20 -7
- package/templates/external-project-workspace/pack-lifecycle.json +7 -1
- package/templates/knowledge-workspace/.gitattributes +2 -0
- package/templates/knowledge-workspace/AGENTS.md +27 -0
- package/templates/knowledge-workspace/README.md +44 -0
- package/templates/knowledge-workspace/atelier.project.json +22 -0
- package/templates/knowledge-workspace/gitignore +6 -0
- package/templates/knowledge-workspace/knowledge-plan.json +111 -0
- package/templates/knowledge-workspace/knowledge-plan.schema.json +281 -0
- package/templates/knowledge-workspace/records/forecast.md +22 -0
- package/templates/knowledge-workspace/records/inspection.md +25 -0
- package/templates/knowledge-workspace/records/telescope.md +24 -0
- package/templates/knowledge-workspace/repo-access.v1.json +9 -0
package/docs/obsidian.md
CHANGED
|
@@ -282,18 +282,30 @@ below is the shipped `atelier obsidian` usage text; `atelier obsidian --help`
|
|
|
282
282
|
prints it. There are three steps, and none of them is done by hand in
|
|
283
283
|
Obsidian.
|
|
284
284
|
|
|
285
|
-
1.
|
|
286
|
-
`
|
|
285
|
+
1. Declare a view in the project configuration:
|
|
286
|
+
`atelier obsidian view add everything --all`. It adds a view of every note
|
|
287
|
+
the machine may show to `atelier.project.json`, shows the change as a diff
|
|
288
|
+
and how many notes the view would show, and makes it the default view. You
|
|
289
|
+
commit the file; Atelier commits nothing. See
|
|
290
|
+
[Adding a view](#adding-a-view). The member it writes,
|
|
291
|
+
`ext["mnstry.atelier.obsidian"]`, is an `atelier-obsidian-ext-settings/v1`
|
|
287
292
|
document: `enabled: true`, `scopes` (each with a `scopeId`, a `mode` of
|
|
288
293
|
`full`, `scoped` or `focus`, a `selector`, and for an expansion an explicit
|
|
289
|
-
`depth` and `maxNodes`), and optionally `defaultScopeId`.
|
|
290
|
-
|
|
291
|
-
`
|
|
294
|
+
`depth` and `maxNodes`), and optionally `defaultScopeId`. It can also be
|
|
295
|
+
written by hand. A project without this member is
|
|
296
|
+
`disabled (not-configured)` and nothing is published.
|
|
297
|
+
`atelier obsidian view list` (or `scope list`) shows what was declared.
|
|
292
298
|
2. Set the private machine settings. They live outside every repository and
|
|
293
299
|
are never committed:
|
|
294
|
-
- `atelier obsidian audience set
|
|
295
|
-
|
|
296
|
-
|
|
300
|
+
- `atelier obsidian audience set me` lets "only you" into a view: every
|
|
301
|
+
audience of a note but `sensitive`, and the notes that carry no
|
|
302
|
+
classification, which only a vault that is yours alone shows.
|
|
303
|
+
`audience set A,B` names the audiences instead, and never shows notes
|
|
304
|
+
without a classification; `sensitive` is added by name
|
|
305
|
+
(`audience set me,sensitive`, which is such a list). No audience is
|
|
306
|
+
admitted by default, so a view is empty until one is set;
|
|
307
|
+
`audience clear` empties it again. See
|
|
308
|
+
[Notes without a classification](#notes-without-a-classification).
|
|
297
309
|
- `atelier obsidian mode set manual` keeps every queued edit waiting for a
|
|
298
310
|
person. `mode set automatic` is refused until an active automatic policy
|
|
299
311
|
is installed.
|
|
@@ -302,11 +314,14 @@ Obsidian.
|
|
|
302
314
|
unchanged; `policy revoke` returns the mode to `manual` and stops every
|
|
303
315
|
later apply.
|
|
304
316
|
3. Open a view:
|
|
305
|
-
`atelier obsidian open --scope ID --
|
|
317
|
+
`atelier obsidian open --scope ID --adapter=obsidian-cli`.
|
|
306
318
|
That's it. Reaching the installed app is never a default, so
|
|
307
|
-
`--adapter=obsidian-cli` is
|
|
308
|
-
|
|
309
|
-
|
|
319
|
+
`--adapter=obsidian-cli` is needed the first time; the workspace remembers
|
|
320
|
+
it, and later runs need no flag. The first open of a workspace records who
|
|
321
|
+
allowed the maintenance service to run: for a person at a terminal, the
|
|
322
|
+
account's name; for a program, `--consent-actor ID`, which it must pass.
|
|
323
|
+
Later opens reconnect and need neither. `atelier obsidian settings` shows
|
|
324
|
+
what was remembered and how to change it. `open` starts or
|
|
310
325
|
reconnects maintenance and asks it for a tick, makes Obsidian (version
|
|
311
326
|
1.13.7 or later) know the view's vault as one of its vaults, opens it,
|
|
312
327
|
publishes the view, verifies the vault by reading it back, and waits until
|
|
@@ -315,26 +330,227 @@ Obsidian.
|
|
|
315
330
|
[What `open` does in each state of Obsidian](#what-open-does-in-each-state-of-obsidian).
|
|
316
331
|
|
|
317
332
|
The maintenance service can also be managed on its own:
|
|
318
|
-
`atelier obsidian service start --consent-actor ID --adapter=obsidian-cli`,
|
|
319
|
-
`service status` and `service stop`; `service unit --print
|
|
320
|
-
|
|
333
|
+
`atelier obsidian service start [--consent-actor ID] [--adapter=obsidian-cli]`,
|
|
334
|
+
`service status` and `service stop`; `service unit --print` prints the login
|
|
335
|
+
item that `service unit --install` would write, and installs nothing (see
|
|
336
|
+
[Start at login](#start-at-login)). The flags are needed only until the
|
|
337
|
+
workspace remembers them, as for `open`.
|
|
338
|
+
|
|
339
|
+
### Adding a view
|
|
340
|
+
|
|
341
|
+
```sh
|
|
342
|
+
atelier obsidian view add ID (--all | --folder PATH [--folder PATH ...] [--repo R] | --tag T) [--expand DEPTH:MAX] [--default] [--allow-empty] [--yes]
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
- `--all` is a view of every note (`full`). `--folder` takes the notes under a
|
|
346
|
+
folder of a repository, written from the repository's root (`docs/notes`),
|
|
347
|
+
and can be given as often as needed; `--repo R` names the repository when
|
|
348
|
+
the project has more than one. `--tag T` takes the notes with a tag. Each of
|
|
349
|
+
these is a `scoped` view, and `--expand DEPTH:MAX` adds the notes they link
|
|
350
|
+
to, following links up to DEPTH steps and adding at most MAX notes.
|
|
351
|
+
- The first view declared is the default; `--default` makes a later one the
|
|
352
|
+
default.
|
|
353
|
+
- Before writing, the command counts the notes the view would show on this
|
|
354
|
+
machine, as the machine decided who may see (as "only you", with the notes
|
|
355
|
+
without a classification it shows, while nobody decided), and names how
|
|
356
|
+
many of the notes it selects are withheld: those without a classification
|
|
357
|
+
(a `kg` block) that the decision does not show, and those whose audience is
|
|
358
|
+
not admitted. A view that would show no note is refused with
|
|
359
|
+
`view-would-be-empty` and the counts; `--allow-empty` declares it anyway.
|
|
360
|
+
When the machine admits no audience at all (`audience clear`), the refusal
|
|
361
|
+
says so and names `atelier obsidian audience set me`.
|
|
362
|
+
- While nobody decided who may see, the maintenance service publishes no
|
|
363
|
+
note, whatever the counts say: the next step `view add` names is then
|
|
364
|
+
`atelier obsidian audience set me`, before `atelier obsidian open` (`next`
|
|
365
|
+
under `--json`, in order).
|
|
366
|
+
- A person at a terminal sees the change and the counts and is asked
|
|
367
|
+
`Write this change? [Y/n]`; `--yes` answers beforehand, and no answer within
|
|
368
|
+
10 minutes writes nothing (`unanswered`). For anyone else the command is the
|
|
369
|
+
consent: the change is made, and printed (`diff` under `--json`).
|
|
370
|
+
- The file is rewritten only when it is in the form Atelier writes JSON in
|
|
371
|
+
(two-space indentation, LF or CRLF line ends, with or without a final one),
|
|
372
|
+
so the change is the new view and nothing else. A file in another form is
|
|
373
|
+
refused with `project-config-format-unknown`, and the member to add by hand
|
|
374
|
+
is in the refusal's `detail`. The file is replaced atomically, keeps its
|
|
375
|
+
mode, and only while it holds the bytes the change was made from
|
|
376
|
+
(`project-config-changed` otherwise). A link is not followed
|
|
377
|
+
(`project-file-not-regular`).
|
|
378
|
+
- The workspace pointer is only ever written into an ignored `.atelier-local/`.
|
|
379
|
+
When that folder is not ignored, the same change adds `.atelier-local/` to
|
|
380
|
+
the project's `.gitignore`, and shows it.
|
|
381
|
+
- A name that is taken is refused with `view-exists`. Removing a view is done
|
|
382
|
+
by hand for now.
|
|
383
|
+
- The contract's `type` selector matches a note's file type (`markdown`,
|
|
384
|
+
`html`), not its `kg.type`, so `view add` does not offer it; a view by
|
|
385
|
+
`kg.type` needs a contract change.
|
|
386
|
+
|
|
387
|
+
### Notes without a classification
|
|
388
|
+
|
|
389
|
+
A note whose front matter carries no `kg` block (or that has no front matter)
|
|
390
|
+
is unclassified: the knowledge graph gives it the `private` audience, and by
|
|
391
|
+
default no view shows it. A vault that is only yours shows it: the audience
|
|
392
|
+
decision `only-you` with `unclassified: shown`, which `audience set me`
|
|
393
|
+
records. A list of audiences never shows one, even a list that names every
|
|
394
|
+
audience "only you" stands for, so a vault for anyone else can never carry
|
|
395
|
+
them.
|
|
396
|
+
|
|
397
|
+
A note whose labels cannot be known is never shown, in any vault: front matter
|
|
398
|
+
Atelier cannot read (a block scalar such as `description: |`, a value wrapped
|
|
399
|
+
onto a second line), front matter that is not plain `key: value` lines at its
|
|
400
|
+
top level (a quoted key such as `"kg":`, a one-line flow or JSON mapping, a
|
|
401
|
+
complex key), or any top-level `kg` key that is not a block. Such a note may
|
|
402
|
+
say `sensitive`, which "only you" leaves out. Only a note with no front
|
|
403
|
+
matter, or with front matter of plain top-level keys at the start of the line,
|
|
404
|
+
none of which is `kg`, counts as one without a classification. A plain key is
|
|
405
|
+
written with ASCII letters, digits, `_` and `-`, starting with a letter or
|
|
406
|
+
`_`: a note whose front matter uses a key with a space (`due date:`), other
|
|
407
|
+
letters (`título:`), a dot or a leading digit, or a quoted key, stays withheld
|
|
408
|
+
even when it holds nothing sensitive. Give it a `kg` block to show it.
|
|
409
|
+
|
|
410
|
+
In an "only you" vault such a note is shown only when its bytes read as a note
|
|
411
|
+
the way the emitter reads every note, so one file that cannot be published
|
|
412
|
+
(front matter that opens and never closes, closes at once, or carries a
|
|
413
|
+
trailing space on its first line; bytes that are not UTF-8) never stops the
|
|
414
|
+
vault: it stays withheld, as does a file that cannot be read. A shown note is
|
|
415
|
+
part of the view in every respect: links to it are links inside the view,
|
|
416
|
+
generated text may name it, the selection operation resolves it, and an edit
|
|
417
|
+
to it is applied to its source like any other.
|
|
418
|
+
|
|
419
|
+
A decision recorded before this release, "only you" with those notes
|
|
420
|
+
withheld, keeps them withheld until `audience set me` is run again. Changing
|
|
421
|
+
the decision rebuilds every view at the next tick (the eligibility revision
|
|
422
|
+
changes).
|
|
423
|
+
|
|
424
|
+
### What this machine remembers
|
|
425
|
+
|
|
426
|
+
A workspace's machine settings (`atelier-obsidian-machine-settings/v2`, owner
|
|
427
|
+
only, under the private data root, never in a repository) hold what a person
|
|
428
|
+
decided once, so no later run asks again or has to be told again. Each
|
|
429
|
+
decision is null until it is made, and otherwise records what was decided,
|
|
430
|
+
when, by whom when that is known (an actor identifier), and how: answered at
|
|
431
|
+
a terminal (`question`), given on the command line (`command`), taken as the
|
|
432
|
+
defaults (`defaults`), or carried over from an earlier release (`v1`).
|
|
433
|
+
|
|
434
|
+
| Decision | Holds | Made today by |
|
|
435
|
+
| --- | --- | --- |
|
|
436
|
+
| `audience` | `only-you` or `custom`, and whether notes that carry no classification are `shown` or `withheld`. Only `only-you` may show them; a list of audiences always withholds them. The admitted list stays in `audienceAllow`, the engine's audience input | `audience set me` (only you, shown) or `audience set A,B` / `audience clear` (withheld) |
|
|
437
|
+
| `location` | the absolute folder that holds this workspace's vaults | `location set DIR` |
|
|
438
|
+
| `loginItem` | `on` or `off` | `service unit --install` (on); `service unit --remove` and `uninstall` (off) |
|
|
439
|
+
| `adapter` | `obsidian-cli` | `--adapter=obsidian-cli` given to `open`, `service start` or `service unit --install` |
|
|
440
|
+
|
|
441
|
+
Who allowed the maintenance service is not a second copy here: it stays the
|
|
442
|
+
consent the service reads from its own settings. `atelier obsidian settings`
|
|
443
|
+
shows all of it, and how each answer is changed.
|
|
444
|
+
|
|
445
|
+
Releases up to 0.2.0-alpha.12 wrote `atelier-obsidian-machine-settings/v1`.
|
|
446
|
+
Such a file is read as the v2 document it stands for, an audience list set
|
|
447
|
+
with it becoming that person's decision, and stays v1 on disk until the next
|
|
448
|
+
write, which writes v2. A release that knows only v1 refuses a v2 file as
|
|
449
|
+
`invalid-machine-settings`: after an upgrade, a maintenance service that
|
|
450
|
+
still runs the earlier release fails its ticks until it is replaced, which
|
|
451
|
+
`atelier obsidian open` and `atelier obsidian service start` do.
|
|
452
|
+
|
|
453
|
+
### Start at login
|
|
454
|
+
|
|
455
|
+
Without a login item the vault is complete after a restart, but nothing keeps
|
|
456
|
+
it fresh until the next `open`. A login item starts the maintenance service
|
|
457
|
+
when you log in:
|
|
458
|
+
|
|
459
|
+
```sh
|
|
460
|
+
atelier obsidian service unit --install [--consent-actor ID] [--adapter=obsidian-cli]
|
|
461
|
+
atelier obsidian service unit --print # what --install writes; writes nothing
|
|
462
|
+
atelier obsidian service unit --remove
|
|
463
|
+
atelier obsidian uninstall
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
- On macOS it is a launchd agent,
|
|
467
|
+
`~/Library/LaunchAgents/ai.mnstry.atelier.<project>.<workspace-id>.plist`.
|
|
468
|
+
macOS says "Background Items Added" and lists it as "node" under System
|
|
469
|
+
Settings → General → Login Items & Extensions, because there is no app to
|
|
470
|
+
name it after. Switched off there, it stays off: `status` reports
|
|
471
|
+
`installed, switched off in System Settings`, and `open` starts the service
|
|
472
|
+
for that session only.
|
|
473
|
+
- On Linux it is a systemd user unit,
|
|
474
|
+
`~/.config/systemd/user/atelier-obsidian-<workspace-id>.service`
|
|
475
|
+
(`$XDG_CONFIG_HOME` when set), enabled with `systemctl --user`. Disabled
|
|
476
|
+
with `systemctl --user disable`, it stays disabled: `status` reports
|
|
477
|
+
`installed, disabled`, and `open` starts the service for that session
|
|
478
|
+
only. Where no user instance of systemd answers (WSL, a container), the
|
|
479
|
+
answer is `login-item-unavailable` and nothing is left behind.
|
|
480
|
+
- Windows is not offered (`startup-platform-unqualified`), as publication
|
|
481
|
+
itself is refused there.
|
|
482
|
+
|
|
483
|
+
It runs the package installed in the project
|
|
484
|
+
(`<project>/node_modules/@mnstry/atelier/…`, named by that path, so the next
|
|
485
|
+
start runs whatever release the project has installed then), with the Node
|
|
486
|
+
that installed it and the search path you had, in the root folder. It names
|
|
487
|
+
the data folder and the workspace it was installed for, so it finds them at
|
|
488
|
+
login whatever variables your shell set. Run from
|
|
489
|
+
`npx` in a project that has no package installed, it refuses with
|
|
490
|
+
`login-item-needs-installed-package`: install `@mnstry/atelier` in the project
|
|
491
|
+
first. Installing records that you allowed the service to run at login (the
|
|
492
|
+
consent then covers startup): `--consent-actor ID` names who allows it; for a
|
|
493
|
+
person at a terminal, the actor already recorded for this workspace, or else
|
|
494
|
+
the account's name. A program that passes no `--consent-actor` for a
|
|
495
|
+
workspace whose consent covers the service alone is refused
|
|
496
|
+
(`startup-consent-required`). The answer is remembered as the `loginItem`
|
|
497
|
+
decision. `--remove` takes the consent back to the service alone, removes the
|
|
498
|
+
item, and starts the service again for this session only, as a process of its
|
|
499
|
+
own (with the adapter given or remembered; otherwise the next `open` starts
|
|
500
|
+
it). Each workspace has its own login item, port, log and consent, and
|
|
501
|
+
projects may pin different releases; `launchctl list | grep ai.mnstry.atelier`
|
|
502
|
+
lists every one on a Mac.
|
|
503
|
+
|
|
504
|
+
Once a login item is installed, the service is started through it: `service
|
|
505
|
+
start`, `open` and the replacement of an outdated service ask launchd or
|
|
506
|
+
systemd to start it, never start a second one beside it, and accept only the
|
|
507
|
+
service that proves itself with the installed entry. When the unit differs from
|
|
508
|
+
what would be written now (the Node it names was removed, say), the next start
|
|
509
|
+
writes it again and says `login item refreshed`. A refusal of the service at
|
|
510
|
+
login (another service already runs, or the consent does not cover startup)
|
|
511
|
+
ends the process cleanly, so it is not retried every minute, and `status` says
|
|
512
|
+
`the login item did not start the service: <code>` with the next step. A crash
|
|
513
|
+
is restarted after a minute.
|
|
514
|
+
|
|
515
|
+
`uninstall` removes the login item and stops the service. It keeps the vaults,
|
|
516
|
+
the private state, the project file and Obsidian's vault list as they are, and
|
|
517
|
+
prints where each is: every vault under the data root and every folder
|
|
518
|
+
allocated to a view where the workspace decided its vaults live, a view no
|
|
519
|
+
longer declared included. It does not start the service again.
|
|
321
520
|
|
|
322
521
|
After an upgrade of Atelier, a maintenance service started earlier still runs
|
|
323
|
-
the earlier release.
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
the
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
do
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
522
|
+
the earlier release. One the login item started notices it: after each tick
|
|
523
|
+
it compares the package on disk (read through the path the item names, so a
|
|
524
|
+
re-pointed link or a `file:` install counts) with the release it started
|
|
525
|
+
with, and when they differ it exits after that tick with code 75, and its
|
|
526
|
+
service manager starts the new release within a minute. Otherwise `open` and
|
|
527
|
+
`service start` replace it: when the service of this workspace proves itself
|
|
528
|
+
Atelier's own but runs an earlier release than the installed one (its record
|
|
529
|
+
names the release it runs: the package version and a digest of its modules;
|
|
530
|
+
an earlier version, the same version with other modules, or no release
|
|
531
|
+
named), or refuses a tick that names a view as releases up to 0.2.0-alpha.11
|
|
532
|
+
do, they stop it as `service stop` would and start the installed release under
|
|
533
|
+
the consent already recorded (through the login item when there is one), and
|
|
534
|
+
say `service: restarted (outdated)`. The installed release is read from the
|
|
535
|
+
package of the entry that would be started, so with a login item it is the
|
|
536
|
+
project's own package. A service of a later release is never replaced by an
|
|
537
|
+
earlier one, so two installations used on one workspace (a global and a
|
|
538
|
+
project-local one, say) do not replace each other's service on every open:
|
|
539
|
+
`open` answers `service-unavailable` / `service-other-release`, and `service
|
|
540
|
+
start` answers that it runs, with `release: later`; run `atelier obsidian
|
|
541
|
+
service stop`, then open again, or open with the later release. A service that
|
|
542
|
+
does not answer health in time, in a stretch of a tick that does not yield, is
|
|
543
|
+
not stopped (`open` answers `busy`), and a listener that does not prove itself
|
|
544
|
+
this workspace's service is never touched. While a view is being prepared and
|
|
545
|
+
published the service does answer: `open` asks it for a tick that starts after
|
|
546
|
+
the one in flight and waits for that tick, two minutes at most unless
|
|
547
|
+
`atelier obsidian open --wait-ms MS` sets another bound, before it reports the
|
|
548
|
+
view as it is (one that read `current` is reported `updating`, with the reason
|
|
549
|
+
`tick-still-running`, while that tick has not ended); and `service stop` is
|
|
550
|
+
accepted and ends the
|
|
551
|
+
service after the tick in flight or after its shutdown grace, whichever comes
|
|
552
|
+
first. A publication cut short that way is settled by the publisher's restart
|
|
553
|
+
recovery at the next one.
|
|
338
554
|
|
|
339
555
|
The first time Obsidian opens a view's vault it asks "Do you trust the author
|
|
340
556
|
of this vault?", because every vault Atelier publishes carries Atelier's own
|
|
@@ -377,13 +593,79 @@ proposals list` and `proposals show OPERATION` read them, and nothing applies
|
|
|
377
593
|
one. A body edit to a note whose source uses CRLF line endings is also a
|
|
378
594
|
proposal, never an apply, because the app normalizes line endings on save.
|
|
379
595
|
|
|
380
|
-
|
|
381
|
-
`~/Library/Application Support/Atelier` on macOS,
|
|
382
|
-
(else `~/.local/share/atelier`) on Linux and
|
|
383
|
-
Windows, under `obsidian/<workspace-id>/`.
|
|
384
|
-
absolute directory. Private state inside an
|
|
385
|
-
Publication is proven on macOS arm64 only and
|
|
386
|
-
[Known limits](#known-limits).
|
|
596
|
+
Atelier's private state lives under the private data root, outside every
|
|
597
|
+
repository: `~/Library/Application Support/Atelier` on macOS,
|
|
598
|
+
`$XDG_DATA_HOME/atelier` (else `~/.local/share/atelier`) on Linux and
|
|
599
|
+
`%LOCALAPPDATA%\Atelier` on Windows, under `obsidian/<workspace-id>/`.
|
|
600
|
+
`--data-root DIR` names another absolute directory. Private state inside an
|
|
601
|
+
enrolled repository is refused. Publication is proven on macOS arm64 only and
|
|
602
|
+
is refused on Windows; see [Known limits](#known-limits).
|
|
603
|
+
|
|
604
|
+
Where the vaults are is decided per workspace. Until a person decides it, a
|
|
605
|
+
view's vault is under the data root too, in `vaults/<view>`, as in every
|
|
606
|
+
earlier release. `atelier obsidian location set DIR` decides a visible folder
|
|
607
|
+
instead (`~/Atelier`, say). Each view that has not been published yet is then
|
|
608
|
+
given its own folder there at its first publication, named
|
|
609
|
+
`<project> (<view>)`: `~/Atelier/harbor-notes (everything)`. That name is also
|
|
610
|
+
the vault's name in Obsidian's vault switcher, so it is readable and unique
|
|
611
|
+
across projects and views. The first name that is free is taken, with a number
|
|
612
|
+
inside the parentheses when needed (`harbor-notes (everything 2)`): free means
|
|
613
|
+
nothing on the disk has it, and no vault Obsidian lists has it in any letter
|
|
614
|
+
case. The folder is created private to this user (mode 0700), and so is `DIR`
|
|
615
|
+
when Atelier creates it; a missing folder on the way to `DIR` is made as any
|
|
616
|
+
other folder is. The allocation is recorded in the workspace's private
|
|
617
|
+
state (`state/allocations/<view>.json`) and is never recomputed: renaming the
|
|
618
|
+
project or deciding another folder later moves no vault, and a vault published
|
|
619
|
+
before under the data root stays there. `location show` says where each
|
|
620
|
+
view's vault is, or will be, or why its record cannot be read.
|
|
621
|
+
|
|
622
|
+
Atelier publishes only into the folder it made. The allocation records the
|
|
623
|
+
folder's real path, device and inode, and the maintenance service checks them
|
|
624
|
+
at every tick, as the publisher does before it writes anything and again
|
|
625
|
+
before each note and before its commit (see Known limits). Another folder
|
|
626
|
+
put where the one made was (moved in, or a link) is not published into
|
|
627
|
+
(`vault-allocation-replaced`), nor is the same folder reached through a link
|
|
628
|
+
put on the way since, into a vault the app lists, say
|
|
629
|
+
(`vault-allocation-moved`). A folder that has gone is made again at the next
|
|
630
|
+
tick, only where its recorded real path leads and only where a location would
|
|
631
|
+
be accepted (not inside a repository, the private state, a vault Atelier
|
|
632
|
+
publishes or one the app lists, and on the data root's volume). A vault
|
|
633
|
+
removed, moved or replaced while it is being published stops that publication
|
|
634
|
+
(`vault-allocation-missing`, `vault-allocation-replaced`, or
|
|
635
|
+
`vault-root-moved` for a vault under the data root). Once the folder is put
|
|
636
|
+
back, or made again, the running service publishes into it again at its next
|
|
637
|
+
tick; no restart is needed. One case waits for the next change at the view's
|
|
638
|
+
sources: a folder made again after a publication that stopped just before its
|
|
639
|
+
commit. That generation is finished by restart recovery, its notes went with
|
|
640
|
+
the folder that went away, and the view reads `vault-note-missing` until then. A
|
|
641
|
+
view that was published and whose vault Atelier cannot find is refused
|
|
642
|
+
(`vault-allocation-lost`) rather than published again into a new, empty vault
|
|
643
|
+
under the data root: its record was lost, or, for a view that never had a
|
|
644
|
+
record (published under the data root), its `vaults/<view>` folder was
|
|
645
|
+
removed. Restore the record from a backup if its vault was allocated
|
|
646
|
+
elsewhere; otherwise make the folder `vaults/<view>` under the data root, and
|
|
647
|
+
the view is published there at the next change at its sources. `status` and
|
|
648
|
+
`open` name that next step for this code, and for `vault-allocation-moved`,
|
|
649
|
+
`vault-allocation-replaced` and `vault-allocation-missing`.
|
|
650
|
+
|
|
651
|
+
`DIR` is refused inside an enrolled repository or the project, inside a vault
|
|
652
|
+
Atelier publishes, inside a folder Obsidian lists as a vault, and on another
|
|
653
|
+
volume than the data root (a note is published by an atomic exchange, which
|
|
654
|
+
cannot cross volumes). A folder a sync client keeps in step (iCloud Drive, a
|
|
655
|
+
cloud storage provider, Dropbox, OneDrive, Google Drive, or a Desktop or
|
|
656
|
+
Documents folder iCloud syncs) is refused unless `--allow-synced-location` is
|
|
657
|
+
given: another machine's Obsidian could hold a vault there unseen. A folder
|
|
658
|
+
macOS protects (Desktop, Documents, Downloads) is accepted with a warning,
|
|
659
|
+
because macOS asks before Obsidian or the maintenance service may read it.
|
|
660
|
+
`~/` is read against the account's home folder, which a shell does not do
|
|
661
|
+
after `--x=`. Each check compares the folder as written and as its real path,
|
|
662
|
+
in any letter case, so a link into a listed or synced vault is refused as
|
|
663
|
+
that vault is. Obsidian's vault list is read through the app when it runs and
|
|
664
|
+
answers, and otherwise from its settings file, never written. When the list
|
|
665
|
+
cannot be read, `location set` says so and accepts the folder, and the
|
|
666
|
+
maintenance service allocates no vault there until it can read the list
|
|
667
|
+
(the view is `stale`, `app-vault-list-unreadable`, and is tried again at
|
|
668
|
+
the next tick).
|
|
387
669
|
|
|
388
670
|
### What `open` does in each state of Obsidian
|
|
389
671
|
|
|
@@ -415,28 +697,97 @@ exactly because the folder is listed.
|
|
|
415
697
|
reopens every vault it had open, this one included, and once its command
|
|
416
698
|
line answers (the app itself, not the tool's "unable to find Obsidian")
|
|
417
699
|
`open` hands it the vault's link through that tool, never through the
|
|
418
|
-
operating system.
|
|
700
|
+
operating system. A vault Obsidian already lists but had closed is not
|
|
701
|
+
reopened by a plain start, so if Obsidian refuses that link just after it
|
|
702
|
+
started, `open` hands it the link once more, again through that tool and
|
|
703
|
+
never through the operating system, before it gives up. On Linux a plain start is not
|
|
419
704
|
qualified yet, and Obsidian is started with the link (the other vaults'
|
|
420
705
|
reopen marks are lost there; a known limit).
|
|
421
|
-
|
|
422
|
-
|
|
706
|
+
In the same write, `open` turns Obsidian's command line on (`cli: true`)
|
|
707
|
+
when it is off: publishing into a vault Obsidian holds needs it until
|
|
708
|
+
Atelier's plugin takes over that part, and it is off in a new installation.
|
|
709
|
+
The answer says what was written and where the backup of the file as it
|
|
710
|
+
was is kept.
|
|
711
|
+
- **Obsidian is installed and never started** (no `obsidian.json` yet). When
|
|
712
|
+
the process table says, positively, that no Obsidian runs, `open` creates
|
|
713
|
+
the file, with this vault flagged open and the command line on, and starts
|
|
714
|
+
Obsidian plainly; Obsidian opens the vault. Nothing else is in the file,
|
|
715
|
+
and the settings folder is created private to you when it does not exist
|
|
716
|
+
yet. The file is created exclusively: if Obsidian writes its own first,
|
|
717
|
+
that one is kept and `open` says so. While an Obsidian may run, nothing is
|
|
718
|
+
created (`app-may-be-running`).
|
|
423
719
|
- **Obsidian runs with no vault open.** Its command line then answers every
|
|
424
720
|
command with "Vault not found.", so nothing can be asked of it, and its
|
|
425
721
|
settings file belongs to the running app, so it is only read. A vault the
|
|
426
722
|
app already lists is opened by path and published through the app as
|
|
427
|
-
above.
|
|
428
|
-
`app-version-unsupported` with reason
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
- **Obsidian's command line is turned off** (the default of a new
|
|
723
|
+
above. For a vault it does not list yet, see "Restart, only when you ask"
|
|
724
|
+
below; without that, `open` answers `app-version-unsupported` with reason
|
|
725
|
+
`no-vault-open` and names `--restart-obsidian`.
|
|
726
|
+
- **Obsidian runs with its command line turned off** (the default of a new
|
|
432
727
|
installation). It then answers every command with "Command line interface
|
|
433
|
-
is not enabled",
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
answers `app-cli-unavailable` with reason `cli-turned-off
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
728
|
+
is not enabled", only a link reaches it, and its settings belong to the
|
|
729
|
+
running app, which keeps the switch in memory and would write it back. See
|
|
730
|
+
"Restart, only when you ask" below; without that, `open` adds and launches
|
|
731
|
+
nothing, and answers `app-cli-unavailable` with reason `cli-turned-off`,
|
|
732
|
+
naming `--restart-obsidian` and where to turn the switch on yourself:
|
|
733
|
+
Settings > General > Advanced > Command line interface.
|
|
734
|
+
|
|
735
|
+
**Restart, only when you ask.** In the two states where Obsidian runs but
|
|
736
|
+
`open` cannot reach it (its command line off, or no vault open and the view's
|
|
737
|
+
vault not in its list), `atelier obsidian open --restart-obsidian` lets `open`
|
|
738
|
+
restart Obsidian once:
|
|
739
|
+
|
|
740
|
+
1. Before anything is sent, it checks, reading only, that the write the
|
|
741
|
+
restart is for can succeed: the view's vault folder exists, Obsidian's
|
|
742
|
+
settings file can be read and is yours, is no link and has no second
|
|
743
|
+
name, is not a Flatpak or snap build's, is not too large to take the
|
|
744
|
+
vault, and lists no vault at a folder above the view's. An Obsidian that
|
|
745
|
+
has shown only its starter window has written no settings file yet; then
|
|
746
|
+
the folder it goes in must be yours (or be missing inside a folder of
|
|
747
|
+
yours), and the file is created after the quit, as for an Obsidian that
|
|
748
|
+
never started. Otherwise `open` answers that refusal and leaves Obsidian
|
|
749
|
+
running. Right before the first signal, once the process below is proven,
|
|
750
|
+
it keeps a copy of the settings file beside it
|
|
751
|
+
(`obsidian.json.atelier-backup-<time>`), which this run's write does not
|
|
752
|
+
prune, and the answer names it; a copy that cannot be kept sends nothing.
|
|
753
|
+
2. It sends SIGTERM to Obsidian's main process, and to nothing else. That
|
|
754
|
+
process is found in the process table and must be the only Obsidian main
|
|
755
|
+
process there, one of yours, named by the app bundle's path, with every
|
|
756
|
+
other Obsidian process its child; it is read again just before the signal
|
|
757
|
+
and must be the same process, yours, under the same parent. Anything else
|
|
758
|
+
(two Obsidians, another user's, a helper of another process, a table that
|
|
759
|
+
cannot be read) is answered `app-main-process-unproven`, with the reason in
|
|
760
|
+
`restart.detail`, and nothing is sent.
|
|
761
|
+
3. Obsidian handles that SIGTERM as a quit: it closes every window, keeping
|
|
762
|
+
the vaults it had open flagged to reopen, and its helper processes end. On
|
|
763
|
+
macOS its main process can then stay, with no window. Electron handles
|
|
764
|
+
only the first SIGTERM as a quit: a second one ends the process at once,
|
|
765
|
+
without its quit handlers. `open` sends that second SIGTERM only when the
|
|
766
|
+
same process has shown no window and no helper at every reading for at
|
|
767
|
+
least 5 seconds (a margin, not a proof that it has nothing left to save). There
|
|
768
|
+
is never a third signal, and never a stronger one: an Obsidian that is not
|
|
769
|
+
gone within 30 seconds is answered `launch-failed` with reason
|
|
770
|
+
`app-did-not-quit`, and nothing is written. The answer says how many
|
|
771
|
+
signals were delivered.
|
|
772
|
+
4. It adds the vault to Obsidian's settings and turns the command line on, in
|
|
773
|
+
the one write described above.
|
|
774
|
+
5. It starts Obsidian plainly, and the vaults it had open come back, with this
|
|
775
|
+
one beside them. If the vault could not be added after all, Obsidian is
|
|
776
|
+
still started again, as it was.
|
|
777
|
+
|
|
778
|
+
The answer says that Obsidian was restarted, which process was signalled,
|
|
779
|
+
how, and what was written. The flag is never implied by any other flag or
|
|
780
|
+
answer; every other operation refuses it. The restart is qualified on macOS
|
|
781
|
+
only (on Linux a quit Obsidian is started with a link, which would drop the
|
|
782
|
+
other vaults' reopen flags): elsewhere `open` answers
|
|
783
|
+
`restart-platform-unqualified`.
|
|
784
|
+
|
|
785
|
+
Two limits. `open` asks nothing at a terminal yet: the restart needs the flag
|
|
786
|
+
there too, until the first-run command asks "Obsidian must restart once to add
|
|
787
|
+
this vault … Restart now?" in that state. And if `open` itself is interrupted
|
|
788
|
+
(Ctrl-C, or its process ended) after Obsidian quit and before it was started
|
|
789
|
+
again, Obsidian stays quit: start it yourself, and it reopens its vaults; run
|
|
790
|
+
`open` again to add the view's vault.
|
|
440
791
|
|
|
441
792
|
Only `open` adds a vault to Obsidian: the maintenance service never does, so
|
|
442
793
|
it never opens a window nobody asked for. A declared view that was never
|
|
@@ -464,6 +815,19 @@ the view stays refused until the list names the folder once.
|
|
|
464
815
|
While one entry of the folder has a window, `open` reaches only that one and
|
|
465
816
|
never opens another entry of the same folder beside it.
|
|
466
817
|
|
|
818
|
+
A view's first publication into the folder allocated for it is the one
|
|
819
|
+
exception to the rule below. While no list the app keeps names that folder, a
|
|
820
|
+
folder above it or one inside it, the vault is filled even while Obsidian
|
|
821
|
+
runs, by creating files only. When that run completes, `open` adds the vault
|
|
822
|
+
to Obsidian and opens it after the commit. A run that stops partway commits
|
|
823
|
+
nothing and falls back to the coordinated path. That happens when the list
|
|
824
|
+
names the folder meanwhile, a settings file with other bytes is already
|
|
825
|
+
there, or an interrupted run left a replacement or a removal. `open` may then
|
|
826
|
+
add and open the vault before it is complete, and the app publishes the rest.
|
|
827
|
+
Anything that would replace or remove a file waits for the app. See "First publication into a vault no Obsidian lists" in
|
|
828
|
+
[obsidian-contract.md](obsidian-contract.md). The plugin's entry written this
|
|
829
|
+
way counts as offered until the plugin says hello from the vault.
|
|
830
|
+
|
|
467
831
|
The publisher still writes into a vault only when it can coordinate with every
|
|
468
832
|
Obsidian that may hold it, or when the process table shows, positively, that
|
|
469
833
|
none runs. Otherwise it stops, and the view reports `publisher-conflict` with
|
|
@@ -500,6 +864,419 @@ command-line interface is turned on). So is a listed vault the command line
|
|
|
500
864
|
does not answer for once it is opened, while only the plugin reports the
|
|
501
865
|
version: `open` says so at once instead of waiting for the app.
|
|
502
866
|
|
|
867
|
+
## Personal workspaces
|
|
868
|
+
|
|
869
|
+
A personal workspace (see [personal-workspace.md](personal-workspace.md)) is
|
|
870
|
+
projected through one route, `bindPersonalWorkspace({ personalHome,
|
|
871
|
+
generationId })` in `src/projection/obsidian/personal-workspace.mjs`. Both
|
|
872
|
+
arguments are explicit; neither is discovered from the environment or the
|
|
873
|
+
working directory. A loader uses `loadBoundProject` with the same arguments,
|
|
874
|
+
or `loadBoundProjectOffThread`, which composes in a worker thread; the engine
|
|
875
|
+
awaits its loader. The command, the service and the login item bind a personal
|
|
876
|
+
home through `--personal-home`, which follows the generation the person
|
|
877
|
+
confirmed there (see "A confirmed selection", below).
|
|
878
|
+
|
|
879
|
+
- **One graph.** The binding is attached to the generation's resolved
|
|
880
|
+
`atelier.project.json` under one symbol key. When a project carries it, the
|
|
881
|
+
pipeline's `buildGraph` builds the canonical graph with the caller's file
|
|
882
|
+
cache and composition's own rule that no private note is a link target, then
|
|
883
|
+
applies eligibility as for any project. The engine, view counts, source apply,
|
|
884
|
+
the proposal adapter and the selection operation all build through it, so they
|
|
885
|
+
read one graph. A plain build of the generation's configuration would differ:
|
|
886
|
+
there, a shared note's link can name a private note.
|
|
887
|
+
- **Only a confirmed graph is used.** A graph is returned only under a validity
|
|
888
|
+
key that `composePersonalWorkspace` confirmed. The key names:
|
|
889
|
+
- the authored manifest and overlay, as regular files of at most 1 MiB;
|
|
890
|
+
- every file of the generation, as regular files of at most 8 MiB (the
|
|
891
|
+
personal-workspace module's own limits);
|
|
892
|
+
- the private home's own state, any `.git` above it, and a `.git` in its
|
|
893
|
+
`generations` folder;
|
|
894
|
+
- each enrolled root's link status and its Git configuration (its remote and
|
|
895
|
+
its helpers), through a `gitdir:` file and its common directory where `.git`
|
|
896
|
+
is a file;
|
|
897
|
+
- the user's Git configuration (`~/.gitconfig` and the XDG one), through a
|
|
898
|
+
link, and any ambient Git variable;
|
|
899
|
+
- a digest of the graph's facts: nodes, edges, embeds, assets, resolved links
|
|
900
|
+
and every finding.
|
|
901
|
+
|
|
902
|
+
The inputs are read before the composition runs. A key is confirmed only when
|
|
903
|
+
the composition then accepted the inputs and composed a graph with exactly
|
|
904
|
+
those facts, so every graph used is one the composition built too. A single
|
|
905
|
+
change that lands while it runs is either seen by it or leaves the key behind;
|
|
906
|
+
a change and its exact undo during one composition is caught at the next full
|
|
907
|
+
reconciliation. Binding confirms the first key. An authored or generation
|
|
908
|
+
file the key reads is opened without following a link and without waiting on
|
|
909
|
+
it. A FIFO, a device or a link refuses at once, with the composition's code
|
|
910
|
+
(`generation-corrupt`, `malformed-input`). A Git configuration file is read
|
|
911
|
+
through a link, as Git reads it, but never waited on.
|
|
912
|
+
|
|
913
|
+
When a build finds its key unconfirmed, a caller that builds once composes in
|
|
914
|
+
its own thread. The engine, and the proposal adapter it runs, compose in a
|
|
915
|
+
worker, off the event loop, then build again. The adapter builds only when it
|
|
916
|
+
looks at a newly observed edit, as for any project. On a tick whose graph is
|
|
917
|
+
pending (`personal-validation-pending`), it records nothing for the edits it
|
|
918
|
+
reached and reports `observed.deferred`. It offers those edits, and every
|
|
919
|
+
earlier refusal, again next tick, and leaves a due proposal operation as it
|
|
920
|
+
is, without spending one of its attempts. While shared sources change before
|
|
921
|
+
every tick, every tick is pending: the edit is deferred again, with one full
|
|
922
|
+
build per tick, until a tick with no change. A worker that does not answer
|
|
923
|
+
within two minutes is let go of and refuses `personal-composition-unavailable`,
|
|
924
|
+
so the tick does not wait. A worker blocked in a system call (a FIFO opened for
|
|
925
|
+
reading) cannot be stopped until that call returns. Until then it keeps the
|
|
926
|
+
process from exiting, and each such expiry leaves one thread behind: a service
|
|
927
|
+
stopped in that state needs a kill. A graph that still differs refuses
|
|
928
|
+
`personal-graph-unconfirmed`.
|
|
929
|
+
- **A copy is refused.** The settings member names the bound generation. A copy
|
|
930
|
+
of a bound project that lost the binding (a structured clone, a JSON round
|
|
931
|
+
trip) refuses `personal-binding-lost`, and is never built as an ordinary
|
|
932
|
+
project.
|
|
933
|
+
- **Validity.** Composition decides whether the generation may be used, at bind
|
|
934
|
+
time and at every build. Its refusals keep their codes (`stale-generation`,
|
|
935
|
+
`generation-missing`, `generation-corrupt`, `generation-relocated`,
|
|
936
|
+
`retained-removed-reference`, `not-private-location`, `root-symlinked`,
|
|
937
|
+
`ambient-git-environment`, `future-schema`, `ambiguous-binding` and the rest).
|
|
938
|
+
`bindPersonalWorkspace` throws them as `PersonalWorkspaceRefusal`.
|
|
939
|
+
`loadBoundProject` and every build refuse with the same code as a maintenance
|
|
940
|
+
refusal, so the engine and every command report it typed, and no view stays
|
|
941
|
+
current. Nothing is published, and no vault, manifest, journal or private home
|
|
942
|
+
byte is written. A refusal the projection adds (`personal-view-id-reserved`,
|
|
943
|
+
`personal-views-exceed-settings-limit`) is a maintenance refusal from the
|
|
944
|
+
start.
|
|
945
|
+
- **Staleness is noticed at the next tick.** The engine observes the person's
|
|
946
|
+
`atelier.personal.json` and `atelier.overlay.json` and the bound generation's
|
|
947
|
+
`generation.json` as configuration. A change to any of them prepares every
|
|
948
|
+
view again on the next tick, unasked, and a generation that no longer holds
|
|
949
|
+
is refused there. Every full reconciliation composes a bound workspace again,
|
|
950
|
+
in a worker, whatever else it prepares. A cause the key does not name is
|
|
951
|
+
therefore refused within the reconciliation interval (five minutes by
|
|
952
|
+
default). Those causes are:
|
|
953
|
+
- the system Git configuration, and files a Git configuration includes
|
|
954
|
+
(`include.path`, `config.worktree`);
|
|
955
|
+
- an enrolled repository's work tree or top level changing;
|
|
956
|
+
- a change of the authored inputs and its exact undo during one composition;
|
|
957
|
+
- a shared note replaced by a link;
|
|
958
|
+
- an untracked note becoming ignored.
|
|
959
|
+
|
|
960
|
+
The views are prepared again only when that composition refuses or confirms a
|
|
961
|
+
different key: an unchanged workspace is not rebuilt, republished or locked.
|
|
962
|
+
After a refusal the engine
|
|
963
|
+
loads the project again at the next tick, so a loader that binds the next
|
|
964
|
+
generation is followed. The generation's record and the settings member both
|
|
965
|
+
name the generation, so a rebound generation invalidates every view even when
|
|
966
|
+
its configuration file is byte-identical.
|
|
967
|
+
- **Views.** The overlay's saved views become scopes in memory: each is a
|
|
968
|
+
scoped view of the repositories it names, and `everything` is a full view of
|
|
969
|
+
the composed graph. A saved view named `everything` is refused. More than 255
|
|
970
|
+
saved views, or a view of more than 256 repositories, is refused at bind
|
|
971
|
+
(`personal-views-exceed-settings-limit`), within the Obsidian settings
|
|
972
|
+
contract. The preferred view, when it names a saved one, is the default. No
|
|
973
|
+
generation file is written. `view add` refuses `views-from-personal-overlay`;
|
|
974
|
+
`audience set`, `audience clear` and `location set` refuse
|
|
975
|
+
`personal-binding-decision-unavailable`. Each refuses before anything is
|
|
976
|
+
resolved or written.
|
|
977
|
+
- **Preferences and coverage.** `preferences` are returned read only. A theme
|
|
978
|
+
is reported and never written: `appearance.json` stays the person's.
|
|
979
|
+
`coverage` is passed through untouched. No Obsidian code reads it: it states
|
|
980
|
+
what is not enforced, and is never permission.
|
|
981
|
+
- **Workspace identity.** The bound project's folder is the private home, so
|
|
982
|
+
the workspace pointer is `<personal home>/.atelier-local/obsidian.json`. A
|
|
983
|
+
generation is a closed, verified inventory, and an extra file there would
|
|
984
|
+
invalidate it. This is provisional until the per-person projection
|
|
985
|
+
configuration exists.
|
|
986
|
+
- **Edits.** A vault edit to a private note is never applied. Source apply
|
|
987
|
+
refuses it with `personal-overlay-proposals-only`. The proposal adapter does
|
|
988
|
+
not observe, record or propose it either: it answers
|
|
989
|
+
`personal-overlay-not-proposed`. Nothing is written into a generation, and
|
|
990
|
+
the view stays
|
|
991
|
+
`held-for-your-edit` until the edit is undone. Turning such an edit into a
|
|
992
|
+
change of the person's overlay is not built.
|
|
993
|
+
- **Reserved settings key.** The settings member
|
|
994
|
+
`ext["mnstry.atelier.personal-workspace"]` is reserved: the binding writes it
|
|
995
|
+
in memory. A project configuration that declares it itself is refused at
|
|
996
|
+
every build (`personal-binding-lost`, naming the key).
|
|
997
|
+
|
|
998
|
+
### A confirmed selection
|
|
999
|
+
|
|
1000
|
+
`--personal-home DIR`, given instead of `--project` to `atelier obsidian`, to
|
|
1001
|
+
the service entry and in a login item's unit, binds the personal home DIR
|
|
1002
|
+
(`src/runtime/obsidian/personal-selection.mjs`). Without it, every argument,
|
|
1003
|
+
unit and loader is what it was.
|
|
1004
|
+
|
|
1005
|
+
- **What is loaded.** The generation the person last confirmed with
|
|
1006
|
+
`selectPersonalGeneration` (see [personal-workspace.md](personal-workspace.md)),
|
|
1007
|
+
and nothing else. The loader reads the head of the selection history from
|
|
1008
|
+
the history alone (`readPersonalSelectionHead`), loads the generation it
|
|
1009
|
+
names through the route above (in a worker for the service, in the
|
|
1010
|
+
command's thread for a command), then reads the head again. A head that
|
|
1011
|
+
moved meanwhile refuses `personal-selection-changed`. Nothing here confirms
|
|
1012
|
+
a selection or chooses one for the person.
|
|
1013
|
+
- **Nothing confirmed publishes nothing.** No record refuses
|
|
1014
|
+
`nothing-selected`; a confirmed generation whose inputs were edited and not
|
|
1015
|
+
confirmed again refuses `stale-generation`; a history whose hash chain does
|
|
1016
|
+
not hold refuses `selection-history-corrupt`; a history that ends at an
|
|
1017
|
+
earlier record than the running service has read refuses
|
|
1018
|
+
`selection-history-shortened`; a home whose history cannot be read refuses
|
|
1019
|
+
`personal-home-unavailable`, and a home that is gone or not
|
|
1020
|
+
private refuses with the module's own code (`root-missing`,
|
|
1021
|
+
`not-private-location`, `root-symlinked`). Every other refusal of the
|
|
1022
|
+
composition keeps its code. Nothing falls back to another generation or to
|
|
1023
|
+
the project without a binding. The vault keeps what it last published from
|
|
1024
|
+
a confirmed generation. `status`, `open` and `service start` load the
|
|
1025
|
+
selection and refuse with its code; the service's freshness shows every view
|
|
1026
|
+
`stale` with it, and its status document has it as the last tick's reason.
|
|
1027
|
+
`selection-history-shortened` comes only from a process that read the
|
|
1028
|
+
history longer, which is the running service: a command of the command line
|
|
1029
|
+
reads the history in a process of its own, as it then stands, so `status`
|
|
1030
|
+
reports the selection a cut history ends with while the service refuses it.
|
|
1031
|
+
A confirmed selection is reported by `status` (`selection`: the generation
|
|
1032
|
+
and the number of its record).
|
|
1033
|
+
- **The service starts whatever the selection.** A bound project's folder is
|
|
1034
|
+
its private home, so the home alone locates the workspace
|
|
1035
|
+
(`locatePersonalHome`): the service resolves its workspace from that at once
|
|
1036
|
+
and starts, and its engine awaits the loader at each tick. Started by a login
|
|
1037
|
+
item, the service asks the home, not the loader, for its workspace and log.
|
|
1038
|
+
`service status` and `stop`, the ticks `open` asks for, `service unit
|
|
1039
|
+
--remove` and `uninstall` locate the service the same way. When no project
|
|
1040
|
+
loaded (a restart while nothing is confirmed), the engine locates the
|
|
1041
|
+
workspace too, so each view published before says why nothing is published
|
|
1042
|
+
now instead of keeping the previous run's `current`. A service given a loader
|
|
1043
|
+
that answers a promise and no locator is still refused at start with
|
|
1044
|
+
`service-loader-not-synchronous`, and so is one whose locator answers a
|
|
1045
|
+
promise; a located value that names no folder refuses
|
|
1046
|
+
`service-locator-invalid`. The workspace pointer is under the home, so a home
|
|
1047
|
+
that is gone when the service starts leaves it no workspace to find
|
|
1048
|
+
(`service-workspace-not-prepared`).
|
|
1049
|
+
- **Following the history.** A loaded project is pinned to the record it was
|
|
1050
|
+
loaded under, and the engine observes that record and the place of the next
|
|
1051
|
+
one as configuration. A confirmation is therefore loaded at the next tick. A
|
|
1052
|
+
history cut short is seen at the next tick too, and a running service does
|
|
1053
|
+
not follow it back. Selections are only ever appended, so a history that
|
|
1054
|
+
ends at an earlier record than the service has read it ending at was cut by
|
|
1055
|
+
something other than a confirmation, and following it back could publish
|
|
1056
|
+
what nobody confirmed after the records removed: cut after a restore nobody
|
|
1057
|
+
confirmed, it would publish that restore. The service notes the highest
|
|
1058
|
+
record number at every read of the history's end (a load's first read, its
|
|
1059
|
+
read once the generation is loaded, the recheck at a full reconciliation,
|
|
1060
|
+
and a read made when a tick stops a view as `mixed-read` because the tick's
|
|
1061
|
+
settings changed or could not be read, once for views stopped one after
|
|
1062
|
+
another), whether or not the
|
|
1063
|
+
record's generation could be used, and whichever part of the service read
|
|
1064
|
+
it. A history that ends earlier refuses `selection-history-shortened` at a
|
|
1065
|
+
load and at a recheck, and nothing is loaded from it while it is shorter; at
|
|
1066
|
+
the read made when a tick stops a view, the view says `mixed-read` at that
|
|
1067
|
+
tick and the code at the next. A project already loaded is loaded again at
|
|
1068
|
+
the next tick once any part of the service has read the history ending at a
|
|
1069
|
+
later record than the one the project was loaded under, so a confirmation
|
|
1070
|
+
read elsewhere in the service and removed before the engine looked refuses at
|
|
1071
|
+
that tick; otherwise a project already loaded is held to this at its next
|
|
1072
|
+
recheck, at the latest the next full reconciliation, and until then it
|
|
1073
|
+
publishes nothing new under another record, and a change at its sources
|
|
1074
|
+
brings the recheck forward. A record added and removed between two of those
|
|
1075
|
+
reads was never read, and does not count: while a load of its own is under
|
|
1076
|
+
way, the plugin's status read compares the files that decide its views and
|
|
1077
|
+
reads no history, so a record it sees then only as a changed file is not
|
|
1078
|
+
noted. The views say why (`open` and
|
|
1079
|
+
`status --json` name the ways out), and the vault keeps what it last
|
|
1080
|
+
published.
|
|
1081
|
+
- **What ends that refusal.** The removed records put back; a history as long
|
|
1082
|
+
again, since a record in a place already read, with other content, is read
|
|
1083
|
+
as a confirmation (after a cut of one record the person's next confirmation
|
|
1084
|
+
is followed; after a cut of several, one confirmation is not enough); or a
|
|
1085
|
+
start of the service. The number is kept in the process only, so a service
|
|
1086
|
+
started after the cut cannot tell a cut history from one never longer: it
|
|
1087
|
+
reads the history as it then stands and follows what it ends with,
|
|
1088
|
+
publishing it if its generation still holds. Every start counts, the
|
|
1089
|
+
person's own and one at login, after an upgrade or after a failure. Stopping
|
|
1090
|
+
and starting the service is therefore how a person accepts a shorter
|
|
1091
|
+
history, and a start nobody chose accepts it too. Nothing here keeps the
|
|
1092
|
+
number across a start.
|
|
1093
|
+
- **What the history is checked for besides.** At every full reconciliation the
|
|
1094
|
+
history is read again, so a record before the last that no longer links, or
|
|
1095
|
+
a confirmation that landed before the engine first looked at the record,
|
|
1096
|
+
refuses with its code within the reconciliation interval. A confirmation
|
|
1097
|
+
during a tick changes an observed file: the view being prepared or published
|
|
1098
|
+
refuses `mixed-read` at its next wait, before its generation commits (one
|
|
1099
|
+
already committed stays), so do the views after it, and the next tick
|
|
1100
|
+
publishes under the new record. While the last load was refused, every tick
|
|
1101
|
+
loads again: with nothing confirmed only the history is read, and with a
|
|
1102
|
+
confirmed generation that no longer holds, one composition runs in a worker
|
|
1103
|
+
per tick.
|
|
1104
|
+
- **A narrowing.** When the newly confirmed generation drops a source (a
|
|
1105
|
+
repository withdrawn, a view narrowed), each view follows the publisher's
|
|
1106
|
+
rule for notes that leave a view; this binding adds no rule. At the view's
|
|
1107
|
+
next publication a generated note of the dropped source leaves the vault and
|
|
1108
|
+
its bytes are moved to private recovery. A note the person edited, once the
|
|
1109
|
+
engine has queued the edit, holds the whole view at the generation it last
|
|
1110
|
+
published (`held-for-your-edit`, `publication-withheld-for-your-edit`): the
|
|
1111
|
+
note is neither removed nor overwritten. A note edited during the
|
|
1112
|
+
publication itself stays in the vault (`retained-edit`).
|
|
1113
|
+
- **Who may see.** The binding never decides it. It stays this machine's
|
|
1114
|
+
audience setting, none by default, and `audience set` still refuses for a
|
|
1115
|
+
bound project: until an audience is allowed some other way, a bound vault
|
|
1116
|
+
shows no note. Deciding who may see a bound vault is not built.
|
|
1117
|
+
- **Reading the history on the service's event loop.** The head is one
|
|
1118
|
+
directory listing and one bounded read per record. A record that is not a
|
|
1119
|
+
regular file (a FIFO, a device) refuses `selection-history-corrupt` without
|
|
1120
|
+
being waited on. A home that becomes unreadable while the engine holds a
|
|
1121
|
+
project (its permissions changed, not removed) fails the tick when the
|
|
1122
|
+
engine observes its files, as an unreadable configuration file does: nothing
|
|
1123
|
+
is published, and the service reports the failed tick.
|
|
1124
|
+
|
|
1125
|
+
Measured once on 2,156 notes in three repositories, with 50 annotations and five
|
|
1126
|
+
saved views (six views in all). The production contributions were loaded, as
|
|
1127
|
+
the service loads them, so the proposal adapter was registered. The host's load
|
|
1128
|
+
average was near 35, slower than an idle one, so read the times as upper
|
|
1129
|
+
bounds. "Main-thread compositions" counts the Git probe a composition makes on
|
|
1130
|
+
the event loop's thread.
|
|
1131
|
+
|
|
1132
|
+
| Step | Wall time | Longest event-loop block | Main-thread compositions |
|
|
1133
|
+
| --- | --- | --- | --- |
|
|
1134
|
+
| Load, composed in a worker (`loadBoundProjectOffThread`) | 3.8 s | 0.03 s | none |
|
|
1135
|
+
| Load in the calling thread (`loadBoundProject`) | 3.5 s | 3.5 s | one |
|
|
1136
|
+
| One build without a file cache (a caller that builds once) | 1.4 s | 1.3 s | none |
|
|
1137
|
+
| First tick: load, build, and first publication of six vaults | 582 s | 574 s | none |
|
|
1138
|
+
| Tick with every view prepared again | 4.5 s | 4.5 s | none |
|
|
1139
|
+
| Idle tick | 0.25 s | 0.23 s | none |
|
|
1140
|
+
| Tick with a vault edit and a graph change: the adapter defers, the engine confirms in a worker | 12.4 s | 2.8 s | none |
|
|
1141
|
+
| Next tick: the adapter observes that edit (one build) | 2.1 s | 2.0 s | none |
|
|
1142
|
+
| Full-reconciliation tick, the edit still open | 5.0 s | 1.0 s | none |
|
|
1143
|
+
|
|
1144
|
+
**What is met.** No composition runs on the engine's event loop, the proposal
|
|
1145
|
+
adapter's included. The load, a confirmation after a change, and the
|
|
1146
|
+
composition at every full reconciliation run in a worker; the tick does not
|
|
1147
|
+
wait on one that misses its deadline. An unchanged workspace is not prepared
|
|
1148
|
+
again. While an edit stays open and shared sources are quiet, the adapter
|
|
1149
|
+
builds nothing more; under continuous change it builds once per tick until a
|
|
1150
|
+
quiet tick.
|
|
1151
|
+
|
|
1152
|
+
**What a service binding needed.** The first publication blocked the event
|
|
1153
|
+
loop far longer than the service's five-second health probe: about ten minutes
|
|
1154
|
+
here. That is the
|
|
1155
|
+
publication path every project takes, not this route: an ordinary project of
|
|
1156
|
+
450 notes, measured the same way, blocked as long as a bound one. Preparing six
|
|
1157
|
+
views again blocked 4.5 s, under the probe. The proposal adapter's build of a
|
|
1158
|
+
newly observed edit, without the engine's file cache, blocked 2.0 s, at a
|
|
1159
|
+
two-second target, as for an ordinary project. A service binding needed the
|
|
1160
|
+
first publication to yield to the event loop, which it now does (see "Since
|
|
1161
|
+
that measurement"), and a start that does not wait for its loader. The service
|
|
1162
|
+
resolves its workspace from the project its loader answers when it starts, and refuses a loader that answers a promise or any
|
|
1163
|
+
other thenable, such as `loadBoundProjectOffThread`, with
|
|
1164
|
+
`service-loader-not-synchronous`, before it reads anything from it, listens or
|
|
1165
|
+
records anything; a promise it was handed is let go, and its refusal, if it
|
|
1166
|
+
is refused, does not go unhandled. Started by a login item (`--startup`), the
|
|
1167
|
+
service asks its loader (or, under `--personal-home`, its locator) once for both
|
|
1168
|
+
the lookup of its workspace and its own start. One that answers a promise there
|
|
1169
|
+
is never handed to the workspace resolution: a promise whose `constructor`
|
|
1170
|
+
reads as this realm's `Promise` is let go without calling a `then` of its
|
|
1171
|
+
own, any other thenable (as a rule another realm's promise or a subclass's)
|
|
1172
|
+
is adopted once (its `then` is called once in all), the refusal is recorded
|
|
1173
|
+
in the workspace the unit names, when that exists, and the process ends with
|
|
1174
|
+
0, so the service manager does not start it again at once. One that throws
|
|
1175
|
+
there, whatever it throws (`undefined` too, a value with no prototype, or one
|
|
1176
|
+
whose `code` cannot be read), or answers a project whose `then` cannot be
|
|
1177
|
+
read, ends the start as a failure of the loader does anywhere else: it is
|
|
1178
|
+
logged, and what carries no code is a crash.
|
|
1179
|
+
A service bound through `--personal-home` is given a locator as well, and starts
|
|
1180
|
+
from it (see "A confirmed selection"). The plugin's status read allows for such
|
|
1181
|
+
a loader without waiting for it (see "The channel" in
|
|
1182
|
+
[obsidian-plugin.md](obsidian-plugin.md)).
|
|
1183
|
+
|
|
1184
|
+
**Since that measurement.** The engine prepares each view cooperatively and
|
|
1185
|
+
the publisher yields between complete units, so the service answers health and
|
|
1186
|
+
status while notes are emitted, staged and published. The first-tick figures
|
|
1187
|
+
in the table are from before that change and have not been taken again on that
|
|
1188
|
+
corpus. Building the canonical graph gives the event loop a turn between the
|
|
1189
|
+
Markdown sources it reads, hashes and parses (the census): a first tick reads
|
|
1190
|
+
every source, and on a heavily loaded host the build of one view of 10,000
|
|
1191
|
+
invented notes held the listener for 8 s in one stretch. The other sources
|
|
1192
|
+
(HTML files and the sidecars of PDF and Word documents) are read without a turn
|
|
1193
|
+
between them, and a project bound to a personal workspace is built by its own
|
|
1194
|
+
route in one stretch. Walking the directories, resolving the links and
|
|
1195
|
+
assembling the graph still run through.
|
|
1196
|
+
So do capturing the source snapshot, the publisher's restart recovery,
|
|
1197
|
+
validating a prepared view and planning its units, the exchange probe, writing
|
|
1198
|
+
and syncing the journal before the candidates move, and the look again at
|
|
1199
|
+
displaced files after the units (`recheckDisplacedFiles`).
|
|
1200
|
+
The read-back after a commit waits between
|
|
1201
|
+
notes: on a loaded host it took 16.8 s for one view of 10,000 invented notes
|
|
1202
|
+
and held the listener for all of it, and now a request is answered between two
|
|
1203
|
+
notes. Nothing is checked at those waits, since the generation is committed. A
|
|
1204
|
+
note edited in the vault while the read-back is under way is read as edited
|
|
1205
|
+
when the walk has not reached it, and is seen by the next tick when it has, as
|
|
1206
|
+
an edit made just after a read-back is. A stop whose grace runs out while a
|
|
1207
|
+
read-back waits ends the process after the commit and before the view is
|
|
1208
|
+
recorded as current, as a crash there does: the next start's first tick reads
|
|
1209
|
+
the committed generation back. Before a view is prepared, a tick looks at
|
|
1210
|
+
every view's notes and then at the sources, and both walks wait between files.
|
|
1211
|
+
They matter after a publication, when the watcher has named the notes it
|
|
1212
|
+
wrote, and at every full reconciliation, when every note and every source is
|
|
1213
|
+
hashed: on one view of 10,000 invented notes on a loaded host such a tick held
|
|
1214
|
+
the listener for 5.5 s when the walks ran through. Nothing is checked at their
|
|
1215
|
+
waits and nothing is written. Within Atelier only the engine commits a
|
|
1216
|
+
manifest or writes the pending edits, under the lock the tick holds, so the
|
|
1217
|
+
pending edits are read, compared and written in one stretch after the walk
|
|
1218
|
+
over the vault, and the bytes an edit keeps are read there. A file changed
|
|
1219
|
+
after its turn in a walk is seen by the next tick, by its stat hint or the
|
|
1220
|
+
watcher (one that keeps its size and times and draws no event, at the latest
|
|
1221
|
+
by the next full reconciliation), as one changed just after a walk that ran
|
|
1222
|
+
through is; when the tick builds the graph, the look again at the sources
|
|
1223
|
+
after the build refuses such a change as `mixed-read`. A source this tick
|
|
1224
|
+
observed, removed before the build read it (or its folder), is refused the
|
|
1225
|
+
same way. So is a selected source removed before a view's preparation, or the
|
|
1226
|
+
look again before its commit, read it: that view is refused, and the tick goes
|
|
1227
|
+
on to its other views. Either way the next tick reads everything again; a path
|
|
1228
|
+
that can never be read still fails the tick. A note gone from a
|
|
1229
|
+
vault, a source that changed, or a configuration file that changed is a change
|
|
1230
|
+
once: a tick that sees it and then fails without a typed reason (a file it
|
|
1231
|
+
cannot read, a disk that is full, a project loader that fails) has recorded it
|
|
1232
|
+
without acting on it. The next tick therefore starts as the first tick of a
|
|
1233
|
+
process does: it loads the project again and looks at every view, so a view
|
|
1234
|
+
whose note is missing reads `vault-note-missing` instead of staying `current`,
|
|
1235
|
+
and a changed source or view is published.
|
|
1236
|
+
The comparison of kept notes with their candidates reads asynchronously,
|
|
1237
|
+
sixteen at a time, and does not hold the listener. Observing pending edits
|
|
1238
|
+
and routing proposals wait between two edits and between two operations, so
|
|
1239
|
+
a burst of edits does not hold the listener for its whole length. One edit,
|
|
1240
|
+
or one operation, is still one stretch. The first edit an observation looks
|
|
1241
|
+
at that the object store does not hold yet builds the canonical graph in its
|
|
1242
|
+
stretch, and the first operation routed builds it again while the lock of
|
|
1243
|
+
its repository is held. An edit whose published note
|
|
1244
|
+
is not retained has its whole view prepared again in its stretch: once for a
|
|
1245
|
+
view and generation in an observation when the preparation succeeds, and for
|
|
1246
|
+
every such edit when it fails. A stop whose grace runs out while proposals
|
|
1247
|
+
are routed can end the process between two operations of a repository with
|
|
1248
|
+
that repository's proposal lock still held, as a crash can. A service in one
|
|
1249
|
+
of those stretches does not answer until it ends, and is `busy` if that
|
|
1250
|
+
outlasts the probe. A request can
|
|
1251
|
+
hold the listener too: the plugin's status read loads the project on the event
|
|
1252
|
+
loop at its first requests after the service starts (until two loads agree),
|
|
1253
|
+
when a configuration file changed, every two and a half minutes, again two to
|
|
1254
|
+
thirty seconds after a load that failed, and at every request while one of
|
|
1255
|
+
those files vouches for nothing or while its loads never agree. For a project
|
|
1256
|
+
named by `--project` none of those loads is taken off the loop. For a bound
|
|
1257
|
+
personal home they compose in a worker, as the engine's loads do, and the read
|
|
1258
|
+
starts each one on the loop by reading the selection history. A load that holds the loop
|
|
1259
|
+
for a second or more is logged (`status-project-loaded`), and so is a load at
|
|
1260
|
+
every request that names the files it started over (see "What the service
|
|
1261
|
+
shows of these loads" in [obsidian-plugin.md](obsidian-plugin.md)).
|
|
1262
|
+
|
|
1263
|
+
What the waits cost. The waits of observation and routing are taken at
|
|
1264
|
+
every boundary between two edits or two operations and before each
|
|
1265
|
+
repository, whatever time was spent; while routing describes its operations
|
|
1266
|
+
a wait comes after about 8 ms of work or 32 units. Nothing is checked around
|
|
1267
|
+
any of them. In preparation and publication a wait
|
|
1268
|
+
comes after about 8 ms of work or 32 units,
|
|
1269
|
+
whichever is first, and around each one the engine checks that the tick's
|
|
1270
|
+
settings still hold: it reads and hashes every configuration file it observes
|
|
1271
|
+
(the project configuration, the repository access file, local overlays and,
|
|
1272
|
+
for a bound personal workspace, its inputs), reads and validates the machine
|
|
1273
|
+
settings, and asks the eligibility revision. Measured on six configuration
|
|
1274
|
+
files, that check took about 0.2 ms, twice per wait; a first publication of
|
|
1275
|
+
200 invented notes waited 463 times, so roughly 0.2 s of a 64 s tick, on a
|
|
1276
|
+
host with a load average above 200 (indicative only). The selected sources
|
|
1277
|
+
cost one stat call each after the preparation and one before the commit; a
|
|
1278
|
+
note reused from the preparation cache is still not read.
|
|
1279
|
+
|
|
503
1280
|
## Known limits
|
|
504
1281
|
|
|
505
1282
|
These are the limits known at this release. None is hidden behind a skipped
|
|
@@ -554,6 +1331,35 @@ test reported as a pass.
|
|
|
554
1331
|
own launch `open` waits, bounded, while the app is still opening the vault;
|
|
555
1332
|
an app whose vault window is still loading answers a command with `Error:
|
|
556
1333
|
Command "…" not found`, which is read as not up yet, never as a version.
|
|
1334
|
+
- Vault location: consent to a folder a sync client keeps in step
|
|
1335
|
+
(`--allow-synced-location`) is given when `location set` decides the folder,
|
|
1336
|
+
and is not recorded, so the maintenance service does not check for sync
|
|
1337
|
+
clients again. The decision keeps the folder as it was written, so at a
|
|
1338
|
+
view's first allocation a folder on the way that has since become a link
|
|
1339
|
+
into a synced folder is followed, and its vault is allocated there without
|
|
1340
|
+
being asked; a folder that becomes synced without any link (iCloud's
|
|
1341
|
+
"Desktop & Documents Folders" turned on, say) is not refused either, at the
|
|
1342
|
+
first allocation or when a vault is made again. Every other check runs
|
|
1343
|
+
again at both, and a vault made again, or published into, through a link on
|
|
1344
|
+
the way is refused (`vault-allocation-moved`).
|
|
1345
|
+
- Replacing a vault during a publication: the allocated folder is checked
|
|
1346
|
+
before the vault lock is taken and again once its folder is made, after the
|
|
1347
|
+
app is asked about the vault, before each note and before the generation is
|
|
1348
|
+
committed. A program of the same account that puts a link in its place
|
|
1349
|
+
between one check and what follows it can still have that go through the
|
|
1350
|
+
link: one note's writes (the folders on its way and the note), or the vault
|
|
1351
|
+
lock's folder (made there, or made private if it was there already) and, in
|
|
1352
|
+
a folder that already holds one (another Atelier vault), a lock ticket and
|
|
1353
|
+
its release flag. For a note replaced or removed while the app holds the
|
|
1354
|
+
vault, the app is asked about that note between the check and the write, so
|
|
1355
|
+
that window lasts a round trip to the app. The next check refuses the
|
|
1356
|
+
publication, and it is never reported current.
|
|
1357
|
+
- Vault identity: an allocated folder is known by its real path, device and
|
|
1358
|
+
inode. On Linux a folder made at the same path after the allocated one was
|
|
1359
|
+
removed can be given the same inode number (ext4 reuses a freed one), and is
|
|
1360
|
+
then taken for the one made; macOS (APFS) did not reuse one in 200 tries.
|
|
1361
|
+
Such a folder is published into only at the recorded real path, and the
|
|
1362
|
+
plugin's bearer only when this account owns it.
|
|
557
1363
|
- Obsidian's settings file: `open` adds a view's vault to the app's own list,
|
|
558
1364
|
`obsidian.json` in its user-data directory: `~/Library/Application
|
|
559
1365
|
Support/obsidian` on macOS and `$XDG_CONFIG_HOME/obsidian` (else
|
|
@@ -646,6 +1452,17 @@ test reported as a pass.
|
|
|
646
1452
|
in Obsidian's settings or runs
|
|
647
1453
|
`atelier obsidian plugin on --scope ID`. Plugin files are never removed from
|
|
648
1454
|
a vault. See [obsidian-plugin.md](obsidian-plugin.md).
|
|
1455
|
+
- Login items: the tests run the launchd and systemd managers' own code
|
|
1456
|
+
against stand-ins that answer as launchctl and systemctl do, and never
|
|
1457
|
+
against a real session. What `launchctl print-disabled` says for an item
|
|
1458
|
+
switched off in System Settings, and the exit statuses read from
|
|
1459
|
+
`launchctl` and `systemctl`, are the documented ones and are not yet
|
|
1460
|
+
confirmed on a host. A unit whose program cannot be loaded at all (the
|
|
1461
|
+
project's package was removed) is restarted by its manager once a minute,
|
|
1462
|
+
and each attempt adds a few lines to `state/service/login-item.log`, which
|
|
1463
|
+
nothing bounds: `service unit --remove` or `uninstall` ends it. An upgrade
|
|
1464
|
+
in the moment between a service's start and the first reading of its release
|
|
1465
|
+
is not noticed until the next change.
|
|
649
1466
|
- Acceptance: a schema-valid receipt closes no gate, the package proof closes
|
|
650
1467
|
no gate, and no adopter acceptance is recorded in this repository.
|
|
651
1468
|
|
|
@@ -665,3 +1482,8 @@ The test suite proves each oracle can fail:
|
|
|
665
1482
|
- `createMaintenanceEngineForOracleTests` with an
|
|
666
1483
|
`ENGINE_PRIMITIVES.isRetryDue` that never holds: a refused view waits for
|
|
667
1484
|
the full reconciliation, and the retry oracle fails.
|
|
1485
|
+
- `createMaintenanceEngineForOracleTests` with
|
|
1486
|
+
`ENGINE_PRIMITIVES.rechecksEveryViewAfterFailure` false: a note removed, a
|
|
1487
|
+
source changed or a view added in a tick that then fails without a typed
|
|
1488
|
+
reason is lost, the view stays `current` with the note gone or the old
|
|
1489
|
+
content, the new view is never published, and the oracle fails.
|