@mnstry/atelier 0.2.0-alpha.6 → 0.2.0-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +176 -0
- package/README.md +59 -15
- package/SECURITY.md +6 -1
- package/contracts/atelier-adoption-policy.v1.schema.json +49 -0
- package/contracts/atelier-coauthor.v1.schema.json +163 -0
- package/contracts/atelier-decision-request.v1.schema.json +476 -0
- package/contracts/atelier-decision-result.v1.schema.json +500 -0
- package/contracts/atelier-guide.v1.schema.json +227 -0
- package/contracts/atelier-intake.v1.schema.json +151 -0
- package/contracts/atelier-migration.v2.schema.json +61 -0
- package/contracts/atelier-obsidian-acceptance-receipt.v1.schema.json +211 -0
- package/contracts/atelier-obsidian-apply-policy.v1.schema.json +298 -0
- package/contracts/atelier-obsidian-corpus-profile.v1.schema.json +98 -0
- package/contracts/atelier-obsidian-edit-operation.v1.schema.json +138 -0
- package/contracts/atelier-obsidian-ext-settings.v1.schema.json +287 -0
- package/contracts/atelier-obsidian-generation-manifest.v1.schema.json +353 -0
- package/contracts/atelier-obsidian-proposal-receipt.v1.schema.json +110 -0
- package/contracts/atelier-obsidian-publication-journal.v1.schema.json +167 -0
- package/contracts/atelier-obsidian-scope.v1.schema.json +264 -0
- package/contracts/atelier-obsidian-service-state.v1.schema.json +149 -0
- package/contracts/atelier-obsidian-source-snapshot.v1.schema.json +179 -0
- package/contracts/atelier-pane-presentation.v1.schema.json +76 -0
- package/contracts/atelier-presentation.v1.schema.json +1293 -0
- package/contracts/atelier-review.v1.schema.json +1601 -0
- package/contracts/atelier-skill-steward.v1.schema.json +692 -0
- package/contracts/atelier-upgrade-plan.v2.schema.json +398 -0
- package/contracts/atelier-upgrade-receipt.v1.schema.json +99 -0
- package/contracts/atelier-vault-publication.v1.schema.json +58 -0
- package/docs/assurance-controls.md +9 -1
- package/docs/atelier-sync.md +5 -4
- package/docs/blocks/claims.md +15 -10
- package/docs/blocks/will-not-do.md +4 -1
- package/docs/coauthor-session.md +57 -0
- package/docs/connected-composition.md +75 -0
- package/docs/decisions.md +167 -0
- package/docs/exact-upgrades.md +159 -0
- package/docs/extension-pack-lifecycle.md +33 -0
- package/docs/guided-upgrades.md +99 -0
- package/docs/install.md +65 -9
- package/docs/intake-and-guides.md +65 -0
- package/docs/integration-and-review-plan.md +409 -0
- package/docs/integration-contract-decisions.md +22 -0
- package/docs/local-review.md +84 -0
- package/docs/local-services.md +227 -0
- package/docs/local-state.md +107 -0
- package/docs/obsidian-contract.md +687 -0
- package/docs/obsidian.md +387 -0
- package/docs/portable-presentation.md +328 -0
- package/docs/presentation-consumer-boundaries.md +75 -0
- package/docs/presentation-review-disposition.md +47 -0
- package/docs/project-command-inventory.md +38 -0
- package/docs/project-options.md +53 -0
- package/docs/release-engineering.md +88 -0
- package/docs/review-portability.md +55 -0
- package/docs/skill-steward.md +156 -0
- package/docs/upgrade.md +55 -2
- package/docs/vault-service.md +292 -0
- package/fixtures/atelier-coauthor/invalid/authority.v1.json +5 -0
- package/fixtures/atelier-coauthor/valid/config.v1.json +12 -0
- package/fixtures/atelier-guide/invalid/authority.v1.json +11 -0
- package/fixtures/atelier-guide/valid/capability.v1.json +10 -0
- package/fixtures/atelier-guide/valid/consent.v1.json +11 -0
- package/fixtures/atelier-guide/valid/engagement.v1.json +7 -0
- package/fixtures/atelier-guide/valid/offer.v1.json +10 -0
- package/fixtures/atelier-intake/invalid/authority.v1.json +9 -0
- package/fixtures/atelier-intake/valid/attempt.v1.json +8 -0
- package/fixtures/atelier-intake/valid/completion.v1.json +9 -0
- package/fixtures/atelier-intake/valid/source.v1.json +7 -0
- package/fixtures/atelier-review/invalid/authenticated-identity.v1.json +22 -0
- package/fixtures/atelier-review/valid/bound.v1.json +229 -0
- package/fixtures/atelier-review/valid/bundle.v1.json +267 -0
- package/fixtures/atelier-review/valid/contribution.v1.json +22 -0
- package/fixtures/atelier-review/valid/lifecycle.v1.json +14 -0
- package/fixtures/atelier-skill-steward/audit/invalid/source-mutation.v1.json +26 -0
- package/fixtures/atelier-skill-steward/audit/valid/clean.v1.json +26 -0
- package/fixtures/atelier-skill-steward/candidates/invalid/workflow-key.v1.json +37 -0
- package/fixtures/atelier-skill-steward/candidates/valid/create.v1.json +37 -0
- package/fixtures/atelier-skill-steward/lock/invalid/bad-digest.v1.json +18 -0
- package/fixtures/atelier-skill-steward/lock/valid/installed.v1.json +23 -0
- package/fixtures/atelier-skill-steward/plan/invalid/delete-action.v1.json +33 -0
- package/fixtures/atelier-skill-steward/plan/valid/add.v1.json +33 -0
- package/fixtures/atelier-upgrade-transaction/adoption-policy/invalid/example.json +12 -0
- package/fixtures/atelier-upgrade-transaction/adoption-policy/valid/example.json +11 -0
- package/fixtures/atelier-upgrade-transaction/migration/invalid/example.json +20 -0
- package/fixtures/atelier-upgrade-transaction/migration/valid/example.json +19 -0
- package/fixtures/atelier-upgrade-transaction/upgrade-plan/invalid/example.json +58 -0
- package/fixtures/atelier-upgrade-transaction/upgrade-plan/valid/example.json +57 -0
- package/fixtures/atelier-upgrade-transaction/upgrade-receipt/invalid/example.json +15 -0
- package/fixtures/atelier-upgrade-transaction/upgrade-receipt/valid/example.json +14 -0
- package/fixtures/atelier-vault-publication/invalid/publication.json +5 -0
- package/fixtures/atelier-vault-publication/valid/publication.json +11 -0
- package/fixtures/decisions/request/invalid/unknown-authority.v1.json +61 -0
- package/fixtures/decisions/request/valid/triage.v1.json +60 -0
- package/fixtures/decisions/result/invalid/execution-authority.v1.json +50 -0
- package/fixtures/decisions/result/valid/abstained.v1.json +23 -0
- package/fixtures/decisions/result/valid/assessed.v1.json +50 -0
- package/fixtures/obsidian/acceptance/ap05-extra-notes.json +14 -0
- package/fixtures/obsidian/acceptance/receipts/G07.valid.v1.json +63 -0
- package/fixtures/obsidian/acceptance/receipts/G13.valid.v1.json +69 -0
- package/fixtures/obsidian/acceptance/receipts/G14.valid.v1.json +62 -0
- package/fixtures/obsidian/acceptance/receipts/G15.valid.v1.json +56 -0
- package/fixtures/obsidian/acceptance/receipts/G16.valid.v1.json +73 -0
- package/fixtures/obsidian/acceptance/receipts/G17.valid.v1.json +75 -0
- package/fixtures/obsidian/acceptance/receipts/G18.valid.v1.json +64 -0
- package/fixtures/obsidian/acceptance/service-sentinels.json +10 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/duplicate-evidence-name.v1.json +39 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/evidence-by-absolute-path.v1.json +34 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/missing-candidate-tree.v1.json +33 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/no-evidence.v1.json +28 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/unknown-outcome.v1.json +34 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/unknown-top-level-field.v1.json +35 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/valid/node-gate.v1.json +34 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/automatic-without-edit-class.v1.json +27 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/conflict-overwrite.v1.json +29 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/missing-policy-digest.v1.json +28 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/unbounded-batch.v1.json +29 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/unimplemented-edit-class.v1.json +30 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/unknown-mode.v1.json +29 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/unknown-selector-operator.v1.json +22 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/unknown-top-level-field.v1.json +30 -0
- package/fixtures/obsidian/contracts/apply-policy/valid/automatic-scoped.v1.json +29 -0
- package/fixtures/obsidian/contracts/apply-policy/valid/manual-default.v1.json +20 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/absolute-path-in-ext.v1.json +30 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/absolute-repository-root.v1.json +27 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/duplicate-repository-id.v1.json +27 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/overlapping-managed-roots.v1.json +27 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/unknown-enrollment.v1.json +27 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/unknown-top-level-field.v1.json +28 -0
- package/fixtures/obsidian/contracts/corpus-profile/valid/three-repositories.v1.json +27 -0
- package/fixtures/obsidian/contracts/edit-operation/invalid/absolute-recovery-ref.v1.json +21 -0
- package/fixtures/obsidian/contracts/edit-operation/invalid/inline-observed-bytes.v1.json +22 -0
- package/fixtures/obsidian/contracts/edit-operation/invalid/missing-base-digest.v1.json +20 -0
- package/fixtures/obsidian/contracts/edit-operation/invalid/short-idempotency-key.v1.json +21 -0
- package/fixtures/obsidian/contracts/edit-operation/invalid/unknown-kind.v1.json +21 -0
- package/fixtures/obsidian/contracts/edit-operation/valid/pending-body-replacement.v1.json +21 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/absolute-path-prefix.v1.json +28 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/apply-policy-in-portable-settings.v1.json +30 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/duplicate-scope-id.v1.json +27 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/machine-local-vault-path.v1.json +28 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/service-port-in-portable-settings.v1.json +28 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/unknown-default-scope.v1.json +27 -0
- package/fixtures/obsidian/contracts/ext-settings/valid/disabled.v1.json +5 -0
- package/fixtures/obsidian/contracts/ext-settings/valid/enabled-with-scopes.v1.json +27 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/absolute-note-path.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/complete-with-unwritten-notes.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/derived-link-claimed-declared.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/duplicate-note-identity.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/in-scope-endpoint-missing.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/title-only-note-path.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/unknown-note-field.v1.json +115 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/unknown-relation-type.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/withheld-endpoint.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/valid/two-notes.v1.json +114 -0
- package/fixtures/obsidian/contracts/oracles/scope-cases.json +412 -0
- package/fixtures/obsidian/contracts/proposal-receipt/invalid/accepted-without-proposal-id.v1.json +12 -0
- package/fixtures/obsidian/contracts/proposal-receipt/invalid/missing-adapter-operation-id.v1.json +11 -0
- package/fixtures/obsidian/contracts/proposal-receipt/invalid/unknown-backpressure-outcome.v1.json +12 -0
- package/fixtures/obsidian/contracts/proposal-receipt/invalid/unknown-top-level-field.v1.json +13 -0
- package/fixtures/obsidian/contracts/proposal-receipt/valid/accepted-new.v1.json +12 -0
- package/fixtures/obsidian/contracts/proposal-receipt/valid/deferred.v1.json +12 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/absolute-recovery-ref.v1.json +47 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/duplicate-sequence.v1.json +47 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/empty-protocol-id.v1.json +47 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/missing-protocol-id.v1.json +46 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/unknown-entry-field.v1.json +48 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/unknown-state.v1.json +47 -0
- package/fixtures/obsidian/contracts/publication-journal/valid/committed.v1.json +47 -0
- package/fixtures/obsidian/contracts/publication-journal/valid/updating-after-restart.v1.json +22 -0
- package/fixtures/obsidian/contracts/scope/invalid/absolute-path-prefix.v1.json +15 -0
- package/fixtures/obsidian/contracts/scope/invalid/duplicate-ids.v1.json +18 -0
- package/fixtures/obsidian/contracts/scope/invalid/expansion-without-node-budget.v1.json +40 -0
- package/fixtures/obsidian/contracts/scope/invalid/full-mode-with-subset.v1.json +8 -0
- package/fixtures/obsidian/contracts/scope/invalid/unknown-mode.v1.json +41 -0
- package/fixtures/obsidian/contracts/scope/invalid/unknown-selector-key.v1.json +15 -0
- package/fixtures/obsidian/contracts/scope/invalid/unknown-selector-operator.v1.json +14 -0
- package/fixtures/obsidian/contracts/scope/invalid/unknown-top-level-field.v1.json +42 -0
- package/fixtures/obsidian/contracts/scope/valid/explicit-empty.v1.json +8 -0
- package/fixtures/obsidian/contracts/scope/valid/focus.v1.json +11 -0
- package/fixtures/obsidian/contracts/scope/valid/full.v1.json +8 -0
- package/fixtures/obsidian/contracts/scope/valid/scoped-set-expression.v1.json +41 -0
- package/fixtures/obsidian/contracts/service-state/invalid/hostname-instead-of-literal-loopback.v1.json +23 -0
- package/fixtures/obsidian/contracts/service-state/invalid/missing-consent.v1.json +18 -0
- package/fixtures/obsidian/contracts/service-state/invalid/privileged-port.v1.json +23 -0
- package/fixtures/obsidian/contracts/service-state/invalid/unknown-top-level-field.v1.json +24 -0
- package/fixtures/obsidian/contracts/service-state/invalid/wildcard-bind.v1.json +23 -0
- package/fixtures/obsidian/contracts/service-state/valid/healthy.v1.json +23 -0
- package/fixtures/obsidian/contracts/source-snapshot/invalid/absolute-file-path.v1.json +48 -0
- package/fixtures/obsidian/contracts/source-snapshot/invalid/duplicate-repository-id.v1.json +48 -0
- package/fixtures/obsidian/contracts/source-snapshot/invalid/mixed-read.v1.json +48 -0
- package/fixtures/obsidian/contracts/source-snapshot/invalid/parent-traversal-path.v1.json +48 -0
- package/fixtures/obsidian/contracts/source-snapshot/invalid/unknown-file-field.v1.json +49 -0
- package/fixtures/obsidian/contracts/source-snapshot/valid/two-repositories.v1.json +48 -0
- package/fixtures/obsidian/edits/workspace.json +65 -0
- package/fixtures/obsidian/materialization/expected-full.json +99 -0
- package/fixtures/obsidian/materialization/expected-scoped.json +39 -0
- package/fixtures/obsidian/materialization/workspace.json +54 -0
- package/fixtures/obsidian/proposals/expected-shapes.json +11 -0
- package/fixtures/ui/presentation/pane.v1.json +7 -0
- package/fixtures/ui/presentation/reference.v1.json +293 -0
- package/package.json +57 -6
- package/skills/claude/atelier-guided-coauthor/SKILL.md +49 -0
- package/skills/claude/atelier-guided-upgrade/SKILL.md +97 -0
- package/skills/claude/atelier-open/SKILL.md +5 -0
- package/skills/claude/atelier-skill-steward/SKILL.md +62 -0
- package/skills/claude/mnstry-readiness/SKILL.md +5 -0
- package/skills/codex/atelier-guided-coauthor/SKILL.md +49 -0
- package/skills/codex/atelier-guided-upgrade/SKILL.md +97 -0
- package/skills/codex/atelier-open/SKILL.md +5 -0
- package/skills/codex/atelier-skill-steward/SKILL.md +62 -0
- package/skills/codex/mnstry-readiness/SKILL.md +5 -0
- package/src/access/preview-enforcer.mjs +84 -0
- package/src/boundary/policy.mjs +57 -25
- package/src/cli/project-options.mjs +58 -0
- package/src/cli/run.mjs +43 -3
- package/src/coauthor/session.mjs +161 -0
- package/src/coauthor/store.mjs +173 -0
- package/src/collaboration/event-ledger.mjs +16 -33
- package/src/collaboration/inspection-bundle.mjs +223 -0
- package/src/collaboration/review-contracts.mjs +25 -0
- package/src/collaboration/review-store.mjs +305 -0
- package/src/commands/coauthor.mjs +26 -0
- package/src/commands/config.mjs +6 -1
- package/src/commands/extension-pack.mjs +15 -8
- package/src/commands/init.mjs +44 -5
- package/src/commands/obsidian.mjs +368 -0
- package/src/commands/review.mjs +147 -0
- package/src/commands/setup.mjs +51 -14
- package/src/commands/skills.mjs +199 -0
- package/src/commands/sync.mjs +8 -1
- package/src/composition/registry.mjs +27 -0
- package/src/composition/wire.mjs +122 -0
- package/src/contracts/corpus.mjs +29 -0
- package/src/decisions/contracts.d.mts +96 -0
- package/src/decisions/contracts.mjs +321 -0
- package/src/disclosure/content-scan.mjs +13 -0
- package/src/export/atelier-export-contract.mjs +16 -1
- package/src/extension-packs/lifecycle.mjs +154 -0
- package/src/graph/graph.mjs +64 -2
- package/src/graph/knowledge-graph.mjs +548 -24
- package/src/guides/contracts.mjs +54 -0
- package/src/intake/store.mjs +119 -0
- package/src/preview/channel.mjs +89 -0
- package/src/project/config.mjs +26 -27
- package/src/project/durable-state.mjs +161 -0
- package/src/project/file-class.mjs +166 -0
- package/src/project/private-state.mjs +11 -1
- package/src/projection/obsidian/contracts.mjs +571 -0
- package/src/projection/obsidian/edits/align.mjs +169 -0
- package/src/projection/obsidian/edits/apply.mjs +889 -0
- package/src/projection/obsidian/edits/arbitrate.mjs +414 -0
- package/src/projection/obsidian/edits/contribution.mjs +108 -0
- package/src/projection/obsidian/edits/index.mjs +19 -0
- package/src/projection/obsidian/edits/object-identity.mjs +61 -0
- package/src/projection/obsidian/edits/object-store.mjs +517 -0
- package/src/projection/obsidian/edits/observe.mjs +123 -0
- package/src/projection/obsidian/edits/policy.mjs +123 -0
- package/src/projection/obsidian/edits/regions.mjs +648 -0
- package/src/projection/obsidian/materialize/byte-lens.mjs +98 -0
- package/src/projection/obsidian/materialize/index.mjs +7 -0
- package/src/projection/obsidian/materialize/path-registry.mjs +105 -0
- package/src/projection/obsidian/materialize/prepare-view.mjs +746 -0
- package/src/projection/obsidian/materialize/settings.mjs +57 -0
- package/src/projection/obsidian/materialize/stage.mjs +27 -0
- package/src/projection/obsidian/proposals/adapter.mjs +572 -0
- package/src/projection/obsidian/proposals/backpressure.mjs +113 -0
- package/src/projection/obsidian/proposals/contribution.mjs +55 -0
- package/src/projection/obsidian/proposals/index.mjs +23 -0
- package/src/projection/obsidian/proposals/observation.mjs +216 -0
- package/src/projection/obsidian/proposals/queue.mjs +277 -0
- package/src/projection/obsidian/proposals/router.mjs +168 -0
- package/src/projection/obsidian/publication/bridge-script.mjs +266 -0
- package/src/projection/obsidian/publication/exchange.mjs +117 -0
- package/src/projection/obsidian/publication/index.mjs +5 -0
- package/src/projection/obsidian/publication/publisher.mjs +588 -0
- package/src/projection/obsidian/publication/test-seam.mjs +16 -0
- package/src/projection/obsidian/publication/transport.mjs +139 -0
- package/src/projection/obsidian/recovery/index.mjs +6 -0
- package/src/projection/obsidian/recovery/journal.mjs +134 -0
- package/src/projection/obsidian/recovery/late-writer.mjs +46 -0
- package/src/projection/obsidian/recovery/restart.mjs +316 -0
- package/src/projection/obsidian/recovery/store.mjs +234 -0
- package/src/projection/obsidian/selection-ui/conflict-view.mjs +91 -0
- package/src/projection/obsidian/selection-ui/contribution.mjs +141 -0
- package/src/projection/obsidian/selection-ui/focus.mjs +82 -0
- package/src/projection/obsidian/selection-ui/index.mjs +12 -0
- package/src/projection/obsidian/selection-ui/policy-setup.mjs +107 -0
- package/src/projection/obsidian/selection-ui/receipt.mjs +173 -0
- package/src/projection/obsidian/selection-ui/selection-state.mjs +122 -0
- package/src/projection/obsidian/selection-ui/selection.mjs +109 -0
- package/src/projection/project.mjs +28 -19
- package/src/readiness-protocols/evidence.mjs +282 -0
- package/src/readiness-protocols/runtime.mjs +4 -3
- package/src/readiness-protocols/source-read.mjs +39 -0
- package/src/runtime/obsidian/app-capability.mjs +110 -0
- package/src/runtime/obsidian/app-production-seams.mjs +96 -0
- package/src/runtime/obsidian/contributions/proposal-adapter.mjs +4 -0
- package/src/runtime/obsidian/contributions/selection-ui.mjs +4 -0
- package/src/runtime/obsidian/contributions/source-apply.mjs +4 -0
- package/src/runtime/obsidian/contributions.mjs +27 -0
- package/src/runtime/obsidian/documents.mjs +37 -0
- package/src/runtime/obsidian/enablement.mjs +53 -0
- package/src/runtime/obsidian/engine.mjs +532 -0
- package/src/runtime/obsidian/errors.mjs +14 -0
- package/src/runtime/obsidian/extension-points.mjs +103 -0
- package/src/runtime/obsidian/index.mjs +34 -0
- package/src/runtime/obsidian/lifecycle.mjs +258 -0
- package/src/runtime/obsidian/machine-settings.mjs +260 -0
- package/src/runtime/obsidian/observation.mjs +158 -0
- package/src/runtime/obsidian/opening.mjs +243 -0
- package/src/runtime/obsidian/pending-edits.mjs +141 -0
- package/src/runtime/obsidian/pipeline.mjs +122 -0
- package/src/runtime/obsidian/private-lock.mjs +156 -0
- package/src/runtime/obsidian/process-identity.mjs +58 -0
- package/src/runtime/obsidian/service-client.mjs +66 -0
- package/src/runtime/obsidian/service-main.mjs +89 -0
- package/src/runtime/obsidian/service-record.mjs +169 -0
- package/src/runtime/obsidian/service-server.mjs +121 -0
- package/src/runtime/obsidian/service.mjs +216 -0
- package/src/runtime/obsidian/startup-adapters.mjs +87 -0
- package/src/runtime/obsidian/state-store.mjs +180 -0
- package/src/runtime/obsidian/sync-notice.mjs +33 -0
- package/src/runtime/obsidian/tick-loop.mjs +84 -0
- package/src/runtime/obsidian/watchers.mjs +36 -0
- package/src/runtime/supervisor.mjs +16 -1
- package/src/server/local-sidecar.mjs +44 -0
- package/src/server/server.mjs +1 -0
- package/src/skills/steward.mjs +780 -0
- package/src/ui/presentation/browser.mjs +278 -0
- package/src/ui/presentation/contract.mjs +116 -0
- package/src/ui/presentation/index.mjs +7 -0
- package/src/ui/presentation/native.mjs +153 -0
- package/src/ui/presentation/proof.mjs +57 -0
- package/src/ui/presentation/schema-check.mjs +30 -0
- package/src/ui/presentation/schema.generated.mjs +1371 -0
- package/src/ui/presentation/state.mjs +43 -0
- package/src/ui/presentation/styles.mjs +67 -0
- package/src/ui/presentation/tokens.mjs +74 -0
- package/src/ui/presentation/web.mjs +85 -0
- package/src/ui/review-page.mjs +32 -0
- package/src/upgrade/explanation.mjs +33 -0
- package/src/upgrade/provenance.mjs +338 -0
- package/src/upgrade/transaction-files.mjs +103 -0
- package/src/upgrade/transaction.mjs +475 -0
- package/src/upgrade/upgrade.mjs +63 -28
- package/src/vault/hosts.mjs +21 -0
- package/src/vault/identity.mjs +19 -0
- package/src/vault/index.mjs +7 -0
- package/src/vault/interface.mjs +11 -0
- package/src/vault/metadata.mjs +31 -0
- package/src/vault/privacy.mjs +148 -0
- package/src/vault/probe.mjs +89 -0
- package/src/vault/service.mjs +131 -0
- package/src/vault/source.mjs +45 -0
- package/src/vault/storage.mjs +23 -0
- package/templates/distribution-workspace/.claude/launch.json +6 -2
- package/templates/distribution-workspace/gitignore +1 -0
- package/templates/distribution-workspace/project/README.md +2 -2
- package/templates/external-project-workspace/README.md +45 -0
- package/templates/external-project-workspace/answers.example.json +4 -0
- package/templates/external-project-workspace/atelier.project.json +40 -0
- package/templates/external-project-workspace/boundary-policy.v1.json +60 -0
- package/templates/external-project-workspace/gitignore +8 -0
- package/templates/external-project-workspace/pack-lifecycle.json +14 -0
- package/templates/external-project-workspace/packs/protocols/contract-gate.v1.json +109 -0
- package/templates/external-project-workspace/packs/sample-pack.v1.json +48 -0
- package/templates/external-project-workspace/repo-access.v1.json +9 -0
- package/templates/external-project-workspace/source/README.md +11 -0
- package/templates/launch.json +6 -2
- package/templates/private-domain-workspace/.claude/launch.json +6 -2
- package/templates/private-domain-workspace/domain/README.md +2 -2
- package/templates/private-domain-workspace/gitignore +1 -0
- package/templates/shared-project-workspace/.claude/launch.json +6 -2
- package/templates/shared-project-workspace/gitignore +1 -0
- package/templates/shared-project-workspace/project/README.md +2 -2
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
applySkillSync,
|
|
7
|
+
auditSkillCatalog,
|
|
8
|
+
buildSkillCandidates,
|
|
9
|
+
planSkillSync,
|
|
10
|
+
recordSkillObservation,
|
|
11
|
+
} from '../skills/steward.mjs'
|
|
12
|
+
|
|
13
|
+
const USAGE = `Usage: atelier skills <subcommand>
|
|
14
|
+
|
|
15
|
+
Subcommands:
|
|
16
|
+
audit [--root DIR] [--peer DIR] [--json]
|
|
17
|
+
Audit required metadata, bundle safety, relative resources, and peer parity.
|
|
18
|
+
With no roots, audits the bundled Codex and Claude skill surfaces.
|
|
19
|
+
|
|
20
|
+
observe --workflow KEY --signal SIGNAL [--skill NAME] [--outcome OUTCOME]
|
|
21
|
+
Append one content-free local observation under ignored .atelier-local state.
|
|
22
|
+
Prompts, transcripts, free-form summaries, and source content are not accepted.
|
|
23
|
+
|
|
24
|
+
candidates [--all] [--json]
|
|
25
|
+
Aggregate local observations into evidence-thresholded create, improve,
|
|
26
|
+
reconcile, or retire candidates. This command never edits a skill.
|
|
27
|
+
|
|
28
|
+
sync [--source DIR] [--target .agents/skills] [--json]
|
|
29
|
+
Produce a content-bound plan for updating a repo-scoped managed projection.
|
|
30
|
+
Apply only with --apply --confirm PLAN_DIGEST. Source skills are never changed,
|
|
31
|
+
unmanaged collisions and locally drifted managed skills block, and removals are
|
|
32
|
+
moved to ignored quarantine rather than deleted.
|
|
33
|
+
|
|
34
|
+
Exit codes: 0 clean or applied, 1 findings or blockers, 2 usage error.`
|
|
35
|
+
|
|
36
|
+
const OPTION_SPEC = {
|
|
37
|
+
root: 'value',
|
|
38
|
+
peer: 'value',
|
|
39
|
+
source: 'value',
|
|
40
|
+
target: 'value',
|
|
41
|
+
workflow: 'value',
|
|
42
|
+
signal: 'value',
|
|
43
|
+
skill: 'value',
|
|
44
|
+
outcome: 'value',
|
|
45
|
+
confirm: 'value',
|
|
46
|
+
json: 'flag',
|
|
47
|
+
all: 'flag',
|
|
48
|
+
apply: 'flag',
|
|
49
|
+
'dry-run': 'flag',
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function usageError(message) {
|
|
53
|
+
console.error(`${message}\n\n${USAGE}`)
|
|
54
|
+
process.exit(2)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function parseOptions(argv, allowed) {
|
|
58
|
+
const options = {}
|
|
59
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
60
|
+
const arg = argv[index]
|
|
61
|
+
if (arg === '--help' || arg === '-h') {
|
|
62
|
+
console.log(USAGE)
|
|
63
|
+
process.exit(0)
|
|
64
|
+
}
|
|
65
|
+
if (!arg.startsWith('--')) usageError(`unexpected argument: ${arg}`)
|
|
66
|
+
const [rawName, inline] = arg.slice(2).split(/=(.*)/s, 2)
|
|
67
|
+
if (!allowed.has(rawName) || !(rawName in OPTION_SPEC)) usageError(`unknown option --${rawName}`)
|
|
68
|
+
if (OPTION_SPEC[rawName] === 'flag') {
|
|
69
|
+
if (inline !== undefined) usageError(`--${rawName} does not take a value`)
|
|
70
|
+
options[rawName] = true
|
|
71
|
+
continue
|
|
72
|
+
}
|
|
73
|
+
let value = inline
|
|
74
|
+
if (value === undefined) {
|
|
75
|
+
index += 1
|
|
76
|
+
value = argv[index]
|
|
77
|
+
}
|
|
78
|
+
if (value === undefined || value.startsWith('--')) usageError(`--${rawName} requires a value`)
|
|
79
|
+
options[rawName] = value
|
|
80
|
+
}
|
|
81
|
+
return options
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function printAudit(report, json) {
|
|
85
|
+
if (json) {
|
|
86
|
+
console.log(JSON.stringify(report, null, 2))
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
console.log(`skills: ${report.summary.skills}; errors: ${report.summary.errors}; warnings: ${report.summary.warnings}`)
|
|
90
|
+
for (const finding of report.findings) {
|
|
91
|
+
console.log(`${finding.severity}: ${finding.code} ${finding.surface}/${finding.skill ?? '-'} ${finding.path}`)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function runAudit(argv) {
|
|
96
|
+
const options = parseOptions(argv, new Set(['root', 'peer', 'json']))
|
|
97
|
+
if (options.peer && !options.root) usageError('--peer requires --root')
|
|
98
|
+
const surfaces = options.root
|
|
99
|
+
? [
|
|
100
|
+
{ name: 'source', root: path.resolve(options.root) },
|
|
101
|
+
...(options.peer ? [{ name: 'peer', root: path.resolve(options.peer) }] : []),
|
|
102
|
+
]
|
|
103
|
+
: undefined
|
|
104
|
+
const report = auditSkillCatalog({ surfaces, reportRoot: process.cwd() })
|
|
105
|
+
printAudit(report, options.json)
|
|
106
|
+
return (process.exitCode = report.ok ? 0 : 1)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function runObserve(argv) {
|
|
110
|
+
const options = parseOptions(argv, new Set(['workflow', 'signal', 'skill', 'outcome', 'json']))
|
|
111
|
+
if (!options.workflow) usageError('--workflow is required')
|
|
112
|
+
if (!options.signal) usageError('--signal is required')
|
|
113
|
+
try {
|
|
114
|
+
const receipt = recordSkillObservation({
|
|
115
|
+
workspaceRoot: process.cwd(),
|
|
116
|
+
workflowKey: options.workflow,
|
|
117
|
+
signal: options.signal,
|
|
118
|
+
skill: options.skill ?? null,
|
|
119
|
+
outcome: options.outcome ?? 'unknown',
|
|
120
|
+
})
|
|
121
|
+
if (options.json) console.log(JSON.stringify(receipt, null, 2))
|
|
122
|
+
else console.log(`recorded ${receipt.observation.signal} for ${receipt.observation.workflowKey} as local observation ${receipt.observation.id}`)
|
|
123
|
+
return (process.exitCode = 0)
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error(error.message)
|
|
126
|
+
process.exit(1)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function runCandidates(argv) {
|
|
131
|
+
const options = parseOptions(argv, new Set(['all', 'json']))
|
|
132
|
+
try {
|
|
133
|
+
const report = buildSkillCandidates({ workspaceRoot: process.cwd(), includeInsufficient: Boolean(options.all) })
|
|
134
|
+
if (options.json) console.log(JSON.stringify(report, null, 2))
|
|
135
|
+
else if (!report.candidates.length) console.log('no skill candidates meet the evidence threshold')
|
|
136
|
+
else {
|
|
137
|
+
for (const candidate of report.candidates) {
|
|
138
|
+
console.log(`${candidate.status}: ${candidate.kind} ${candidate.workflowKey}${candidate.skill ? ` (${candidate.skill})` : ''} from ${candidate.evidenceCount} observation(s)`)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return (process.exitCode = 0)
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error(error.message)
|
|
144
|
+
process.exit(1)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function printPlan(plan, json) {
|
|
149
|
+
if (json) {
|
|
150
|
+
console.log(JSON.stringify(plan, null, 2))
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
console.log(`plan: ${plan.planDigest}`)
|
|
154
|
+
console.log(`target: ${plan.target.path}`)
|
|
155
|
+
for (const action of plan.actions) console.log(`${action.type}: ${action.name}`)
|
|
156
|
+
for (const blocker of plan.blockers) console.log(`blocker: ${blocker}`)
|
|
157
|
+
if (!plan.actions.length && !plan.blockers.length) console.log('no managed skill changes')
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function runSync(argv) {
|
|
161
|
+
const options = parseOptions(argv, new Set(['source', 'target', 'confirm', 'json', 'apply', 'dry-run']))
|
|
162
|
+
if (options.apply && options['dry-run']) usageError('--apply and --dry-run are mutually exclusive')
|
|
163
|
+
if (options.confirm && !options.apply) usageError('--confirm requires --apply')
|
|
164
|
+
if (options.apply && !options.confirm) usageError('--apply requires --confirm PLAN_DIGEST')
|
|
165
|
+
const settings = {
|
|
166
|
+
workspaceRoot: process.cwd(),
|
|
167
|
+
sourceRoot: options.source ? path.resolve(options.source) : undefined,
|
|
168
|
+
target: options.target ?? path.join('.agents', 'skills'),
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
if (options.apply) {
|
|
172
|
+
const result = applySkillSync({ ...settings, confirm: options.confirm })
|
|
173
|
+
if (options.json) console.log(JSON.stringify(result, null, 2))
|
|
174
|
+
else {
|
|
175
|
+
console.log(`applied: ${result.plan.planDigest}`)
|
|
176
|
+
for (const quarantined of result.quarantined) console.log(`quarantined: ${quarantined}`)
|
|
177
|
+
}
|
|
178
|
+
return (process.exitCode = 0)
|
|
179
|
+
}
|
|
180
|
+
const plan = planSkillSync(settings)
|
|
181
|
+
printPlan(plan, options.json)
|
|
182
|
+
return (process.exitCode = plan.applyAllowed ? 0 : 1)
|
|
183
|
+
} catch (error) {
|
|
184
|
+
console.error(error.message)
|
|
185
|
+
process.exit(1)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const argv = process.argv.slice(2)
|
|
190
|
+
const subcommand = argv[0]
|
|
191
|
+
if (subcommand === '--help' || subcommand === '-h' || subcommand === 'help') {
|
|
192
|
+
console.log(USAGE)
|
|
193
|
+
process.exit(0)
|
|
194
|
+
}
|
|
195
|
+
if (subcommand === 'audit' || subcommand === undefined) runAudit(argv.slice(subcommand ? 1 : 0))
|
|
196
|
+
else if (subcommand === 'observe') runObserve(argv.slice(1))
|
|
197
|
+
else if (subcommand === 'candidates') runCandidates(argv.slice(1))
|
|
198
|
+
else if (subcommand === 'sync') runSync(argv.slice(1))
|
|
199
|
+
else usageError(`unknown skills subcommand: ${subcommand}`)
|
package/src/commands/sync.mjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
enrollRepository,
|
|
4
4
|
executeUserConfirmedCommit,
|
|
5
|
+
obsidianMaintenanceNotice,
|
|
5
6
|
operationTrace,
|
|
6
7
|
planUserConfirmedCommit,
|
|
7
8
|
reconcileRepository,
|
|
@@ -35,6 +36,12 @@ function print(value) {
|
|
|
35
36
|
console.log(JSON.stringify(value, null, 2))
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
// Status only gains a member when the enrolled project enables Obsidian maintenance; otherwise it is unchanged.
|
|
40
|
+
function withMaintenanceNotice(result) {
|
|
41
|
+
const notice = obsidianMaintenanceNotice({ repoPath })
|
|
42
|
+
return notice === null ? result : { ...result, obsidianMaintenance: notice }
|
|
43
|
+
}
|
|
44
|
+
|
|
38
45
|
async function runLoop({ repoPath, intervalMs, fetchAttempts, once }) {
|
|
39
46
|
do {
|
|
40
47
|
const result = reconcileRepository({ repoPath, fetchAttempts })
|
|
@@ -56,7 +63,7 @@ try {
|
|
|
56
63
|
process.exitCode = result.ok ? 0 : 1
|
|
57
64
|
} else if (subcommand === 'status' || subcommand === 'audit') {
|
|
58
65
|
const result = runtimeStatus({ repoPath })
|
|
59
|
-
print(result)
|
|
66
|
+
print(withMaintenanceNotice(result))
|
|
60
67
|
process.exitCode = result.ok ? 0 : 1
|
|
61
68
|
} else if (subcommand === 'reconcile') {
|
|
62
69
|
const result = reconcileRepository({ repoPath, fetchAttempts: integer(args.retries, 3) })
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { parseDefinition, parsePlacement, need, isRef, deepFreeze } from './wire.mjs';
|
|
3
|
+
export const digestBytes = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
4
|
+
|
|
5
|
+
// A host-owned immutable snapshot, not a mutable catalogue or remote importer.
|
|
6
|
+
export function createRegistrySnapshot({ definitionBytes, rendererBytes, revision, lifecycle = 'active', allowDeprecated = false, validateProps, validateIntent }) {
|
|
7
|
+
const definition = deepFreeze(parseDefinition(definitionBytes));
|
|
8
|
+
need(isRef(revision) && ['active', 'deprecated', 'withdrawn', 'revoked'].includes(lifecycle));
|
|
9
|
+
need(typeof validateProps === 'function' && typeof validateIntent === 'function');
|
|
10
|
+
const definitionDigest = digestBytes(definitionBytes), rendererDigest = digestBytes(rendererBytes);
|
|
11
|
+
return Object.freeze({ definition, revision, definitionDigest, rendererDigest, lifecycle,
|
|
12
|
+
resolve({ sourceBytes, sourceRef, request, operationRef, serviceRef }) {
|
|
13
|
+
need(lifecycle === 'active' || (lifecycle === 'deprecated' && allowDeprecated));
|
|
14
|
+
const placement = deepFreeze(parsePlacement(sourceBytes));
|
|
15
|
+
need(isRef(sourceRef) && isRef(serviceRef) && isRef(operationRef));
|
|
16
|
+
need(placement.componentId === definition.componentId && placement.componentVersion === definition.version);
|
|
17
|
+
need(placement.placementId === request.placementId && digestBytes(sourceBytes) === request.sourceDigest);
|
|
18
|
+
need(definition.previewModes.includes(request.mode) && definition.projections.includes('web'));
|
|
19
|
+
need(validateProps(placement.props) === true);
|
|
20
|
+
need(placement.dataBindings.length === 1 && definition.dataContractRefs.includes(placement.dataBindings[0].contractRef));
|
|
21
|
+
if (request.intentRef) need(validateIntent(request.intentRef, request.payload) === true);
|
|
22
|
+
return deepFreeze({ resourceRef: placement.dataBindings[0].resourceRef, operationRef, serviceRef, sourceRef,
|
|
23
|
+
placementRef: placement.placementId, sourceDigest: request.sourceDigest, descriptorDigest: definitionDigest,
|
|
24
|
+
rendererDigest, registryRevision: revision, warning: lifecycle === 'deprecated' ? 'deprecated' : null });
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Opt-in experimental wire parser. No identity, URL resolution or execution.
|
|
2
|
+
export const MODES = Object.freeze(['synthetic', 'connected-read-only', 'sandbox-interactive']);
|
|
3
|
+
const REF = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,159}$/;
|
|
4
|
+
const HASH = /^[a-f0-9]{64}$/;
|
|
5
|
+
const VERSION = /^\d+\.\d+\.\d+$/;
|
|
6
|
+
const forbidden = new Set(['__proto__', 'constructor', 'prototype']);
|
|
7
|
+
const encoder = new TextEncoder();
|
|
8
|
+
const decoder = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
|
|
9
|
+
export function need(value, message = 'Invalid bounded composition data') { if (!value) throw new TypeError(message); }
|
|
10
|
+
export const isRef = value => typeof value === 'string' && REF.test(value);
|
|
11
|
+
export const isDigest = value => typeof value === 'string' && HASH.test(value);
|
|
12
|
+
export function closed(value, required, optional = []) {
|
|
13
|
+
need(value && typeof value === 'object' && !Array.isArray(value));
|
|
14
|
+
need(required.every(key => Object.hasOwn(value, key)) && Object.keys(value).every(key => required.includes(key) || optional.includes(key)));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// JSON.parse alone accepts duplicate keys. This small JSON grammar refuses them
|
|
18
|
+
// at every depth, before schema validation; it never evaluates source text.
|
|
19
|
+
export function parseBoundedJSON(input) {
|
|
20
|
+
need(typeof input === 'string' || input instanceof Uint8Array);
|
|
21
|
+
const bytes = typeof input === 'string' ? encoder.encode(input) : input;
|
|
22
|
+
need(bytes.byteLength <= 65536);
|
|
23
|
+
let text = decoder.decode(bytes);
|
|
24
|
+
if (typeof input === 'string') need(text === input, 'Lossy Unicode refused');
|
|
25
|
+
if (text.charCodeAt(0) === 0xfeff) text = text.slice(1);
|
|
26
|
+
let i = 0;
|
|
27
|
+
const ws = () => { while (/[\x20\t\r\n]/.test(text[i] || '\0')) i++; };
|
|
28
|
+
const string = () => {
|
|
29
|
+
const start = i++;
|
|
30
|
+
while (i < text.length) {
|
|
31
|
+
const c = text[i++];
|
|
32
|
+
if (c === '\\') { i++; continue; }
|
|
33
|
+
if (c === '"') {
|
|
34
|
+
const value = JSON.parse(text.slice(start, i));
|
|
35
|
+
need(decoder.decode(encoder.encode(value)) === value, 'Lossy Unicode refused');
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
throw new TypeError('Unterminated JSON string');
|
|
40
|
+
};
|
|
41
|
+
function value(depth) {
|
|
42
|
+
need(depth <= 8); ws();
|
|
43
|
+
if (text[i] === '"') return string();
|
|
44
|
+
if (text[i] === '{') {
|
|
45
|
+
i++; ws(); const result = Object.create(null);
|
|
46
|
+
if (text[i] === '}') { i++; return result; }
|
|
47
|
+
while (i < text.length) {
|
|
48
|
+
ws(); need(text[i] === '"'); const key = string();
|
|
49
|
+
need(!Object.hasOwn(result, key), 'Duplicate JSON key refused');
|
|
50
|
+
ws(); need(text[i++] === ':'); result[key] = value(depth + 1); ws();
|
|
51
|
+
if (text[i] === '}') { i++; return result; }
|
|
52
|
+
need(text[i++] === ',');
|
|
53
|
+
}
|
|
54
|
+
} else if (text[i] === '[') {
|
|
55
|
+
i++; ws(); const result = [];
|
|
56
|
+
if (text[i] === ']') { i++; return result; }
|
|
57
|
+
while (i < text.length) {
|
|
58
|
+
result.push(value(depth + 1)); ws();
|
|
59
|
+
if (text[i] === ']') { i++; return result; }
|
|
60
|
+
need(text[i++] === ',');
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
const match = /^(?:true|false|null|-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)/.exec(text.slice(i));
|
|
64
|
+
need(match); i += match[0].length;
|
|
65
|
+
const result = JSON.parse(match[0]); need(typeof result !== 'number' || Number.isFinite(result)); return result;
|
|
66
|
+
}
|
|
67
|
+
throw new TypeError('Invalid JSON');
|
|
68
|
+
}
|
|
69
|
+
const result = value(0); ws(); need(i === text.length); return result;
|
|
70
|
+
}
|
|
71
|
+
export function scalarMap(value) {
|
|
72
|
+
need(value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length <= 64);
|
|
73
|
+
for (const [key, v] of Object.entries(value)) {
|
|
74
|
+
need(/^[a-z][a-zA-Z0-9_]{0,79}$/.test(key) && !forbidden.has(key));
|
|
75
|
+
need(v === null || typeof v === 'boolean' || (typeof v === 'number' && Number.isFinite(v)) || (typeof v === 'string' && v.length <= 4096));
|
|
76
|
+
}
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
function refs(values, maximum, allowed) {
|
|
80
|
+
need(Array.isArray(values) && values.length <= maximum && new Set(values).size === values.length);
|
|
81
|
+
need(values.every(v => allowed ? allowed.includes(v) : isRef(v)));
|
|
82
|
+
}
|
|
83
|
+
function version(v) { need(typeof v === 'string' && v.length <= 32 && VERSION.test(v)); }
|
|
84
|
+
export function parseDefinition(bytes) {
|
|
85
|
+
const v = parseBoundedJSON(bytes);
|
|
86
|
+
closed(v, ['schema', 'componentId', 'version', 'rendererRef', 'propsSchemaRef', 'dataContractRefs', 'projections', 'previewModes']);
|
|
87
|
+
need(v.schema === 'atelier-component-definition/proposal-v1');
|
|
88
|
+
for (const key of ['componentId', 'rendererRef', 'propsSchemaRef']) need(isRef(v[key]));
|
|
89
|
+
version(v.version); refs(v.dataContractRefs, 16); refs(v.projections, 4, ['web', 'native', 'desktop', 'document']); refs(v.previewModes, 3, MODES);
|
|
90
|
+
return v;
|
|
91
|
+
}
|
|
92
|
+
export function parsePlacement(bytes) {
|
|
93
|
+
const v = parseBoundedJSON(bytes);
|
|
94
|
+
closed(v, ['schema', 'placementId', 'componentId', 'componentVersion', 'slotRef', 'props', 'dataBindings']);
|
|
95
|
+
need(v.schema === 'atelier-component-placement/proposal-v1');
|
|
96
|
+
for (const key of ['placementId', 'componentId', 'slotRef']) need(isRef(v[key]));
|
|
97
|
+
version(v.componentVersion); scalarMap(v.props);
|
|
98
|
+
need(Array.isArray(v.dataBindings) && v.dataBindings.length <= 16);
|
|
99
|
+
const seen = new Set();
|
|
100
|
+
for (const binding of v.dataBindings) {
|
|
101
|
+
closed(binding, ['contractRef', 'resourceRef']); need(isRef(binding.contractRef) && isRef(binding.resourceRef) && !seen.has(binding.contractRef)); seen.add(binding.contractRef);
|
|
102
|
+
}
|
|
103
|
+
return v;
|
|
104
|
+
}
|
|
105
|
+
export function parseRequest(bytes) {
|
|
106
|
+
const v = parseBoundedJSON(bytes);
|
|
107
|
+
const action = v?.schema === 'atelier-component-intent/proposal-v1';
|
|
108
|
+
closed(v, ['schema', 'status', 'requestId', 'placementId', 'sourceDigest', 'mode', ...(action ? ['intentRef', 'payload'] : [])]);
|
|
109
|
+
need(action || v.schema === 'atelier-preview-request/proposal-v1'); need(v.status === 'proposed' && MODES.includes(v.mode));
|
|
110
|
+
need(isRef(v.requestId) && isRef(v.placementId) && isDigest(v.sourceDigest));
|
|
111
|
+
if (action) { need(isRef(v.intentRef)); scalarMap(v.payload); need(v.mode === 'sandbox-interactive'); }
|
|
112
|
+
return v;
|
|
113
|
+
}
|
|
114
|
+
export function deepFreeze(value) {
|
|
115
|
+
if (value && typeof value === 'object') { for (const item of Object.values(value)) deepFreeze(item); Object.freeze(value); }
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
export function canonical(value) {
|
|
119
|
+
if (Array.isArray(value)) return '[' + value.map(canonical).join(',') + ']';
|
|
120
|
+
if (value && typeof value === 'object') return '{' + Object.keys(value).sort().map(key => JSON.stringify(key) + ':' + canonical(value[key])).join(',') + '}';
|
|
121
|
+
return JSON.stringify(value);
|
|
122
|
+
}
|
package/src/contracts/corpus.mjs
CHANGED
|
@@ -26,6 +26,25 @@ import { fileURLToPath } from 'node:url'
|
|
|
26
26
|
// document, not a schema, and is deliberately absent.
|
|
27
27
|
|
|
28
28
|
export const CONTRACT_CORPUS = [
|
|
29
|
+
{ name: 'atelier-decision-request', contractFile: 'contracts/atelier-decision-request.v1.schema.json', fixtureRoot: 'fixtures/decisions/request', registry: false },
|
|
30
|
+
{ name: 'atelier-decision-result', contractFile: 'contracts/atelier-decision-result.v1.schema.json', fixtureRoot: 'fixtures/decisions/result', registry: false },
|
|
31
|
+
{ name: 'atelier-adoption-policy-v1', contractFile: 'contracts/atelier-adoption-policy.v1.schema.json', fixtureRoot: 'fixtures/atelier-upgrade-transaction/adoption-policy', registry: false },
|
|
32
|
+
{ name: 'atelier-migration-v2', contractFile: 'contracts/atelier-migration.v2.schema.json', fixtureRoot: 'fixtures/atelier-upgrade-transaction/migration', registry: false },
|
|
33
|
+
{ name: 'atelier-upgrade-plan-v2', contractFile: 'contracts/atelier-upgrade-plan.v2.schema.json', fixtureRoot: 'fixtures/atelier-upgrade-transaction/upgrade-plan', registry: false },
|
|
34
|
+
{ name: 'atelier-upgrade-receipt-v1', contractFile: 'contracts/atelier-upgrade-receipt.v1.schema.json', fixtureRoot: 'fixtures/atelier-upgrade-transaction/upgrade-receipt', registry: false },
|
|
35
|
+
{ name: 'atelier-presentation', contractFile: 'contracts/atelier-presentation.v1.schema.json', validFiles: ['fixtures/ui/presentation/reference.v1.json'], registry: false },
|
|
36
|
+
{ name: 'atelier-pane-presentation', contractFile: 'contracts/atelier-pane-presentation.v1.schema.json', validFiles: ['fixtures/ui/presentation/pane.v1.json'], registry: false },
|
|
37
|
+
{ name: 'atelier-vault-publication', contractFile: 'contracts/atelier-vault-publication.v1.schema.json', fixtureRoot: 'fixtures/atelier-vault-publication', registry: true },
|
|
38
|
+
...['guide', 'intake'].map(kind => ({
|
|
39
|
+
name: `atelier-${kind}`, contractFile: `contracts/atelier-${kind}.v1.schema.json`,
|
|
40
|
+
fixtureRoot: `fixtures/atelier-${kind}`, registry: true,
|
|
41
|
+
})),
|
|
42
|
+
...[['audit', 'auditReport'], ['candidates', 'candidateReport'], ['plan', 'syncPlan'], ['lock', 'syncLock']].map(([kind, definition]) => ({
|
|
43
|
+
name: `atelier-skill-${kind}`, contractFile: 'contracts/atelier-skill-steward.v1.schema.json',
|
|
44
|
+
docPointer: `#/$defs/${definition}`, fixtureRoot: `fixtures/atelier-skill-steward/${kind}`, registry: true,
|
|
45
|
+
})),
|
|
46
|
+
{name: 'atelier-coauthor', contractFile: 'contracts/atelier-coauthor.v1.schema.json', fixtureRoot: 'fixtures/atelier-coauthor', registry: true},
|
|
47
|
+
{name: 'atelier-review', contractFile: 'contracts/atelier-review.v1.schema.json', fixtureRoot: 'fixtures/atelier-review', registry: false},
|
|
29
48
|
{
|
|
30
49
|
name: 'atelier-repository-observation',
|
|
31
50
|
contractFile: 'contracts/atelier-repository-observation.v1.schema.json',
|
|
@@ -142,6 +161,16 @@ export const CONTRACT_CORPUS = [
|
|
|
142
161
|
invalidFiles: ['fixtures/analysis-adapter/invalid/manifest-hidden-provider.v1.json'],
|
|
143
162
|
registry: true,
|
|
144
163
|
},
|
|
164
|
+
// Obsidian projection contracts. test/obsidian-contract.test.mjs owns their
|
|
165
|
+
// positive and negative coverage (several refusals are semantic, beyond what
|
|
166
|
+
// a schema matcher can express), so they carry registry: false.
|
|
167
|
+
...[
|
|
168
|
+
'corpus-profile', 'scope', 'source-snapshot', 'generation-manifest', 'publication-journal', 'service-state',
|
|
169
|
+
'edit-operation', 'apply-policy', 'proposal-receipt', 'acceptance-receipt', 'ext-settings',
|
|
170
|
+
].map((shape) => ({
|
|
171
|
+
name: `atelier-obsidian-${shape}`, contractFile: `contracts/atelier-obsidian-${shape}.v1.schema.json`,
|
|
172
|
+
fixtureRoot: `fixtures/obsidian/contracts/${shape}`, registry: false,
|
|
173
|
+
})),
|
|
145
174
|
{
|
|
146
175
|
name: 'analysis-adapter-claim-output',
|
|
147
176
|
contractFile: 'contracts/analysis-adapter.v1.schema.json',
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export type JsonValue = null | boolean | number | string | JsonValue[] | JsonObject
|
|
2
|
+
export interface JsonObject { [key: string]: JsonValue }
|
|
3
|
+
export interface DecisionExtension { ext?: JsonObject }
|
|
4
|
+
export interface DecisionScope extends DecisionExtension {
|
|
5
|
+
workspaceId: string
|
|
6
|
+
/** Host-reported provenance; this reference does not grant authorization. */
|
|
7
|
+
authorizationRef: string
|
|
8
|
+
}
|
|
9
|
+
export interface DecisionEvidence extends DecisionExtension {
|
|
10
|
+
id: string
|
|
11
|
+
sourceRef: string
|
|
12
|
+
}
|
|
13
|
+
export interface DecisionQuestionBase extends DecisionExtension {
|
|
14
|
+
instructions: string
|
|
15
|
+
evidenceIds: string[]
|
|
16
|
+
}
|
|
17
|
+
export interface ChoiceQuestion extends DecisionQuestionBase {
|
|
18
|
+
type: 'choice'
|
|
19
|
+
criteria: Record<string, string>
|
|
20
|
+
}
|
|
21
|
+
export interface ScoreQuestion extends DecisionQuestionBase {
|
|
22
|
+
type: 'score'
|
|
23
|
+
criteria: string[]
|
|
24
|
+
}
|
|
25
|
+
export interface BooleanQuestion extends DecisionQuestionBase {
|
|
26
|
+
type: 'boolean'
|
|
27
|
+
criteria: DecisionExtension & { true: string; false: string }
|
|
28
|
+
}
|
|
29
|
+
export type DecisionQuestion = ChoiceQuestion | ScoreQuestion | BooleanQuestion
|
|
30
|
+
export interface DecisionRequest extends DecisionExtension {
|
|
31
|
+
schema: 'atelier-decision-request@v1'
|
|
32
|
+
contractVersion?: `1.${number}.${number}`
|
|
33
|
+
id: string
|
|
34
|
+
task: string
|
|
35
|
+
rubricVersion: string
|
|
36
|
+
scope: DecisionScope
|
|
37
|
+
state: string
|
|
38
|
+
evidence: DecisionEvidence[]
|
|
39
|
+
questions: Record<string, DecisionQuestion>
|
|
40
|
+
}
|
|
41
|
+
export interface ChoiceAnswer extends DecisionExtension {
|
|
42
|
+
type: 'choice'
|
|
43
|
+
choice: string
|
|
44
|
+
probabilities: Record<string, number>
|
|
45
|
+
/** A provider statistic, not a measured probability of semantic correctness. */
|
|
46
|
+
confidence: number
|
|
47
|
+
}
|
|
48
|
+
export interface ScoreAnswer extends DecisionExtension {
|
|
49
|
+
type: 'score'
|
|
50
|
+
/** The zero-index weighted expectation of the ordered criteria. */
|
|
51
|
+
score: number
|
|
52
|
+
probabilities: number[]
|
|
53
|
+
confidence: number
|
|
54
|
+
}
|
|
55
|
+
export interface BooleanAnswer extends DecisionExtension {
|
|
56
|
+
type: 'boolean'
|
|
57
|
+
probability: number
|
|
58
|
+
}
|
|
59
|
+
export type DecisionAnswer = ChoiceAnswer | ScoreAnswer | BooleanAnswer
|
|
60
|
+
export interface DecisionUsage extends DecisionExtension {
|
|
61
|
+
inputTokens: number
|
|
62
|
+
outputTokens: number
|
|
63
|
+
}
|
|
64
|
+
export interface DecisionResultBase extends DecisionExtension {
|
|
65
|
+
schema: 'atelier-decision-result@v1'
|
|
66
|
+
contractVersion?: `1.${number}.${number}`
|
|
67
|
+
requestId: string
|
|
68
|
+
requestDigest: string
|
|
69
|
+
task: string
|
|
70
|
+
rubricVersion: string
|
|
71
|
+
scope: DecisionScope
|
|
72
|
+
provider: DecisionExtension & { id: string; model: string }
|
|
73
|
+
authority: 'proposal-only'
|
|
74
|
+
mode: 'shadow' | 'advisory'
|
|
75
|
+
usage: DecisionUsage | null
|
|
76
|
+
elapsedMs: number
|
|
77
|
+
}
|
|
78
|
+
export interface AssessedDecisionResult extends DecisionResultBase {
|
|
79
|
+
status: 'assessed'
|
|
80
|
+
answers: Record<string, DecisionAnswer>
|
|
81
|
+
reason?: never
|
|
82
|
+
}
|
|
83
|
+
export type DecisionAbstentionReason = 'insufficient-evidence' | 'ambiguous' | 'no-match' | 'budget-exhausted' | 'provider-unavailable' | 'timeout' | 'invalid-response' | 'unauthorized'
|
|
84
|
+
export interface AbstainedDecisionResult extends DecisionResultBase {
|
|
85
|
+
status: 'abstained'
|
|
86
|
+
answers: Record<string, never>
|
|
87
|
+
reason: DecisionAbstentionReason
|
|
88
|
+
}
|
|
89
|
+
export type DecisionResult = AssessedDecisionResult | AbstainedDecisionResult
|
|
90
|
+
export interface DecisionValidation { ok: boolean; errors: string[] }
|
|
91
|
+
export function validateDecisionRequest(input: unknown): DecisionValidation
|
|
92
|
+
export function validateDecisionResult(request: unknown, result: unknown): DecisionValidation
|
|
93
|
+
/** Checks questions and answers without state, hashing, or evidence membership. */
|
|
94
|
+
export function validateDecisionAnswers(questions: unknown, answers: unknown): DecisionValidation
|
|
95
|
+
/** Throws a generic TypeError for invalid input. The digest is lowercase SHA-256. */
|
|
96
|
+
export function decisionRequestDigest(request: unknown): string
|