@mnstry/atelier 0.2.0-alpha.12 → 0.2.0-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +661 -0
- package/README.md +142 -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 +269 -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 +211 -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 +238 -0
- package/docs/learning.md +215 -0
- package/docs/local-services.md +181 -16
- package/docs/obsidian-contract.md +223 -15
- package/docs/obsidian-plugin.md +38 -4
- package/docs/obsidian.md +613 -51
- package/docs/optional-ingestion.md +83 -0
- package/docs/personal-workspace.md +257 -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 +49 -0
- package/docs/repo-boundary-guard.md +94 -0
- 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 +317 -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 +47 -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 +75 -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 +59 -5
- 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 +4 -2
- package/src/boundary/delegation-command.mjs +158 -0
- package/src/boundary/delegation.mjs +396 -0
- package/src/boundary/policy.mjs +52 -10
- 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 +65 -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 +754 -63
- 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 +17 -1
- package/src/graph/knowledge-graph.mjs +58 -8
- 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 +933 -0
- package/src/ingestion/optional/graphify/json_bridge.py +163 -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 +44 -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 +385 -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/projection/obsidian/edits/apply.mjs +55 -16
- package/src/projection/obsidian/personal-workspace.mjs +436 -0
- package/src/projection/obsidian/proposals/adapter.mjs +47 -8
- package/src/projection/obsidian/proposals/observation.mjs +10 -2
- package/src/projection/obsidian/publication/publisher.mjs +142 -13
- 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/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 +163 -20
- 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 +92 -12
- package/src/runtime/obsidian/login-item.mjs +332 -0
- package/src/runtime/obsidian/machine-settings.mjs +119 -12
- package/src/runtime/obsidian/opening.mjs +171 -37
- package/src/runtime/obsidian/pipeline.mjs +71 -3
- 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/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 +99 -16
- 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 +36 -3
- 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/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/knowledge-page.mjs +1388 -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 +1 -0
- package/templates/external-project-workspace/pack-lifecycle.json +6 -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
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto'
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import { buildCanonicalGraph, createGraphFileCache } from '../../graph/graph.mjs'
|
|
4
|
-
import {
|
|
4
|
+
import { markdownMetadata } from '../../graph/knowledge-graph.mjs'
|
|
5
|
+
import { EMITTER_VERSION, createPreparationCache, prepareView, readMarkdownLens, withEligibility } from '../../projection/obsidian/materialize/index.mjs'
|
|
6
|
+
import { personalWorkspaceGraph } from '../../projection/obsidian/personal-workspace.mjs'
|
|
5
7
|
import { publishView } from '../../projection/obsidian/publication/publisher.mjs'
|
|
6
8
|
import { recheckDisplacedFiles } from '../../projection/obsidian/recovery/late-writer.mjs'
|
|
7
9
|
import { createRecoveryStore, readFileBytes } from '../../projection/obsidian/recovery/store.mjs'
|
|
@@ -31,6 +33,68 @@ export const DEFAULT_ELIGIBILITY = Object.freeze({
|
|
|
31
33
|
isEligible: (node) => node.classification === 'classified',
|
|
32
34
|
})
|
|
33
35
|
|
|
36
|
+
// A vault that is the person's own also shows the notes that carry no classification, when they decided so: the
|
|
37
|
+
// audience decision `only-you` with `unclassified: 'shown'`, which no other audience decision may carry. Those notes
|
|
38
|
+
// are the person's own files, in a vault only they see. One is admitted only when its bytes read as a note (the byte
|
|
39
|
+
// lens the emitter reads it with), so a file the emitter would refuse never stops the vault: it stays withheld, as does
|
|
40
|
+
// one that cannot be read. A note whose labels are unknown is never admitted: front matter Atelier could not read
|
|
41
|
+
// (`malformed-frontmatter`: a block scalar, a wrapped value), or any top-level `kg` key that is not a
|
|
42
|
+
// block, may carry an audience such as `sensitive` that "only you" leaves out. Only a note with no front matter, or
|
|
43
|
+
// front matter of plain top-level keys at column 0 none of which is `kg` (plainTopLevelKeys) and in which the graph
|
|
44
|
+
// reads no `kg` either, is the person's plain note. Assets follow the documents that
|
|
45
|
+
// embed them, as always.
|
|
46
|
+
export function onlyYouEligibility({ project }) {
|
|
47
|
+
const roots = new Map((project.repos ?? []).filter((repo) => !repo.external && typeof repo.path === 'string').map((repo) => [repo.name, repo.path]))
|
|
48
|
+
const readsAsNote = (node) => {
|
|
49
|
+
const root = roots.get(node.repo)
|
|
50
|
+
if (root === undefined || node.extension !== 'md' || typeof node.path !== 'string') return false
|
|
51
|
+
let bytes
|
|
52
|
+
let lens
|
|
53
|
+
try { bytes = readFileBytes(path.join(root, ...node.path.split('/'))); lens = readMarkdownLens(bytes) } catch { return false }
|
|
54
|
+
// Read now, not as the graph saw it: a file that gained front matter since is judged by what it holds now.
|
|
55
|
+
if (lens.frontmatter === null) return node.classificationReason === 'absent-frontmatter'
|
|
56
|
+
if (node.classificationReason !== 'missing-kg-block') return false
|
|
57
|
+
const text = bytes.toString('utf8')
|
|
58
|
+
const block = text.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)
|
|
59
|
+
if (block === null || !plainTopLevelKeys(block[1])) return false
|
|
60
|
+
// A second layer: the graph's own reading of the front matter has no kg key either.
|
|
61
|
+
try { return !('kg' in markdownMetadata(text)) } catch { return false }
|
|
62
|
+
}
|
|
63
|
+
return Object.freeze({
|
|
64
|
+
revision: () => 'classified-documents/v2+unclassified-notes-read-as-notes-for-only-you/v4+assets-embedded-by-eligible-documents/v1',
|
|
65
|
+
isEligible: (node) => node.classification === 'classified' || (node.classification === 'unclassified' && readsAsNote(node)),
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Whether front matter is plain `key: value` lines at its top level, none of them `kg`: every line that is not blank,
|
|
70
|
+
// a comment, or indented (a nested value or a continuation) starts with a bare key. A quoted key, a flow or JSON
|
|
71
|
+
// mapping, a complex key (`? `), a top-level list or anything else unusual is not plain, so a label it may hold (a
|
|
72
|
+
// `kg.audience: sensitive` Atelier did not read) keeps the note withheld. An allow-list, never a pattern to find.
|
|
73
|
+
const PLAIN_KEY = /^([A-Za-z_][A-Za-z0-9_-]*)[ \t]*:(?:[ \t]|$)/
|
|
74
|
+
function plainTopLevelKeys(frontmatter) {
|
|
75
|
+
// Line breaks YAML 1.1 readers honour and this reading does not (NEL, LS, PS) could hide a kg from it.
|
|
76
|
+
if (/[\u0085\u2028\u2029]/.test(frontmatter)) return false
|
|
77
|
+
let rooted = false
|
|
78
|
+
for (const line of frontmatter.split(/\r?\n/)) {
|
|
79
|
+
const trimmed = line.trim()
|
|
80
|
+
if (trimmed === '' || trimmed.startsWith('#')) continue
|
|
81
|
+
// An indented line is a nested value or a continuation only below a key at column 0; YAML also lets a whole root
|
|
82
|
+
// mapping be indented, and then its keys (kg among them) are indented too.
|
|
83
|
+
if (/^[ \t]/.test(line)) { if (!rooted) return false; continue }
|
|
84
|
+
const key = PLAIN_KEY.exec(line)
|
|
85
|
+
if (key === null || key[1] === 'kg') return false
|
|
86
|
+
rooted = true
|
|
87
|
+
}
|
|
88
|
+
return true
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// The eligibility a workspace's machine settings decide: the one above only for "only you" with unclassified notes
|
|
92
|
+
// shown, and the default, which withholds them, for every other decision and for none.
|
|
93
|
+
export function eligibilityFor({ machine, project }) {
|
|
94
|
+
const audience = machine?.decisions?.audience
|
|
95
|
+
return audience?.choice === 'only-you' && audience.unclassified === 'shown' ? onlyYouEligibility({ project }) : DEFAULT_ELIGIBILITY
|
|
96
|
+
}
|
|
97
|
+
|
|
34
98
|
export function assetEligibilityFor({ graph, eligibility }) {
|
|
35
99
|
const eligibleSources = new Set(graph.nodes.filter((node) => eligibility.isEligible(node) === true).map((node) => node.id))
|
|
36
100
|
const embeds = Array.isArray(graph.embeds) ? graph.embeds : []
|
|
@@ -59,9 +123,13 @@ export function profileFor({ project, workspaceId, audienceAllow }) {
|
|
|
59
123
|
// `index` is the engine's observation index. With both, a source whose observed digest is the cached one is not
|
|
60
124
|
// opened: observation already decided by digest that it did not change, under the stat-hint bound observation
|
|
61
125
|
// documents, and a full reconciliation hashes it again. Without an index every source is read and hashed here.
|
|
62
|
-
|
|
126
|
+
// A project bound to a personal workspace (personal-workspace.mjs) is built by that route, for every caller, with the
|
|
127
|
+
// same caches: the composition's graph, served only under a validity key the composition confirmed. With
|
|
128
|
+
// `deferPersonalValidation` (the engine), an unconfirmed key refuses `personal-validation-pending` instead of composing
|
|
129
|
+
// on this thread.
|
|
130
|
+
export function buildGraph({ project, eligibility, cache = null, index = null, deferPersonalValidation = false }) {
|
|
63
131
|
const observedDigest = cache && index ? (repoId, relative) => index.get(sourceKey(repoId, relative))?.digest ?? null : null
|
|
64
|
-
const canonical = buildCanonicalGraph(project, { fileCache: cache, observedDigest })
|
|
132
|
+
const canonical = personalWorkspaceGraph(project, { fileCache: cache, observedDigest, defer: deferPersonalValidation }) ?? buildCanonicalGraph(project, { fileCache: cache, observedDigest })
|
|
65
133
|
if (!canonical.ok) refuse('canonical-graph-invalid', 'the canonical graph has errors; no view is prepared from it', { errorCount: canonical.errors.length })
|
|
66
134
|
return withEligibility(canonical, eligibility.isEligible, assetEligibilityFor({ graph: canonical, eligibility }))
|
|
67
135
|
}
|
|
@@ -2,6 +2,7 @@ import { createHash, randomBytes as cryptoRandomBytes, timingSafeEqual } from 'n
|
|
|
2
2
|
import fs from 'node:fs'
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { atomicReplacePrivateText, ensureContainedPrivateDirectory, readRegularTextNoFollow, realPathAsStored } from '../../project/private-state.mjs'
|
|
5
|
+
import { assertAllocatedFolder, vaultRootFor } from '../../projection/obsidian/recovery/store.mjs'
|
|
5
6
|
import {
|
|
6
7
|
PLUGIN_BEARER, PLUGIN_CHALLENGE_WINDOW_MS, PLUGIN_CHANNEL_PROTOCOL, PLUGIN_HANDSHAKE_TTL_MS, PLUGIN_LEASE_TTL_MS, PLUGIN_MAX_PENDING_HANDSHAKES_PER_SCOPE, PLUGIN_MAX_SESSION_AGE_MS, PLUGIN_MAX_SESSIONS_PER_SCOPE,
|
|
7
8
|
PLUGIN_RENEW_INTERVAL_MS, PLUGIN_STATUS_SCHEMA, pluginClientProof, pluginKeyHint, pluginRequestMac, pluginResponseMac, pluginServerProof, pluginSessionKey, pluginVaultProof,
|
|
@@ -158,9 +159,16 @@ export function createPluginChannelForOracleTests({
|
|
|
158
159
|
onSessionOpened = () => {},
|
|
159
160
|
}, primitives = PLUGIN_CHANNEL_PRIMITIVES) {
|
|
160
161
|
const rules = { ...PLUGIN_CHANNEL_PRIMITIVES, ...primitives }
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
|
|
162
|
+
// The view's vault wherever it is (vaultRootFor: allocated for the view, else under the data root), and an allocated one
|
|
163
|
+
// only while it is the folder recorded; as the file system stores the path, as the store registers the vault in the app
|
|
164
|
+
// (realPathAsStored): a data root given in another letter case names the same vault.
|
|
165
|
+
const vaultRootOf = (scopeId) => {
|
|
166
|
+
try {
|
|
167
|
+
const found = vaultRootFor({ workspaceRoot, workspaceId, scopeId })
|
|
168
|
+
if (found.allocation !== null) assertAllocatedFolder(found.allocation)
|
|
169
|
+
return realPathAsStored(found.path)
|
|
170
|
+
} catch { return null }
|
|
171
|
+
}
|
|
164
172
|
const keyDigestOf = (bearer) => createHash('sha256').update(bearer, 'utf8').digest()
|
|
165
173
|
const handshakes = new Map()
|
|
166
174
|
const pruneHandshakes = () => { const at = now(); for (const [handshakeId, handshake] of handshakes) if (handshake.expiresAt <= at) handshakes.delete(handshakeId) }
|
|
@@ -4,6 +4,7 @@ import { atomicReplacePrivateText, ensureContainedPrivateDirectory, openRegularF
|
|
|
4
4
|
import { PLUGIN_ID } from '../../projection/obsidian/plugin-bridge/channel.mjs'
|
|
5
5
|
import { COMMUNITY_PLUGINS_PATH } from '../../projection/obsidian/materialize/settings.mjs'
|
|
6
6
|
import { sha256Digest } from '../../projection/obsidian/materialize/byte-lens.mjs'
|
|
7
|
+
import { vaultRootFor } from '../../projection/obsidian/recovery/store.mjs'
|
|
7
8
|
import { canonicalJson, isPlainObject, isoTime } from './documents.mjs'
|
|
8
9
|
|
|
9
10
|
// Whether the person wants Atelier's plugin in one vault, as the vault itself
|
|
@@ -42,8 +43,14 @@ const MAX_SETTINGS_BYTES = 256 * 1024
|
|
|
42
43
|
const segment = (identifier) => identifier.replaceAll(':', '_')
|
|
43
44
|
|
|
44
45
|
export const pluginChoiceDirectory = (workspaceRoot) => path.join(workspaceRoot, 'state', 'plugin', 'choices')
|
|
45
|
-
// Where the recovery store
|
|
46
|
-
|
|
46
|
+
// Where a view's vault is, as the recovery store finds it (vaultRootFor): the folder allocated to the view where the
|
|
47
|
+
// workspace decided its vaults live, else its folder under the data root. A record that cannot be read, or was lost,
|
|
48
|
+
// refuses, typed. Pass `workspaceId`; every caller here does. The two-argument form of earlier releases still works:
|
|
49
|
+
// it takes the name of the workspace's private-state folder as the id, which a record of another workspace (or a
|
|
50
|
+
// folder reached through a link) does not match, and such a record refuses, typed, rather than answering another vault.
|
|
51
|
+
export function viewVaultRoot(workspaceRoot, scopeId, workspaceId = path.basename(String(workspaceRoot))) {
|
|
52
|
+
return vaultRootFor({ workspaceRoot, workspaceId, scopeId }).path
|
|
53
|
+
}
|
|
47
54
|
const choiceFile = (workspaceRoot, scopeId) => path.join(pluginChoiceDirectory(workspaceRoot), `${segment(scopeId)}.json`)
|
|
48
55
|
|
|
49
56
|
function validChoice(document, { workspaceId, scopeId }) {
|
|
@@ -142,7 +149,10 @@ export function decidePluginChoice({ workspaceRoot, workspaceId, scopeId, vaultR
|
|
|
142
149
|
// `pending` (the view's next preparation records it).
|
|
143
150
|
export function currentPluginChoice({ workspaceRoot, workspaceId, scopeId }) {
|
|
144
151
|
const recorded = readPluginChoice({ workspaceRoot, workspaceId, scopeId })
|
|
145
|
-
|
|
152
|
+
// A view whose vault cannot be found (its record cannot be read) shows what was recorded; its view says why.
|
|
153
|
+
let vaultRoot
|
|
154
|
+
try { vaultRoot = viewVaultRoot(workspaceRoot, scopeId, workspaceId) } catch (error) { if (typeof error?.code !== 'string') throw error; return recorded }
|
|
155
|
+
const change = changeShown(recorded, readCommunityEntry(vaultRoot).entry)
|
|
146
156
|
return change === null ? recorded : { ...change, since: null, pending: true }
|
|
147
157
|
}
|
|
148
158
|
|
|
@@ -150,7 +160,9 @@ export function currentPluginChoice({ workspaceRoot, workspaceId, scopeId }) {
|
|
|
150
160
|
// no app ran, it is confirmed: an app reads the list, entry and all, when it
|
|
151
161
|
// next opens the vault. Published through a running app, it is only offered:
|
|
152
162
|
// that app may not have read it, and the plugin running there confirms it
|
|
153
|
-
// (confirmPluginSeen).
|
|
163
|
+
// (confirmPluginSeen). So is one published into a vault no Obsidian listed
|
|
164
|
+
// while one ran (`direct-unheld`): the evidence covers only the lists Atelier
|
|
165
|
+
// can read, not another profile's or another machine's.
|
|
154
166
|
const IN_PLACE = new Set(['policy-satisfied', 'created', 'published', 'published-external-captured', 'already-current'])
|
|
155
167
|
export function confirmPluginEntry({ workspaceRoot, workspaceId, scopeId, result, clock }) {
|
|
156
168
|
const unit = (result?.notes ?? []).find((entry) => entry.path === COMMUNITY_PLUGINS_PATH)
|
|
@@ -18,6 +18,10 @@ import { viewVaultRoot } from './plugin-choice.mjs'
|
|
|
18
18
|
// where a file goes) is asked about once, not at every tick; a change to it is
|
|
19
19
|
// asked about again. Where the plugin is turned off in the vault, a pinned
|
|
20
20
|
// file that is gone is no drift: the publisher never makes one again.
|
|
21
|
+
// `settle(scopeIds)` looks again after each tick, and takes as seen what a
|
|
22
|
+
// publication of that tick could not change, so a drift publishing cannot
|
|
23
|
+
// repair is not published a second time at the next look (at a service's
|
|
24
|
+
// start, say).
|
|
21
25
|
|
|
22
26
|
const segment = (identifier) => identifier.replaceAll(':', '_')
|
|
23
27
|
const TURNED_OFF = 'turned-off-in-this-vault'
|
|
@@ -33,7 +37,7 @@ function pinnedPlugin(workspaceRoot, workspaceId, scopeId) {
|
|
|
33
37
|
if (sha256Digest(bytes) !== pointer.manifestDigest) return null
|
|
34
38
|
const owned = JSON.parse(bytes.toString('utf8'))?.ext?.[OBSIDIAN_EXT_KEY]?.settings?.pluginOwned
|
|
35
39
|
if (!Array.isArray(owned?.files)) return null
|
|
36
|
-
return { files: owned.files.filter((file) => typeof file?.path === 'string' && typeof file?.digest === 'string'), off: owned.withheldBecause === TURNED_OFF }
|
|
40
|
+
return { manifest: pointer.manifestDigest, files: owned.files.filter((file) => typeof file?.path === 'string' && typeof file?.digest === 'string'), off: owned.withheldBecause === TURNED_OFF }
|
|
37
41
|
} catch {
|
|
38
42
|
return null
|
|
39
43
|
}
|
|
@@ -53,24 +57,68 @@ function onDisk(vaultRoot, relativePath) {
|
|
|
53
57
|
try { return readFileDigest(path.join(vaultRoot, relativePath)) ?? 'absent' } catch { return 'unreadable' }
|
|
54
58
|
}
|
|
55
59
|
|
|
60
|
+
// A look, as it is compared: the vault root, and each pinned file with its pin and what the disk holds.
|
|
61
|
+
const signatureOf = (record) => JSON.stringify([record.root, record.files.map((file) => file.digest), record.files.map((file) => file.state)])
|
|
62
|
+
// Never what a look finds on disk: a path recorded so is asked about at the next look whatever it holds then.
|
|
63
|
+
const NOT_SEEN = 'not-seen'
|
|
64
|
+
|
|
56
65
|
export function createPluginDriftObserver({ workspaceRoot, workspaceId }) {
|
|
66
|
+
// Per view: the last look ({ root, files: [{ path, digest, state }], manifest }).
|
|
57
67
|
const looked = new Map()
|
|
68
|
+
// The views the last observe asked to be prepared again: the tick that follows publishes them.
|
|
69
|
+
let asked = new Set()
|
|
70
|
+
// What the pinned plugin files of a view look like on disk now; null when its committed generation pins none.
|
|
71
|
+
const look = (scopeId) => {
|
|
72
|
+
const pinned = pinnedPlugin(workspaceRoot, workspaceId, scopeId)
|
|
73
|
+
if (pinned === null || pinned.files.length === 0) return null
|
|
74
|
+
// The view's vault wherever it is; one that cannot be found is refused by the engine, not observed here.
|
|
75
|
+
let vaultRoot
|
|
76
|
+
try { vaultRoot = viewVaultRoot(workspaceRoot, scopeId, workspaceId) } catch (error) { if (typeof error?.code !== 'string') throw error; return null }
|
|
77
|
+
let root = 'absent'
|
|
78
|
+
try { const stat = fs.lstatSync(vaultRoot); root = stat.isSymbolicLink() ? 'link' : (stat.mode & 0o777).toString(8) } catch { root = 'absent' }
|
|
79
|
+
const files = pinned.files.map((file) => ({ path: file.path, digest: file.digest, state: onDisk(vaultRoot, file.path) }))
|
|
80
|
+
const drifted = (file) => file.state !== file.digest && !(pinned.off && file.state === 'absent')
|
|
81
|
+
return { root, files, manifest: pinned.manifest, drifted, differs: files.some(drifted) }
|
|
82
|
+
}
|
|
58
83
|
return {
|
|
59
84
|
observe(scopeIds) {
|
|
60
85
|
const drifted = []
|
|
61
86
|
for (const scopeId of scopeIds) {
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const disk = pinned.files.map((file) => onDisk(vaultRoot, file.path))
|
|
68
|
-
const differs = disk.some((found, index) => found !== pinned.files[index].digest && !(pinned.off && found === 'absent'))
|
|
69
|
-
const signature = JSON.stringify([root, pinned.files.map((file) => file.digest), disk])
|
|
70
|
-
if (differs && looked.get(scopeId) !== signature) drifted.push(scopeId)
|
|
71
|
-
looked.set(scopeId, signature)
|
|
87
|
+
const seen = look(scopeId)
|
|
88
|
+
if (seen === null) { looked.delete(scopeId); continue }
|
|
89
|
+
const last = looked.get(scopeId)
|
|
90
|
+
if (seen.differs && (last === undefined || signatureOf(last) !== signatureOf(seen))) drifted.push(scopeId)
|
|
91
|
+
looked.set(scopeId, { root: seen.root, files: seen.files, manifest: seen.manifest })
|
|
72
92
|
}
|
|
93
|
+
asked = new Set(drifted)
|
|
73
94
|
return drifted
|
|
74
95
|
},
|
|
96
|
+
// After a tick. A view this tick published (one the last observe asked for, one whose committed generation
|
|
97
|
+
// changed since the last look, or one never looked at before) had its plugin files written or left for the person
|
|
98
|
+
// by its publisher. Of what the disk shows now, only what that publication could not change is taken as seen,
|
|
99
|
+
// without asking: a file as it pins it, or one that holds what it held before the tick under the same pin and the
|
|
100
|
+
// same vault root. So a drift publishing cannot repair (the data file of a vault root that is a link, say) is not
|
|
101
|
+
// asked about again once it was asked about under its pin. Anything else is not taken as seen, and the next look
|
|
102
|
+
// asks about it: a file another writer removed or changed after the publication, while the tick went on, or
|
|
103
|
+
// brought back to what it held under a pin the tick replaced; a file the publication left for the person, which
|
|
104
|
+
// the person may have repaired before the tick ended; and any file, when the vault root changed during the tick.
|
|
105
|
+
// Asking about a file that is still left writes no vault file: the publisher does not publish a committed
|
|
106
|
+
// generation again for it (pluginFilesDrifted). Any other view keeps its last look.
|
|
107
|
+
settle(scopeIds) {
|
|
108
|
+
for (const scopeId of scopeIds) {
|
|
109
|
+
const seen = look(scopeId)
|
|
110
|
+
if (seen === null) { looked.delete(scopeId); continue }
|
|
111
|
+
const last = looked.get(scopeId)
|
|
112
|
+
if (!asked.has(scopeId) && last !== undefined && last.manifest === seen.manifest) continue
|
|
113
|
+
// Keyed by path and pin: what a file held under a pin this tick replaced says nothing about the new one.
|
|
114
|
+
const key = (file) => `${file.path}\u0000${file.digest}`
|
|
115
|
+
const before = new Map((last?.files ?? []).map((file) => [key(file), file.state]))
|
|
116
|
+
// And only under the same vault root: a root made private during the tick changes what the publisher leaves.
|
|
117
|
+
const sameRoot = last !== undefined && last.root === seen.root
|
|
118
|
+
const files = seen.files.map((file) => (!seen.drifted(file) || (sameRoot && before.get(key(file)) === file.state) ? file : { ...file, state: NOT_SEEN }))
|
|
119
|
+
looked.set(scopeId, { root: seen.root, files, manifest: seen.manifest })
|
|
120
|
+
}
|
|
121
|
+
asked = new Set()
|
|
122
|
+
},
|
|
75
123
|
}
|
|
76
124
|
}
|
|
@@ -9,6 +9,18 @@ export const PLUGIN_PRESENCE_REASONS = Object.freeze(['live-lease', 'no-live-lea
|
|
|
9
9
|
// The two ways back, for a view whose vault turned the plugin off.
|
|
10
10
|
export const turnPluginOnNext = (scopeId) => `turn Atelier on in Obsidian (Settings, Community plugins), or run \`atelier obsidian plugin on --scope ${scopeId}\`, which restores it at the view's next publication`
|
|
11
11
|
|
|
12
|
+
// A view kept `current` while a plugin file its committed generation pins waits for an app that qualifies: the notes
|
|
13
|
+
// are verified, the plugin file (often `data.json`) is not written yet (engine.mjs). Its freshness reason says so.
|
|
14
|
+
export const PLUGIN_FILES_WAIT_FOR_APP = 'verified-by-read-back-plugin-waits-for-app'
|
|
15
|
+
export const PLUGIN_FILES_WAIT_NEXT = 'the view\'s notes are current, but a file of Atelier\'s plugin in this vault is not written yet, since Obsidian could not be coordinated with (it runs without its command line answering, say): it is written once Obsidian answers its command line or is quit, and it is retried automatically'
|
|
16
|
+
|
|
17
|
+
// The presence, with the plugin files that wait for the app when the view's freshness says so (`files`, and the next
|
|
18
|
+
// step unless the presence has its own).
|
|
19
|
+
export function withPluginFilesPending(presence, freshness) {
|
|
20
|
+
if (freshness?.state !== 'current' || freshness.reason !== PLUGIN_FILES_WAIT_FOR_APP) return presence
|
|
21
|
+
return { ...presence, files: 'waits-for-app', next: presence.next ?? PLUGIN_FILES_WAIT_NEXT }
|
|
22
|
+
}
|
|
23
|
+
|
|
12
24
|
export function pluginPresenceOf(statusDocument, scopeId) {
|
|
13
25
|
if (!isPlainObject(statusDocument)) return { present: false, reason: 'service-not-running' }
|
|
14
26
|
const scopes = isPlainObject(statusDocument.plugins) && Array.isArray(statusDocument.plugins.scopes) ? statusDocument.plugins.scopes : null
|
|
@@ -40,7 +52,8 @@ export function withPluginReportedVersion(appProbe, readPresence, { maxAgeMs = 1
|
|
|
40
52
|
return {
|
|
41
53
|
async inspect() {
|
|
42
54
|
const [observation, presence] = await Promise.all([appProbe.inspect(), presenceNow()])
|
|
43
|
-
|
|
55
|
+
// A command line that answered it is turned off stays that answer: a plugin's version never hides it.
|
|
56
|
+
if (presence?.present !== true || observation?.installed !== true || observation?.cli !== true || observation.running === false || typeof observation.version === 'string' || observation.cliOff === true) return observation
|
|
44
57
|
return { ...observation, running: true, version: presence.appVersion, noVaultOpen: false, versionSource: 'plugin' }
|
|
45
58
|
},
|
|
46
59
|
vaultState: (input) => appProbe.vaultState(input),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process'
|
|
2
2
|
import fs from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
3
4
|
import { executableIdentity } from './service-record.mjs'
|
|
4
5
|
|
|
5
6
|
// Whether a live PID provably runs the executable a service record names,
|
|
@@ -32,27 +33,36 @@ export function readProcessCommandLine(pid, { platform = process.platform, readF
|
|
|
32
33
|
return null
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
// Pure. The command line must name the recorded entry module,
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
// Pure. The command line must name the recorded entry module, by one of
|
|
37
|
+
// `entries` (its recorded path, and the path it was started by when that is
|
|
38
|
+
// another name for it), and a runtime identifier it carries must be the
|
|
39
|
+
// recorded one.
|
|
40
|
+
export function commandLineNamesRecord(commandLine, record, entries = [record?.executable?.path]) {
|
|
41
|
+
const names = entries.filter((entry) => typeof entry === 'string' && entry !== '')
|
|
42
|
+
if (names.length === 0 || typeof record?.runtimeId !== 'string') return false
|
|
40
43
|
if (Array.isArray(commandLine)) {
|
|
41
|
-
if (!commandLine.slice(1).includes(
|
|
44
|
+
if (!commandLine.slice(1).some((word) => names.includes(word))) return false
|
|
42
45
|
const named = commandLine.filter((word) => word.startsWith(RUNTIME_ARGUMENT)).map((word) => word.slice(RUNTIME_ARGUMENT.length))
|
|
43
46
|
return named.every((runtimeId) => runtimeId === record.runtimeId)
|
|
44
47
|
}
|
|
45
48
|
if (typeof commandLine !== 'string') return false
|
|
46
|
-
if (
|
|
49
|
+
if (!names.some((entry) => ` ${commandLine} `.includes(` ${entry} `))) return false
|
|
47
50
|
const named = [...commandLine.matchAll(/(?:^|\s)--runtime-id=(\S+)/g)].map((match) => match[1])
|
|
48
51
|
return named.every((runtimeId) => runtimeId === record.runtimeId)
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
// The recorded executable is still the bytes that were recorded, and the live
|
|
52
|
-
// process was started on it
|
|
53
|
-
|
|
55
|
+
// process was started on it: by its recorded path, or by the path the record
|
|
56
|
+
// says it was started by (a login item names the installed entry by its path),
|
|
57
|
+
// when that still leads to the recorded one.
|
|
58
|
+
export function processRunsRecordedExecutable(record, { commandLineOf = readProcessCommandLine, identityOf = executableIdentity, realpathOf = fs.realpathSync } = {}) {
|
|
54
59
|
try {
|
|
55
60
|
if (identityOf(record.executable.path).digest !== record.executable.digest) return false
|
|
56
61
|
} catch { return false }
|
|
57
|
-
|
|
62
|
+
const entries = [record.executable.path]
|
|
63
|
+
const invokedAs = record.executable.ext?.invokedAs
|
|
64
|
+
if (typeof invokedAs === 'string' && path.isAbsolute(invokedAs)) {
|
|
65
|
+
try { if (realpathOf(invokedAs) === record.executable.path) entries.push(invokedAs) } catch { /* not another name for it */ }
|
|
66
|
+
}
|
|
67
|
+
return commandLineNamesRecord(commandLineOf(record.pid), record, entries)
|
|
58
68
|
}
|