@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
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
trustedMutationRequest,
|
|
26
26
|
trustedReadRequest,
|
|
27
27
|
} from './security.mjs'
|
|
28
|
+
import { canonicalize } from '../attestation/jcs.mjs'
|
|
28
29
|
import { createProposalStore } from '../collaboration/proposals.mjs'
|
|
29
30
|
import {
|
|
30
31
|
renderProposalDetailPageHtml,
|
|
@@ -34,6 +35,9 @@ import {
|
|
|
34
35
|
import { createReviewStore } from '../collaboration/review-store.mjs'
|
|
35
36
|
import { loadBoundRun, currentRunEligibility } from '../readiness-protocols/evidence.mjs'
|
|
36
37
|
import { renderReviewPage } from '../ui/review-page.mjs'
|
|
38
|
+
import { renderKnowledgePage } from '../ui/knowledge-page.mjs'
|
|
39
|
+
import { knowledgeDashboard, knowledgeQuestionContext, loadKnowledgeWorkspace } from '../knowledge/workspace.mjs'
|
|
40
|
+
import { createKnowledgeSessions } from '../knowledge/sessions.mjs'
|
|
37
41
|
|
|
38
42
|
export const ATELIER_CONTEXT_SCHEMA = 'atelier-context@v1'
|
|
39
43
|
export const ATELIER_PRESENCE_SCHEMA = 'atelier-presence@v1'
|
|
@@ -216,6 +220,28 @@ function listenFailure(error, port) {
|
|
|
216
220
|
return error
|
|
217
221
|
}
|
|
218
222
|
|
|
223
|
+
function knowledgeError(error) {
|
|
224
|
+
const message = error instanceof Error ? error.message : ''
|
|
225
|
+
if (message === 'coauthor requires a Git workspace with untracked, ignored .atelier-local/ state')
|
|
226
|
+
return 'coauthor requires a Git workspace with untracked, ignored local state. Initialize Git and ignore the .atelier-local directory.'
|
|
227
|
+
if (/^collaboration (ledger|event).*ceiling/.test(message))
|
|
228
|
+
return 'knowledge history capacity reached. Preserve the ledger and saved values for operator inspection; do not compact active session chains.'
|
|
229
|
+
if (message.startsWith('EEXIST: private state is locked'))
|
|
230
|
+
return 'knowledge writer is busy. Preserve the request and retry after the active writer finishes.'
|
|
231
|
+
const safe = [
|
|
232
|
+
'workspace changed', 'stale revision', 'event id conflict', 'source changed',
|
|
233
|
+
'coauthor session not found', 'coauthor requires', 'unknown knowledge',
|
|
234
|
+
'invalid knowledge', 'request UUID', 'retry budget exhausted',
|
|
235
|
+
'receipt and failure events are store-owned', 'event refused in',
|
|
236
|
+
'coauthor plan must', 'knowledge session configuration mismatch',
|
|
237
|
+
'knowledge session identity mismatch', 'knowledge session exceeds',
|
|
238
|
+
'knowledge session list exceeds',
|
|
239
|
+
'knowledge history unavailable', 'knowledge start marker mismatch',
|
|
240
|
+
]
|
|
241
|
+
return safe.some(prefix => message.startsWith(prefix)) && !message.includes('/')
|
|
242
|
+
? message : 'Knowledge operation unavailable. Preserve your draft and inspect the local workspace or active writer.'
|
|
243
|
+
}
|
|
244
|
+
|
|
219
245
|
function doctorEnvelope() {
|
|
220
246
|
return {
|
|
221
247
|
ok: true,
|
|
@@ -231,6 +257,7 @@ export function createAtelierSidecarServer({
|
|
|
231
257
|
workspaceRoot = process.cwd(),
|
|
232
258
|
stateDir = workspaceRoot,
|
|
233
259
|
reviewProject = null,
|
|
260
|
+
knowledgeProject = null,
|
|
234
261
|
port = 0,
|
|
235
262
|
presenceTtlMs = Number(process.env.ATELIER_PRESENCE_TTL_MS || 5 * 60 * 1000),
|
|
236
263
|
} = {}) {
|
|
@@ -243,6 +270,7 @@ export function createAtelierSidecarServer({
|
|
|
243
270
|
})
|
|
244
271
|
const publication = loadPublishedWorkspaceManifest(root)
|
|
245
272
|
const review = reviewProject ? createReviewStore(reviewProject) : null
|
|
273
|
+
const knowledge = knowledgeProject ? createKnowledgeSessions(knowledgeProject) : null
|
|
246
274
|
const workspaceId = workspaceIdForRoot(root)
|
|
247
275
|
const noncePath = path.join(stateDirReal, '.atelier-nonce')
|
|
248
276
|
const presencePath = path.join(stateDirReal, '.atelier-presence.json')
|
|
@@ -253,6 +281,95 @@ export function createAtelierSidecarServer({
|
|
|
253
281
|
workspaceId,
|
|
254
282
|
})
|
|
255
283
|
|
|
284
|
+
// One copy-only declaration is retained from the workshop: this path, this
|
|
285
|
+
// action, from this view. The request stays `{ sessionId }`; everything
|
|
286
|
+
// retained is derived here from fresh owning readbacks, and the generic
|
|
287
|
+
// /api/proposals route keeps its published-HTML fence.
|
|
288
|
+
const WORKSHOP_HANDOFF_TARGET = Object.freeze({ path: 'records/checklist.md', action: 'copy.repoPath', viewId: 'knowledge-workshop' })
|
|
289
|
+
const sameJson = (left, right) => canonicalize(left) === canonicalize(right)
|
|
290
|
+
const refusedRetention = (error, status = 'refused') => ({ status: 409, body: { ok: false, error, retention: { status } } })
|
|
291
|
+
const retentionSummary = (record, outcome, sourceCurrent) => ({
|
|
292
|
+
status: 'retained',
|
|
293
|
+
outcome,
|
|
294
|
+
sourceCurrent,
|
|
295
|
+
proposalId: record.proposal.id,
|
|
296
|
+
readback: `/api/proposals/${record.proposal.id}`,
|
|
297
|
+
receipt: {
|
|
298
|
+
sessionId: record.retention.sessionId,
|
|
299
|
+
requestId: record.retention.requestId,
|
|
300
|
+
valueDigest: record.retention.valueDigest,
|
|
301
|
+
},
|
|
302
|
+
payloadDigest: record.retention.payloadDigest,
|
|
303
|
+
proposalStatus: record.proposal.status,
|
|
304
|
+
eventVersion: record.proposal.eventVersion,
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
function retainWorkshopHandoff(body) {
|
|
308
|
+
if (!body || Array.isArray(body) || Object.keys(body).join(',') !== 'sessionId' || typeof body.sessionId !== 'string') {
|
|
309
|
+
return refusedRetention('workshop handoff accepts a session id only')
|
|
310
|
+
}
|
|
311
|
+
// A fresh owning readback: this throws when the session, its ledger or a
|
|
312
|
+
// saved value is missing, redirected or altered.
|
|
313
|
+
const session = knowledge.read(body.sessionId)
|
|
314
|
+
const selected = session.record.flow === 'workshop-review'
|
|
315
|
+
? session.state.saved.find((value) => value.fieldId === 'authored-revision')
|
|
316
|
+
: null
|
|
317
|
+
if (!selected) {
|
|
318
|
+
// No separately selected draft receipt: prepared only, as before. An
|
|
319
|
+
// incomplete Review or a response that selects nothing is refused by
|
|
320
|
+
// the existing preparation.
|
|
321
|
+
return { status: 200, body: { ...knowledge.workshopHandoff(body), retention: { status: 'not-retained', reason: 'no separately selected draft receipt' } } }
|
|
322
|
+
}
|
|
323
|
+
const own = session.owningReadbacks?.verified === true
|
|
324
|
+
? session.owningReadbacks.immutableValues.find((value) => sameJson(value.receipt, selected.receipt))
|
|
325
|
+
: null
|
|
326
|
+
if (!own || own.text !== selected.text || selected.receipt.sessionId !== session.record.id) {
|
|
327
|
+
return refusedRetention('the selected draft receipt is not confirmed by its owning readback')
|
|
328
|
+
}
|
|
329
|
+
if (!session.current) {
|
|
330
|
+
// Reopen after a source change: read back what was retained, never append.
|
|
331
|
+
const known = proposals.lookupRetainedProposal(selected.receipt)
|
|
332
|
+
if (known.outcome === 'retained') {
|
|
333
|
+
return {
|
|
334
|
+
status: 200,
|
|
335
|
+
body: {
|
|
336
|
+
ok: true,
|
|
337
|
+
handoff: proposals.retainedHandoff(known.record),
|
|
338
|
+
sourceEditsApplied: false,
|
|
339
|
+
directWrite: false,
|
|
340
|
+
retention: retentionSummary(known.record, 'existing', false),
|
|
341
|
+
},
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return known.outcome === 'not-found'
|
|
345
|
+
? refusedRetention('the source changed before this draft was retained; nothing is retained for this receipt', 'not-retained')
|
|
346
|
+
: refusedRetention(known.error, known.outcome === 'conflict' ? 'refused' : 'unknown')
|
|
347
|
+
}
|
|
348
|
+
const prepared = knowledge.workshopHandoff(body)
|
|
349
|
+
const handoff = prepared.handoff
|
|
350
|
+
if (
|
|
351
|
+
!sameJson(handoff.proposal.selectedDraftReceipt, selected.receipt) || handoff.sessionId !== session.record.id ||
|
|
352
|
+
handoff.path !== WORKSHOP_HANDOFF_TARGET.path || handoff.action !== WORKSHOP_HANDOFF_TARGET.action ||
|
|
353
|
+
handoff.viewId !== WORKSHOP_HANDOFF_TARGET.viewId || handoff.directWrite !== false ||
|
|
354
|
+
handoff.applyEndpoint !== null || handoff.diff?.replacement !== selected.text
|
|
355
|
+
) {
|
|
356
|
+
return refusedRetention('the prepared handoff does not match the selected draft readback')
|
|
357
|
+
}
|
|
358
|
+
// The original contribution is read back from its own session as well.
|
|
359
|
+
const original = knowledge.read(handoff.proposal.contributionSessionId)
|
|
360
|
+
const values = original.owningReadbacks?.verified === true ? original.owningReadbacks.immutableValues : null
|
|
361
|
+
if (
|
|
362
|
+
!values ||
|
|
363
|
+
!sameJson([...values.map((value) => value.receipt), selected.receipt], handoff.proposal.savedDraftReceipts) ||
|
|
364
|
+
values.find((value) => value.receipt.fieldId === 'original-words')?.text !== handoff.proposal.originalWords
|
|
365
|
+
) {
|
|
366
|
+
return refusedRetention('the original contribution is not confirmed by its owning readback')
|
|
367
|
+
}
|
|
368
|
+
const result = proposals.retainProposal(handoff)
|
|
369
|
+
if (!result.ok) return refusedRetention(result.error, result.outcome === 'unknown' ? 'unknown' : 'refused')
|
|
370
|
+
return { status: 200, body: { ...prepared, retention: retentionSummary(result.record, result.outcome, true) } }
|
|
371
|
+
}
|
|
372
|
+
|
|
256
373
|
function resolvePublishedPath({ rel, requireFile = false, requireHtml = false } = {}) {
|
|
257
374
|
const resolved = resolveWorkspacePath({
|
|
258
375
|
workspaceRoot: root,
|
|
@@ -395,8 +512,8 @@ export function createAtelierSidecarServer({
|
|
|
395
512
|
// Browsers send Origin on POST; same-origin GET normally omits it. Keep
|
|
396
513
|
// the strict origin gate for nonce disclosure instead of relaxing the
|
|
397
514
|
// existing session-auth GET contract or accepting unclassified reads.
|
|
398
|
-
if (review && url.pathname === '/api/review/session') {
|
|
399
|
-
if (!trustedMutationRequest(req.headers) || !body || Array.isArray(body) || Object.keys(body).length) {
|
|
515
|
+
if ((review && url.pathname === '/api/review/session') || (knowledge && url.pathname === '/api/knowledge/session')) {
|
|
516
|
+
if (!trustedMutationRequest(req.headers) || !body || Array.isArray(body) || Object.keys(body).length || (url.pathname === '/api/knowledge/session' && url.search)) {
|
|
400
517
|
json(res, 403, { ok: false, error: 'review session requires a same-origin empty request' })
|
|
401
518
|
return
|
|
402
519
|
}
|
|
@@ -411,6 +528,22 @@ export function createAtelierSidecarServer({
|
|
|
411
528
|
return
|
|
412
529
|
}
|
|
413
530
|
|
|
531
|
+
if (knowledge && url.pathname.startsWith('/api/knowledge/')) {
|
|
532
|
+
const operation = url.pathname.slice('/api/knowledge/'.length)
|
|
533
|
+
if (!['start', 'event', 'recover', 'lookup', 'workshop-handoff', 'workshop-review'].includes(operation) || url.search) {
|
|
534
|
+
json(res, 404, { ok: false, error: 'unknown knowledge action' }); return
|
|
535
|
+
}
|
|
536
|
+
try {
|
|
537
|
+
if (operation === 'workshop-handoff') {
|
|
538
|
+
const retained = retainWorkshopHandoff(body)
|
|
539
|
+
json(res, retained.status, retained.body)
|
|
540
|
+
} else {
|
|
541
|
+
json(res, 200, knowledge[operation === 'workshop-review' ? 'workshopReview' : operation](body))
|
|
542
|
+
}
|
|
543
|
+
} catch (error) { json(res, 409, { ok: false, error: knowledgeError(error) }) }
|
|
544
|
+
return
|
|
545
|
+
}
|
|
546
|
+
|
|
414
547
|
if (review && url.pathname === '/api/review/contributions') {
|
|
415
548
|
const result = review.contribute(body)
|
|
416
549
|
json(res, result.status, result)
|
|
@@ -528,6 +661,38 @@ export function createAtelierSidecarServer({
|
|
|
528
661
|
return
|
|
529
662
|
}
|
|
530
663
|
|
|
664
|
+
if (knowledge && url.pathname === '/knowledge' && !url.search) {
|
|
665
|
+
const body = renderKnowledgePage()
|
|
666
|
+
res.writeHead(200, htmlDocumentHeaders(Buffer.byteLength(body)))
|
|
667
|
+
res.end(body)
|
|
668
|
+
return
|
|
669
|
+
}
|
|
670
|
+
if (knowledge && url.pathname.startsWith('/api/knowledge/')) {
|
|
671
|
+
try {
|
|
672
|
+
let result
|
|
673
|
+
const operation = url.pathname.slice('/api/knowledge/'.length)
|
|
674
|
+
const parameters = operation === 'context' ? ['id', 'mode'] : operation === 'read' ? ['id'] : []
|
|
675
|
+
if ([...url.searchParams.keys()].some(k => !parameters.includes(k)) || [...url.searchParams.keys()].length !== new Set(url.searchParams.keys()).size) throw new Error('invalid knowledge query')
|
|
676
|
+
if (operation === 'dashboard') {
|
|
677
|
+
const dashboard = knowledgeDashboard(loadKnowledgeWorkspace(knowledgeProject))
|
|
678
|
+
try {
|
|
679
|
+
const profile = knowledge.workshopProfile()
|
|
680
|
+
dashboard.flows = [profile.flow, ...dashboard.flows]
|
|
681
|
+
dashboard.workshop = { available: true, context: profile.context }
|
|
682
|
+
} catch {
|
|
683
|
+
dashboard.workshop = { available: false, reason: 'No current actionable public workshop finding.' }
|
|
684
|
+
}
|
|
685
|
+
result = { dashboard }
|
|
686
|
+
}
|
|
687
|
+
else if (operation === 'context') result = { context: knowledgeQuestionContext(loadKnowledgeWorkspace(knowledgeProject), url.searchParams.get('id'), url.searchParams.get('mode') || 'graph') }
|
|
688
|
+
else if (operation === 'sessions') result = knowledge.list()
|
|
689
|
+
else if (operation === 'read') result = knowledge.read(url.searchParams.get('id'))
|
|
690
|
+
else { json(res, 404, { ok: false, error: 'unknown knowledge action' }); return }
|
|
691
|
+
json(res, 200, { ok: true, ...result })
|
|
692
|
+
} catch (error) { json(res, 409, { ok: false, error: knowledgeError(error) }) }
|
|
693
|
+
return
|
|
694
|
+
}
|
|
695
|
+
|
|
531
696
|
if (review && url.pathname === '/review') {
|
|
532
697
|
const body = renderReviewPage()
|
|
533
698
|
res.writeHead(200, htmlDocumentHeaders(Buffer.byteLength(body)))
|
|
@@ -626,6 +791,8 @@ export function createAtelierSidecarServer({
|
|
|
626
791
|
proposal: record.proposal,
|
|
627
792
|
diff: record.diff,
|
|
628
793
|
copyable: record.copyable,
|
|
794
|
+
// A retained workshop handoff is read back whole; other proposals are unchanged.
|
|
795
|
+
...(record.retention ? { payload: record.payload, retention: record.retention } : {}),
|
|
629
796
|
} : { ok: false, error: result.error, diagnostics: result.diagnostics })
|
|
630
797
|
return
|
|
631
798
|
}
|
package/src/server/server.mjs
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
1
3
|
import { AtelierDiagnosticError, resolveProjectConfig } from '../project/config.mjs'
|
|
2
4
|
import { createAtelierSidecarServer } from './local-sidecar.mjs'
|
|
3
5
|
|
|
6
|
+
// dev serves what build wrote and builds nothing itself. Before build, the
|
|
7
|
+
// output folder (or, after graph alone, its manifest) is absent, and the
|
|
8
|
+
// sidecar failed with a raw ENOENT carrying an absolute path or a redacted
|
|
9
|
+
// internal error. Name the missing steps instead, and name the folder only
|
|
10
|
+
// relative to the project config. A non-directory is left to the sidecar.
|
|
11
|
+
function assertProjectionBuilt(project) {
|
|
12
|
+
const stat = fs.statSync(project.outputRoot, { throwIfNoEntry: false })
|
|
13
|
+
if (stat && !stat.isDirectory()) return
|
|
14
|
+
const manifestMissing = stat && !fs.existsSync(path.join(project.outputRoot, 'atelier.manifest.json'))
|
|
15
|
+
if (stat && !manifestMissing) return
|
|
16
|
+
const rel = path.relative(project.configDir, project.outputRoot)
|
|
17
|
+
const contained = rel !== '' && rel !== '..' && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel)
|
|
18
|
+
const folder = contained ? `projection output folder ${rel}` : 'the projection output folder'
|
|
19
|
+
const state = manifestMissing ? 'has no atelier.manifest.json' : 'does not exist'
|
|
20
|
+
throw new AtelierDiagnosticError('projection-output-missing', `${folder} ${state}; this project has not been built yet`, {
|
|
21
|
+
hint: 'Run atelier graph, then atelier build, with the same --project path, then retry.',
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
4
25
|
function parseServerArgs(argv = []) {
|
|
5
26
|
const args = { projectArgs: [] }
|
|
6
27
|
for (const arg of argv) {
|
|
@@ -30,11 +51,16 @@ function resolvePort(args, env = process.env) {
|
|
|
30
51
|
export async function runServerCommand(argv = process.argv.slice(2)) {
|
|
31
52
|
const args = parseServerArgs(argv)
|
|
32
53
|
const project = resolveProjectConfig({ argv: args.projectArgs })
|
|
54
|
+
// An unusable --port or PORT is refused before the project's build state,
|
|
55
|
+
// so a mistyped argument is named even in a project not built yet.
|
|
56
|
+
const port = args.smoke ? 0 : resolvePort(args)
|
|
57
|
+
assertProjectionBuilt(project)
|
|
33
58
|
const sidecar = createAtelierSidecarServer({
|
|
34
59
|
workspaceRoot: project.outputRoot,
|
|
35
60
|
stateDir: project.outputRoot,
|
|
36
61
|
reviewProject: argv.includes('--review') ? project : null,
|
|
37
|
-
|
|
62
|
+
knowledgeProject: argv.includes('--knowledge') ? project : null,
|
|
63
|
+
port,
|
|
38
64
|
})
|
|
39
65
|
|
|
40
66
|
if (args.smoke) {
|
|
@@ -53,5 +79,5 @@ export async function runServerCommand(argv = process.argv.slice(2)) {
|
|
|
53
79
|
}
|
|
54
80
|
|
|
55
81
|
const address = await sidecar.listen()
|
|
56
|
-
console.log(`MNSTRY Atelier listening on http://127.0.0.1:${address.port}
|
|
82
|
+
console.log(`MNSTRY Atelier listening on http://127.0.0.1:${address.port}/${argv.includes('--knowledge') ? 'knowledge' : ''}`)
|
|
57
83
|
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import { buildCanonicalGraph, createGraphFileCache } from '../graph/graph.mjs'
|
|
3
|
+
import { projectGraph, PROJECTION_TARGETS } from '../projection/policy.mjs'
|
|
4
|
+
import { renderReadOnlyDocument, validatePresentation } from '../ui/presentation/index.mjs'
|
|
5
|
+
import { templateReference, validateTemplateDefinition, validateTemplateBinding, validateTemplateHost } from '../templates/profile.mjs'
|
|
6
|
+
|
|
7
|
+
const packageVersion = JSON.parse(fs.readFileSync(new URL('../../package.json', import.meta.url), 'utf8')).version
|
|
8
|
+
const identity = value => templateReference('PayloadRef', 'identity', '1', value).digest.slice(7, 39)
|
|
9
|
+
const outcome = (ok, errors, rest = {}) => ({ ok, errors, authority: 'structural-only', executionAuthority: false, publicationAuthority: false, conformanceClaim: 'none', ...rest })
|
|
10
|
+
|
|
11
|
+
/** Read-only composition over a loaded project, not a graph or source writer.
|
|
12
|
+
* The request is bounded JSON text: {profile, projectRef, roleNodeIds, target,
|
|
13
|
+
* theme?}. This initial binding supports classified active Markdown resources,
|
|
14
|
+
* Resource bindings and CollectionView/StatusView. Other requirements
|
|
15
|
+
* refuse instead of being converted to a superficially similar surface.
|
|
16
|
+
*/
|
|
17
|
+
export function createTemplateProjectView(project, requestJson) {
|
|
18
|
+
try {
|
|
19
|
+
if (typeof requestJson !== 'string' || Buffer.byteLength(requestJson) > 1048576) return outcome(false, ['invalid request'])
|
|
20
|
+
const request = JSON.parse(requestJson)
|
|
21
|
+
// The profile reference validator also enforces bounded plain JSON before
|
|
22
|
+
// the request is used; no caller-supplied accessors enter this adapter.
|
|
23
|
+
templateReference('PayloadRef', 'request', '1', request)
|
|
24
|
+
if (!request || Array.isArray(request) || Object.keys(request).some(key => !['profile', 'projectRef', 'roleNodeIds', 'target', 'theme'].includes(key))) return outcome(false, ['invalid request'])
|
|
25
|
+
const { profile, projectRef, roleNodeIds, target, theme = 'light' } = request
|
|
26
|
+
const definition = validateTemplateDefinition(profile)
|
|
27
|
+
if (!definition.ok) return outcome(false, definition.errors)
|
|
28
|
+
if (!Object.hasOwn(PROJECTION_TARGETS, target)) return outcome(false, ['unknown projection target'])
|
|
29
|
+
if (!roleNodeIds || Array.isArray(roleNodeIds) || typeof roleNodeIds !== 'object') return outcome(false, ['invalid role selection'])
|
|
30
|
+
if (profile.runtimeProfileRef !== null) return outcome(false, ['runtime profile requires its owning adapter'])
|
|
31
|
+
const templateRef = templateReference('TemplateRef', profile.id, profile.version, profile)
|
|
32
|
+
const host = validateTemplateHost(profile, {
|
|
33
|
+
schema: 'atelier-template-host@v1', templateRef, carrier: 'web', atelierVersion: packageVersion,
|
|
34
|
+
packRefs: [], semanticPrimitives: ['Resource'], surfacePrimitives: ['CollectionView', 'StatusView'],
|
|
35
|
+
runtimeProfileRef: null, optionalDecisions: [],
|
|
36
|
+
})
|
|
37
|
+
if (!host.ok) return outcome(false, host.errors)
|
|
38
|
+
const cache = createGraphFileCache()
|
|
39
|
+
const graph = buildCanonicalGraph(project, { fileCache: cache })
|
|
40
|
+
if (!graph.ok) return outcome(false, ['canonical graph invalid'])
|
|
41
|
+
const projected = projectGraph(graph, { target })
|
|
42
|
+
const visible = new Map(projected.nodes.filter(node => node.classification === 'classified' && node.status === 'active').map(node => [node.id, node]))
|
|
43
|
+
const records = [], sources = new Map(), roles = []
|
|
44
|
+
for (const [roleRef, ids] of Object.entries(roleNodeIds)) {
|
|
45
|
+
if (!profile.semanticRoles.some(role => role.id === roleRef) || !Array.isArray(ids) || !ids.length || ids.length > 256 || ids.some(id => typeof id !== 'string') || new Set(ids).size !== ids.length) return outcome(false, ['invalid role selection'])
|
|
46
|
+
if (profile.semanticRoles.find(role => role.id === roleRef).primitive !== 'Resource') return outcome(false, ['semantic role has no canonical binding'])
|
|
47
|
+
const refs = []
|
|
48
|
+
for (const id of ids) {
|
|
49
|
+
const node = visible.get(id)
|
|
50
|
+
// A node's audience cannot widen its repository's read boundary.
|
|
51
|
+
if (!node || !PROJECTION_TARGETS[target].has(node.repoAccess?.readBoundary)) return outcome(false, ['selected source unavailable for this view'])
|
|
52
|
+
const source = cache.files.get(`${node.repo}\u0000${node.path}`)
|
|
53
|
+
if (!source || !/^[a-f0-9]{64}$/.test(source.digest)) return outcome(false, ['source byte pin unavailable'])
|
|
54
|
+
if (!sources.has(id)) {
|
|
55
|
+
const document = { nodeId: id, repository: node.repo, path: node.path, rawDigest: `sha256:${source.digest}`, title: node.title, summary: node.summary }
|
|
56
|
+
const ref = templateReference('SourceRef', `source.${identity(id)}`, document.rawDigest, document)
|
|
57
|
+
sources.set(id, { ref, document }); records.push({ ref, document })
|
|
58
|
+
}
|
|
59
|
+
refs.push(sources.get(id).ref)
|
|
60
|
+
}
|
|
61
|
+
roles.push({ roleRef, sourceRefs: refs })
|
|
62
|
+
}
|
|
63
|
+
const binding = { schema: 'atelier-template-binding@v1', id: `binding.${identity(projectRef)}`, version: '1.0.0', templateRef, projectRef, roles }
|
|
64
|
+
const checked = validateTemplateBinding(profile, binding, records)
|
|
65
|
+
if (!checked.ok) return outcome(false, checked.errors)
|
|
66
|
+
const bindingRef = templateReference('BindingRef', binding.id, binding.version, binding)
|
|
67
|
+
const nodes = profile.surfaceRoles.map((view, index) => {
|
|
68
|
+
const id = `view-${index}`
|
|
69
|
+
if (view.primitive === 'StatusView') return { id, type: 'status', label: view.id, text: 'Read-only local projection. Runtime actions and publication are unavailable.', tone: 'neutral', details: host.diagnostics.map(item => ({ label: item.id, value: item.kind === 'semantic-role' ? 'Optional role unsupported; binding unavailable' : `${item.status}; deterministic or manual fallback` })), actions: [] }
|
|
70
|
+
const sourceIds = [...new Set(view.semanticRoleRefs.flatMap(role => roleNodeIds[role] ?? []))]
|
|
71
|
+
return { id, type: 'collection', label: view.id, items: sourceIds.map(sourceId => ({ id: `item-${identity(sourceId)}`, label: sources.get(sourceId).document.title, detail: sources.get(sourceId).document.summary })) }
|
|
72
|
+
})
|
|
73
|
+
nodes.unshift({ id: 'description', type: 'text', label: 'Inputs and limits', text: `Inputs: ${profile.inputs.join('; ')}. Limits: ${profile.limitations.join('; ')}` })
|
|
74
|
+
nodes.push({ id: 'identity', type: 'text', label: 'Template and binding identity', text: `Template: ${templateRef.id}@${templateRef.version}; ${templateRef.digest}. Binding: ${bindingRef.id}@${bindingRef.version}; ${bindingRef.digest}. Release: none (local preview). Structural references only; not verified authorship, release acceptance or rights.` })
|
|
75
|
+
// Provenance travels with readable exports, not only the API envelope.
|
|
76
|
+
// Use opaque refs and byte pins; never expose local repository paths here.
|
|
77
|
+
for (let offset = 0; offset < records.length; offset += 128) nodes.push({
|
|
78
|
+
id: `provenance-${offset}`, type: 'collection', label: 'Source provenance',
|
|
79
|
+
items: records.slice(offset, offset + 128).map(({ ref, document }) => ({ id: `source-${identity(ref.id)}`, label: document.title, detail: `SourceRef: ${ref.id}; ${ref.digest}. Source bytes: ${document.rawDigest}.` })),
|
|
80
|
+
})
|
|
81
|
+
const model = { schema: 'atelier.presentation/v1', version: '1.0.0', id: `template-${identity(profile.id)}`, title: profile.purpose, lang: 'en', direction: 'ltr', theme, density: 'comfortable', navigation: [], panes: [{ id: 'content', label: 'Template preview', role: 'primary', blocks: nodes.map(node => node.id) }], nodes }
|
|
82
|
+
if (validatePresentation(model).length) return outcome(false, ['presentation binding invalid'])
|
|
83
|
+
// Reuse the exact same non-interactive renderer for local web and document
|
|
84
|
+
// preview. This creates bytes only; it does not publish or erase old copies.
|
|
85
|
+
const html = renderReadOnlyDocument(model)
|
|
86
|
+
const textPreview = [model.title, ...nodes.flatMap(node => [node.label, ...(node.items ? node.items.flatMap(item => [item.label, item.detail]) : [node.text]), ...(node.details ?? []).map(item => `${item.label}: ${item.value}`)])].join('\n\n')
|
|
87
|
+
return outcome(true, [], { templateRef, bindingRef, binding, records, model, html, textPreview, capabilityDiagnostics: host.diagnostics, optionalDecisions: host.diagnostics.filter(item => item.kind !== 'semantic-role'), target, lifecycle: 'active-only', artifacts: { web: html, documents: html } })
|
|
88
|
+
} catch {
|
|
89
|
+
// Do not disclose source paths or source text through exception messages.
|
|
90
|
+
return outcome(false, ['template project composition refused'])
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import { createHash } from 'node:crypto'
|
|
3
|
+
import { canonicalize } from '../attestation/jcs.mjs'
|
|
4
|
+
import { validateJsonSchema } from '../export/atelier-export-contract.mjs'
|
|
5
|
+
import { validateDecisionRequest, validateDecisionResult } from '../decisions/contracts.mjs'
|
|
6
|
+
|
|
7
|
+
const schema = JSON.parse(fs.readFileSync(new URL('../../contracts/atelier-template-profile.v1.schema.json', import.meta.url), 'utf8'))
|
|
8
|
+
const shapes = Object.fromEntries(Object.keys(schema.$defs).map(kind => [kind, {
|
|
9
|
+
$schema: schema.$schema, $defs: schema.$defs, $ref: `#/$defs/${kind}`,
|
|
10
|
+
}]))
|
|
11
|
+
const forbidden = new Set(['__proto__', 'prototype', 'constructor'])
|
|
12
|
+
const referenceKinds = new Set(Object.keys(schema.$defs).filter(kind => kind.endsWith('Ref')))
|
|
13
|
+
|
|
14
|
+
// Inspect descriptors before traversing untrusted values; no getters or toJSON.
|
|
15
|
+
// All public entry points snapshot once before relational or hash checks.
|
|
16
|
+
function snapshot(input) {
|
|
17
|
+
const ancestors = new Set()
|
|
18
|
+
let nodes = 0, bytes = 0
|
|
19
|
+
function copy(value, depth) {
|
|
20
|
+
if (++nodes > 20000 || depth > 24) throw new TypeError('template input limit')
|
|
21
|
+
if (value === null || typeof value === 'boolean' || typeof value === 'string' || typeof value === 'number' && Number.isFinite(value)) {
|
|
22
|
+
if (typeof value === 'string' && value.length > 1048576) throw new TypeError('template input limit')
|
|
23
|
+
bytes += Buffer.byteLength(JSON.stringify(value))
|
|
24
|
+
if (bytes > 1048576) throw new TypeError('template input limit')
|
|
25
|
+
return value
|
|
26
|
+
}
|
|
27
|
+
if (!value || typeof value !== 'object' || ancestors.has(value)) throw new TypeError('template input is not JSON')
|
|
28
|
+
const array = Array.isArray(value), proto = Object.getPrototypeOf(value)
|
|
29
|
+
if (array ? proto !== Array.prototype : proto !== Object.prototype && proto !== null) throw new TypeError('template input prototype')
|
|
30
|
+
const descriptors = Object.getOwnPropertyDescriptors(value), keys = Reflect.ownKeys(descriptors)
|
|
31
|
+
if (keys.some(key => typeof key !== 'string' || forbidden.has(key))) throw new TypeError('template input key')
|
|
32
|
+
ancestors.add(value)
|
|
33
|
+
const result = array ? [] : {}
|
|
34
|
+
if (array && (descriptors.length.value > 20000 || keys.length !== descriptors.length.value + 1)) throw new TypeError('template array shape')
|
|
35
|
+
for (const key of keys) {
|
|
36
|
+
if (array && key === 'length') continue
|
|
37
|
+
const d = descriptors[key]
|
|
38
|
+
if (!Object.hasOwn(d, 'value') || !d.enumerable || array && !/^(0|[1-9][0-9]*)$/.test(key)) throw new TypeError('template input descriptor')
|
|
39
|
+
bytes += Buffer.byteLength(key) + 4
|
|
40
|
+
if (bytes > 1048576) throw new TypeError('template input limit')
|
|
41
|
+
result[key] = copy(d.value, depth + 1)
|
|
42
|
+
}
|
|
43
|
+
if (array && result.length !== descriptors.length.value) throw new TypeError('template sparse array')
|
|
44
|
+
ancestors.delete(value)
|
|
45
|
+
return result
|
|
46
|
+
}
|
|
47
|
+
return copy(input, 0)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function shape(kind, value, errors) {
|
|
51
|
+
const invalid = validateJsonSchema(shapes[kind], value).length > 0
|
|
52
|
+
if (invalid) errors.push(`invalid ${kind} shape`)
|
|
53
|
+
return !invalid
|
|
54
|
+
}
|
|
55
|
+
function same(a, b) { return canonicalize(a) === canonicalize(b) }
|
|
56
|
+
function unique(items, key, errors, label) {
|
|
57
|
+
if (new Set(items.map(key)).size !== items.length) errors.push(`duplicate ${label}`)
|
|
58
|
+
}
|
|
59
|
+
function refFor(kind, id, version, document) {
|
|
60
|
+
if (!referenceKinds.has(kind)) throw new TypeError('unsupported reference kind')
|
|
61
|
+
const domain = { schema: 'atelier-template-digest@v1', kind, id, version, document }
|
|
62
|
+
const digest = `sha256:${createHash('sha256').update(canonicalize(domain)).digest('hex')}`
|
|
63
|
+
const ref = { kind, id, version, digest }
|
|
64
|
+
if (validateJsonSchema(shapes[kind], ref).length) throw new TypeError('invalid reference identity')
|
|
65
|
+
return ref
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Content identity only; neither signature verification nor owner acceptance. */
|
|
69
|
+
export function templateReference(kind, id, version, document) {
|
|
70
|
+
const value = snapshot({ kind, id, version, document })
|
|
71
|
+
return refFor(value.kind, value.id, value.version, value.document)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function run(stage, input, inspect) {
|
|
75
|
+
const errors = [], diagnostics = []
|
|
76
|
+
try { inspect(snapshot(input), errors, diagnostics) }
|
|
77
|
+
catch { errors.push('expected bounded plain JSON and supported reference identity') }
|
|
78
|
+
return { ok: errors.length === 0, stage, errors: [...new Set(errors)], diagnostics,
|
|
79
|
+
authority: 'structural-only', executionAuthority: false, publicationAuthority: false }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function definition(value, errors) {
|
|
83
|
+
if (!shape('definition', value, errors)) return false
|
|
84
|
+
unique(value.semanticRoles, x => x.id, errors, 'semantic role')
|
|
85
|
+
unique(value.surfaceRoles, x => x.id, errors, 'surface role')
|
|
86
|
+
unique(value.carriers, x => x.id, errors, 'carrier')
|
|
87
|
+
unique(value.extensions, x => x.namespace, errors, 'extension namespace')
|
|
88
|
+
unique(value.optionalDecisions, x => x.id, errors, 'optional decision')
|
|
89
|
+
unique(value.compatibility.packRefs, x => x.id, errors, 'pack identity')
|
|
90
|
+
const roles = new Set(value.semanticRoles.map(x => x.id))
|
|
91
|
+
for (const view of value.surfaceRoles) {
|
|
92
|
+
if (view.semanticRoleRefs.some(id => !roles.has(id))) errors.push('dangling surface semantic role')
|
|
93
|
+
}
|
|
94
|
+
// No extension handlers are registered by this additive profile. Optional
|
|
95
|
+
// opaque payloads remain inert. Required semantics cannot be silently ignored.
|
|
96
|
+
if (value.extensions.some(x => x.required)) errors.push('required extension unsupported')
|
|
97
|
+
return errors.length === 0
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function validateTemplateDefinition(input) {
|
|
101
|
+
return run('definition', input, (value, errors) => definition(value, errors))
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function inventory(records, errors) {
|
|
105
|
+
const entries = new Map(), identities = new Set()
|
|
106
|
+
if (!Array.isArray(records) || records.length > 256) {
|
|
107
|
+
errors.push('invalid reference inventory'); return entries
|
|
108
|
+
}
|
|
109
|
+
for (const record of records) {
|
|
110
|
+
if (!shape('record', record, errors)) continue
|
|
111
|
+
const { ref, document } = record
|
|
112
|
+
const key = canonicalize(ref), identity = canonicalize([ref.kind, ref.id, ref.version])
|
|
113
|
+
if (identities.has(identity)) errors.push('ambiguous reference inventory')
|
|
114
|
+
identities.add(identity)
|
|
115
|
+
if (!same(ref, refFor(ref.kind, ref.id, ref.version, document))) errors.push('reference digest mismatch')
|
|
116
|
+
entries.set(key, document)
|
|
117
|
+
}
|
|
118
|
+
return entries
|
|
119
|
+
}
|
|
120
|
+
function resolve(ref, entries, errors) {
|
|
121
|
+
const key = canonicalize(ref)
|
|
122
|
+
if (!entries.has(key)) errors.push('dangling exact reference')
|
|
123
|
+
return entries.get(key)
|
|
124
|
+
}
|
|
125
|
+
function extensions(value, entries, errors) {
|
|
126
|
+
for (const entry of value.extensions) {
|
|
127
|
+
resolve(entry.schemaRef, entries, errors)
|
|
128
|
+
resolve(entry.payloadRef, entries, errors)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function binding(profile, value, entries, errors) {
|
|
132
|
+
if (!shape('binding', value, errors)) return false
|
|
133
|
+
if (!same(value.templateRef, refFor('TemplateRef', profile.id, profile.version, profile))) errors.push('template binding mismatch')
|
|
134
|
+
unique(value.roles, x => x.roleRef, errors, 'role binding')
|
|
135
|
+
const roles = new Map(profile.semanticRoles.map(x => [x.id, x]))
|
|
136
|
+
for (const entry of value.roles) {
|
|
137
|
+
const role = roles.get(entry.roleRef)
|
|
138
|
+
if (!role) errors.push('unknown semantic role')
|
|
139
|
+
if (role?.cardinality === 'one' && entry.sourceRefs.length !== 1) errors.push('plural singular role binding')
|
|
140
|
+
for (const ref of entry.sourceRefs) resolve(ref, entries, errors)
|
|
141
|
+
}
|
|
142
|
+
for (const role of profile.semanticRoles) {
|
|
143
|
+
if (role.required && !value.roles.some(x => x.roleRef === role.id)) errors.push('required semantic role unbound')
|
|
144
|
+
}
|
|
145
|
+
extensions(profile, entries, errors)
|
|
146
|
+
return errors.length === 0
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function validateTemplateBinding(profile, projectBinding, records = []) {
|
|
150
|
+
return run('binding', { profile, projectBinding, records }, (value, errors) => {
|
|
151
|
+
if (!definition(value.profile, errors)) return
|
|
152
|
+
binding(value.profile, value.projectBinding, inventory(value.records, errors), errors)
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function validateTemplateRelease(profile, projectBinding, release, records = []) {
|
|
157
|
+
return run('release', { profile, projectBinding, release, records }, (value, errors) => {
|
|
158
|
+
if (!definition(value.profile, errors) || !shape('release', value.release, errors)) return
|
|
159
|
+
const entries = inventory(value.records, errors)
|
|
160
|
+
if (!binding(value.profile, value.projectBinding, entries, errors)) return
|
|
161
|
+
const templateRef = refFor('TemplateRef', value.profile.id, value.profile.version, value.profile)
|
|
162
|
+
const bindingRef = refFor('BindingRef', value.projectBinding.id, value.projectBinding.version, value.projectBinding)
|
|
163
|
+
if (!same(value.release.templateRef, templateRef) || !same(value.release.bindingRef, bindingRef)) errors.push('release binding mismatch')
|
|
164
|
+
const carriers = new Set(), sources = new Set(value.projectBinding.roles.flatMap(x => x.sourceRefs.map(canonicalize)))
|
|
165
|
+
for (const ref of value.release.projectionRefs) {
|
|
166
|
+
const projection = resolve(ref, entries, errors)
|
|
167
|
+
if (!shape('projection', projection, errors)) continue
|
|
168
|
+
if (projection.id !== ref.id || projection.version !== ref.version) errors.push('projection identity mismatch')
|
|
169
|
+
if (!same(projection.templateRef, templateRef) || !same(projection.bindingRef, bindingRef)) errors.push('projection binding mismatch')
|
|
170
|
+
if (carriers.has(projection.carrier)) errors.push('duplicate projection carrier')
|
|
171
|
+
carriers.add(projection.carrier)
|
|
172
|
+
if (!value.profile.carriers.some(x => x.id === projection.carrier)) errors.push('undeclared projection carrier')
|
|
173
|
+
for (const source of projection.sourceRefs) {
|
|
174
|
+
if (!sources.has(canonicalize(source))) errors.push('projection source outside project binding')
|
|
175
|
+
resolve(source, entries, errors)
|
|
176
|
+
}
|
|
177
|
+
for (const item of [...projection.policyRefs, ...projection.authorityDecisionRefs, projection.payloadRef, projection.capabilityRef]) resolve(item, entries, errors)
|
|
178
|
+
}
|
|
179
|
+
for (const carrier of value.profile.carriers) {
|
|
180
|
+
if (carrier.required && !carriers.has(carrier.id)) errors.push('required carrier missing')
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Host declarations are structural observations, never runtime grants. */
|
|
186
|
+
export function validateTemplateHost(profile, host) {
|
|
187
|
+
return run('host', { profile, host }, (value, errors, diagnostics) => {
|
|
188
|
+
if (!definition(value.profile, errors) || !shape('host', value.host, errors)) return
|
|
189
|
+
const { profile: p, host: h } = value
|
|
190
|
+
if (!same(h.templateRef, refFor('TemplateRef', p.id, p.version, p))) errors.push('host template mismatch')
|
|
191
|
+
if (!p.carriers.some(x => x.id === h.carrier)) errors.push('undeclared host carrier')
|
|
192
|
+
if (!p.compatibility.atelierVersions.includes(h.atelierVersion)) errors.push('incompatible host version')
|
|
193
|
+
unique(h.packRefs, x => x.id, errors, 'host pack')
|
|
194
|
+
for (const pack of p.compatibility.packRefs) {
|
|
195
|
+
if (!h.packRefs.some(x => same(x, pack))) errors.push('required exact pack unavailable')
|
|
196
|
+
}
|
|
197
|
+
for (const role of p.semanticRoles) if (!h.semanticPrimitives.includes(role.primitive)) {
|
|
198
|
+
if (role.required) errors.push('unsupported semantic primitive')
|
|
199
|
+
else diagnostics.push({ kind: 'semantic-role', id: role.id, status: 'unsupported', bindingAllowed: false })
|
|
200
|
+
}
|
|
201
|
+
for (const role of p.surfaceRoles) if (!h.surfacePrimitives.includes(role.primitive)) errors.push('unsupported surface primitive')
|
|
202
|
+
if (!same(p.runtimeProfileRef, h.runtimeProfileRef)) errors.push('runtime profile missing or mismatched')
|
|
203
|
+
unique(h.optionalDecisions, x => x.id, errors, 'host decision')
|
|
204
|
+
for (const observation of h.optionalDecisions) {
|
|
205
|
+
if (!p.optionalDecisions.some(x => x.id === observation.id)) errors.push('undeclared optional decision')
|
|
206
|
+
if (observation.status === 'available' && observation.capabilityRef === null) errors.push('available decision needs exact capability reference')
|
|
207
|
+
if (observation.status === 'absent' && observation.capabilityRef !== null) errors.push('absent decision cannot claim a capability reference')
|
|
208
|
+
}
|
|
209
|
+
for (const request of p.optionalDecisions) {
|
|
210
|
+
const status = h.optionalDecisions.find(x => x.id === request.id)?.status ?? 'absent'
|
|
211
|
+
diagnostics.push({ id: request.id, status, fallback: 'deterministic-or-manual', invocationAuthorized: false })
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Reuse provider-neutral decision semantics; never select or invoke a provider. */
|
|
217
|
+
export function validateTemplateDecision(profile, decisionId, request, result) {
|
|
218
|
+
return run('advisory-decision', { profile, decisionId, request, result }, (value, errors) => {
|
|
219
|
+
if (!definition(value.profile, errors)) return
|
|
220
|
+
if (!value.profile.optionalDecisions.some(x => x.id === value.decisionId)) errors.push('undeclared optional decision')
|
|
221
|
+
if (value.request?.task !== value.decisionId) errors.push('decision task binding mismatch')
|
|
222
|
+
if (!validateDecisionRequest(value.request).ok) errors.push('invalid decision request')
|
|
223
|
+
else if (!validateDecisionResult(value.request, value.result).ok) errors.push('invalid decision result')
|
|
224
|
+
})
|
|
225
|
+
}
|