@mnstry/atelier 0.2.0-alpha.5 → 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 +191 -0
- package/README.md +86 -25
- 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-repository-observation.v1.schema.json +163 -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 +12 -2
- package/docs/atelier-runtime.md +13 -2
- package/docs/atelier-sync.md +172 -0
- package/docs/blocks/claims.md +28 -18
- package/docs/blocks/will-not-do.md +12 -3
- 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 +99 -2
- package/docs/review-portability.md +55 -0
- package/docs/skill-steward.md +156 -0
- package/docs/upgrade.md +70 -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-repository-observation/invalid/complete-with-blocker.v1.json +18 -0
- package/fixtures/atelier-repository-observation/valid/complete-local.v1.json +48 -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 +61 -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/content-rules.mjs +9 -4
- package/src/boundary/policy.mjs +74 -42
- package/src/cli/project-options.mjs +58 -0
- package/src/cli/run.mjs +61 -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 +107 -0
- package/src/composition/registry.mjs +27 -0
- package/src/composition/wire.mjs +122 -0
- package/src/contracts/corpus.mjs +35 -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/index.mjs +29 -0
- package/src/intake/store.mjs +119 -0
- package/src/preview/channel.mjs +89 -0
- package/src/project/config.mjs +50 -49
- 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/git-adapter.mjs +189 -0
- package/src/runtime/local-state.mjs +439 -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/repository-observation.mjs +491 -0
- package/src/runtime/supervisor.mjs +803 -0
- 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,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
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
|
|
3
|
+
const ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/
|
|
4
|
+
const RESERVED = new Set(['__proto__', 'constructor', 'prototype'])
|
|
5
|
+
const VERSION = /^1\.[0-9]+\.[0-9]+$/
|
|
6
|
+
const REASONS = new Set(['insufficient-evidence', 'ambiguous', 'no-match', 'budget-exhausted', 'provider-unavailable', 'timeout', 'invalid-response', 'unauthorized'])
|
|
7
|
+
const TOLERANCE = 1e-6
|
|
8
|
+
|
|
9
|
+
// Inspect descriptors before reading values. Invalid in-process inputs such as
|
|
10
|
+
// accessors, symbols, sparse arrays, cycles and custom prototypes are refused
|
|
11
|
+
// without calling their conversion methods or returning their content.
|
|
12
|
+
function jsonSnapshot(input) {
|
|
13
|
+
const ancestors = new Set()
|
|
14
|
+
let nodes = 0
|
|
15
|
+
let bytes = 0
|
|
16
|
+
function count(value) {
|
|
17
|
+
bytes += value
|
|
18
|
+
if (bytes > 16777216) throw new TypeError()
|
|
19
|
+
}
|
|
20
|
+
function copy(value, depth) {
|
|
21
|
+
if (++nodes > 100000 || depth > 32) throw new TypeError()
|
|
22
|
+
if (value === null || typeof value === 'string' || typeof value === 'boolean' || typeof value === 'number' && Number.isFinite(value)) {
|
|
23
|
+
if (typeof value === 'string' && Buffer.byteLength(value) > 16777216) throw new TypeError()
|
|
24
|
+
count(Buffer.byteLength(JSON.stringify(value)))
|
|
25
|
+
return value
|
|
26
|
+
}
|
|
27
|
+
if (!value || typeof value !== 'object' || ancestors.has(value)) throw new TypeError()
|
|
28
|
+
const array = Array.isArray(value)
|
|
29
|
+
const prototype = Object.getPrototypeOf(value)
|
|
30
|
+
if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) throw new TypeError()
|
|
31
|
+
const descriptors = Object.getOwnPropertyDescriptors(value)
|
|
32
|
+
const keys = Reflect.ownKeys(descriptors)
|
|
33
|
+
if (keys.some((key) => typeof key !== 'string' || RESERVED.has(key))) throw new TypeError()
|
|
34
|
+
count(2)
|
|
35
|
+
ancestors.add(value)
|
|
36
|
+
let result
|
|
37
|
+
if (array) {
|
|
38
|
+
const length = descriptors.length?.value
|
|
39
|
+
if (!Number.isSafeInteger(length) || length < 0 || length > 100000 || keys.length !== length + 1) throw new TypeError()
|
|
40
|
+
result = []
|
|
41
|
+
for (let index = 0; index < length; index++) {
|
|
42
|
+
const descriptor = descriptors[String(index)]
|
|
43
|
+
if (!descriptor || !Object.hasOwn(descriptor, 'value') || !descriptor.enumerable) throw new TypeError()
|
|
44
|
+
if (index) count(1)
|
|
45
|
+
result.push(copy(descriptor.value, depth + 1))
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
result = Object.create(null)
|
|
49
|
+
for (const [index, key] of keys.entries()) {
|
|
50
|
+
const descriptor = descriptors[key]
|
|
51
|
+
if (!Object.hasOwn(descriptor, 'value') || !descriptor.enumerable) throw new TypeError()
|
|
52
|
+
if (Buffer.byteLength(key) > 16777216) throw new TypeError()
|
|
53
|
+
count(Buffer.byteLength(JSON.stringify(key)) + 1 + (index ? 1 : 0))
|
|
54
|
+
result[key] = copy(descriptor.value, depth + 1)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
ancestors.delete(value)
|
|
58
|
+
return result
|
|
59
|
+
}
|
|
60
|
+
return copy(input, 0)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function object(value) {
|
|
64
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function text(value, maximum, allowEmpty = false) {
|
|
68
|
+
if (typeof value !== 'string' || (!allowEmpty && !value.trim())) return false
|
|
69
|
+
let length = 0
|
|
70
|
+
for (const character of value) {
|
|
71
|
+
void character
|
|
72
|
+
if (++length > maximum) return false
|
|
73
|
+
}
|
|
74
|
+
return true
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function identifier(value) {
|
|
78
|
+
return typeof value === 'string' && ID.test(value) && !RESERVED.has(value)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function report() {
|
|
82
|
+
const errors = []
|
|
83
|
+
return {
|
|
84
|
+
errors,
|
|
85
|
+
check(condition, label) {
|
|
86
|
+
if (!condition && errors.length < 64) errors.push(label)
|
|
87
|
+
return condition
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function closed(value, required, optional, label, checks) {
|
|
93
|
+
if (!checks.check(object(value), `${label}: expected object`)) return false
|
|
94
|
+
const allowed = new Set([...required, ...optional, 'ext'])
|
|
95
|
+
checks.check(required.every((key) => Object.hasOwn(value, key)), `${label}: required field missing`)
|
|
96
|
+
checks.check(Object.keys(value).every((key) => allowed.has(key)), `${label}: unknown field`)
|
|
97
|
+
if (Object.hasOwn(value, 'ext')) checks.check(object(value.ext), `${label}.ext: expected object`)
|
|
98
|
+
return true
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function record(value, minimum, maximum, label, checks) {
|
|
102
|
+
if (!checks.check(object(value), `${label}: expected record`)) return false
|
|
103
|
+
const keys = Object.keys(value)
|
|
104
|
+
checks.check(keys.length >= minimum && keys.length <= maximum, `${label}: entry count outside limits`)
|
|
105
|
+
checks.check(keys.every(identifier), `${label}: invalid identifier`)
|
|
106
|
+
return keys.length <= maximum
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function scope(value, label, checks) {
|
|
110
|
+
if (closed(value, ['workspaceId', 'authorizationRef'], [], label, checks)) {
|
|
111
|
+
checks.check(identifier(value.workspaceId), `${label}.workspaceId: invalid identifier`)
|
|
112
|
+
checks.check(identifier(value.authorizationRef), `${label}.authorizationRef: invalid identifier`)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function version(value, label, checks) {
|
|
117
|
+
if (Object.hasOwn(value, 'contractVersion')) {
|
|
118
|
+
checks.check(typeof value.contractVersion === 'string' && VERSION.test(value.contractVersion), `${label}.contractVersion: invalid version`)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function inspectRequest(request, checks) {
|
|
123
|
+
if (!closed(request, ['schema', 'id', 'task', 'rubricVersion', 'scope', 'state', 'evidence', 'questions'], ['contractVersion'], 'request', checks)) return
|
|
124
|
+
checks.check(request.schema === 'atelier-decision-request@v1', 'request.schema: unsupported schema')
|
|
125
|
+
version(request, 'request', checks)
|
|
126
|
+
for (const key of ['id', 'task', 'rubricVersion']) checks.check(identifier(request[key]), `request.${key}: invalid identifier`)
|
|
127
|
+
scope(request.scope, 'request.scope', checks)
|
|
128
|
+
checks.check(text(request.state, 32000, true), 'request.state: expected string within limit')
|
|
129
|
+
const evidenceIds = new Set()
|
|
130
|
+
if (checks.check(Array.isArray(request.evidence) && request.evidence.length >= 1 && request.evidence.length <= 256, 'request.evidence: expected 1..256 entries')) {
|
|
131
|
+
for (const evidence of request.evidence) {
|
|
132
|
+
if (!closed(evidence, ['id', 'sourceRef'], [], 'request.evidence[]', checks)) continue
|
|
133
|
+
checks.check(identifier(evidence.id), 'request.evidence[].id: invalid identifier')
|
|
134
|
+
checks.check(!evidenceIds.has(evidence.id), 'request.evidence: duplicate identifier')
|
|
135
|
+
evidenceIds.add(evidence.id)
|
|
136
|
+
checks.check(text(evidence.sourceRef, 2048), 'request.evidence[].sourceRef: expected nonempty reference within limit')
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
inspectQuestions(request.questions, checks, 'request.questions', evidenceIds)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function inspectQuestions(questions, checks, label, evidenceIds = null) {
|
|
143
|
+
if (!record(questions, 1, 64, label, checks)) return
|
|
144
|
+
for (const question of Object.values(questions)) {
|
|
145
|
+
if (!closed(question, ['type', 'instructions', 'evidenceIds', 'criteria'], [], `${label}[]`, checks)) continue
|
|
146
|
+
checks.check(text(question.instructions, 4000), `${label}[].instructions: expected nonempty string within limit`)
|
|
147
|
+
if (checks.check(Array.isArray(question.evidenceIds) && question.evidenceIds.length >= 1 && question.evidenceIds.length <= 256, `${label}[].evidenceIds: expected 1..256 references`)) {
|
|
148
|
+
checks.check(new Set(question.evidenceIds).size === question.evidenceIds.length, `${label}[].evidenceIds: duplicate reference`)
|
|
149
|
+
checks.check(question.evidenceIds.every(identifier), `${label}[].evidenceIds: invalid reference`)
|
|
150
|
+
if (evidenceIds !== null) checks.check(question.evidenceIds.every((id) => evidenceIds.has(id)), `${label}[].evidenceIds: unknown reference`)
|
|
151
|
+
}
|
|
152
|
+
if (question.type === 'choice') {
|
|
153
|
+
if (record(question.criteria, 2, 64, `${label}[].criteria`, checks)) {
|
|
154
|
+
checks.check(Object.values(question.criteria).every((value) => text(value, 2000)), `${label}[].criteria: expected nonempty descriptions within limit`)
|
|
155
|
+
}
|
|
156
|
+
} else if (question.type === 'score') {
|
|
157
|
+
checks.check(Array.isArray(question.criteria) && question.criteria.length >= 2 && question.criteria.length <= 10 && question.criteria.every((value) => text(value, 2000)), `${label}[].criteria: expected 2..10 nonempty ordered descriptions within limit`)
|
|
158
|
+
} else if (question.type === 'boolean') {
|
|
159
|
+
if (closed(question.criteria, ['true', 'false'], [], `${label}[].criteria`, checks)) {
|
|
160
|
+
checks.check(text(question.criteria.true, 2000) && text(question.criteria.false, 2000), `${label}[].criteria: expected nonempty descriptions within limit`)
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
checks.check(false, `${label}[].type: unsupported type`)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function canonical(value) {
|
|
169
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(',')}]`
|
|
170
|
+
if (object(value)) return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(',')}}`
|
|
171
|
+
return JSON.stringify(value)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function digest(request) {
|
|
175
|
+
return createHash('sha256').update(canonical(request), 'utf8').digest('hex')
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function inspected(input) {
|
|
179
|
+
const checks = report()
|
|
180
|
+
let request
|
|
181
|
+
try {
|
|
182
|
+
request = jsonSnapshot(input)
|
|
183
|
+
inspectRequest(request, checks)
|
|
184
|
+
} catch {
|
|
185
|
+
checks.check(false, 'request: expected bounded plain JSON')
|
|
186
|
+
}
|
|
187
|
+
return { request, checks }
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Validate structure and evidence references; this does not authorize egress. */
|
|
191
|
+
export function validateDecisionRequest(input) {
|
|
192
|
+
const { checks } = inspected(input)
|
|
193
|
+
return { ok: checks.errors.length === 0, errors: checks.errors }
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Hash a valid request, including its scope, evidence, rubric and extensions. */
|
|
197
|
+
export function decisionRequestDigest(input) {
|
|
198
|
+
const { request, checks } = inspected(input)
|
|
199
|
+
if (checks.errors.length) throw new TypeError('Invalid decision request')
|
|
200
|
+
return digest(request)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function probability(value) {
|
|
204
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0 && value <= 1
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function distribution(values, label, checks) {
|
|
208
|
+
if (!checks.check(values.every(probability), `${label}: values must be probabilities`)) return false
|
|
209
|
+
return checks.check(Math.abs(values.reduce((sum, value) => sum + value, 0) - 1) <= TOLERANCE, `${label}: probabilities must sum to one`)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function sameKeys(left, right) {
|
|
213
|
+
const leftKeys = Object.keys(left).sort()
|
|
214
|
+
const rightKeys = Object.keys(right).sort()
|
|
215
|
+
return leftKeys.length === rightKeys.length && leftKeys.every((key, index) => key === rightKeys[index])
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function answer(question, value, checks, label) {
|
|
219
|
+
if (question.type === 'choice') {
|
|
220
|
+
if (!closed(value, ['type', 'choice', 'probabilities', 'confidence'], [], label, checks)) return
|
|
221
|
+
checks.check(value.type === 'choice', `${label}.type: question type mismatch`)
|
|
222
|
+
checks.check(probability(value.confidence), `${label}.confidence: expected probability`)
|
|
223
|
+
checks.check(identifier(value.choice) && Object.hasOwn(question.criteria, value.choice), `${label}.choice: unknown choice`)
|
|
224
|
+
if (record(value.probabilities, 2, 64, `${label}.probabilities`, checks)) {
|
|
225
|
+
checks.check(sameKeys(value.probabilities, question.criteria), `${label}.probabilities: criteria keys mismatch`)
|
|
226
|
+
const values = Object.values(value.probabilities)
|
|
227
|
+
if (distribution(values, `${label}.probabilities`, checks) && Object.hasOwn(value.probabilities, value.choice)) {
|
|
228
|
+
checks.check(value.probabilities[value.choice] === Math.max(...values), `${label}.choice: choice must have maximum probability`)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
} else if (question.type === 'score') {
|
|
232
|
+
if (!closed(value, ['type', 'score', 'probabilities', 'confidence'], [], label, checks)) return
|
|
233
|
+
checks.check(value.type === 'score', `${label}.type: question type mismatch`)
|
|
234
|
+
checks.check(probability(value.confidence), `${label}.confidence: expected probability`)
|
|
235
|
+
checks.check(typeof value.score === 'number' && Number.isFinite(value.score) && value.score >= 0 && value.score <= question.criteria.length - 1, `${label}.score: outside score range`)
|
|
236
|
+
if (checks.check(Array.isArray(value.probabilities) && value.probabilities.length === question.criteria.length, `${label}.probabilities: criteria count mismatch`)) {
|
|
237
|
+
if (distribution(value.probabilities, `${label}.probabilities`, checks)) {
|
|
238
|
+
const expectation = value.probabilities.reduce((sum, value, index) => sum + value * index, 0)
|
|
239
|
+
checks.check(Math.abs(value.score - expectation) <= TOLERANCE, `${label}.score: weighted expectation mismatch`)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
} else {
|
|
243
|
+
if (!closed(value, ['type', 'probability'], [], label, checks)) return
|
|
244
|
+
checks.check(value.type === 'boolean', `${label}.type: question type mismatch`)
|
|
245
|
+
checks.check(probability(value.probability), `${label}.probability: expected probability`)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function inspectAnswers(questions, answers, checks, label) {
|
|
250
|
+
if (!record(answers, 0, 64, label, checks)) return
|
|
251
|
+
checks.check(sameKeys(answers, questions), `${label}: question keys mismatch`)
|
|
252
|
+
for (const [id, question] of Object.entries(questions)) {
|
|
253
|
+
if (Object.hasOwn(answers, id)) answer(question, answers[id], checks, `${label}[]`)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Validate question/answer shape and distributions without state or hashing.
|
|
259
|
+
* Evidence reference syntax is checked; membership and authority belong to
|
|
260
|
+
* the host that holds the source snapshot and transient request binding.
|
|
261
|
+
*/
|
|
262
|
+
export function validateDecisionAnswers(inputQuestions, inputAnswers) {
|
|
263
|
+
const checks = report()
|
|
264
|
+
let questions
|
|
265
|
+
try {
|
|
266
|
+
questions = jsonSnapshot(inputQuestions)
|
|
267
|
+
inspectQuestions(questions, checks, 'questions')
|
|
268
|
+
} catch {
|
|
269
|
+
checks.check(false, 'questions: expected bounded plain JSON')
|
|
270
|
+
}
|
|
271
|
+
if (checks.errors.length) return { ok: false, errors: checks.errors }
|
|
272
|
+
try {
|
|
273
|
+
inspectAnswers(questions, jsonSnapshot(inputAnswers), checks, 'answers')
|
|
274
|
+
} catch {
|
|
275
|
+
checks.check(false, 'answers: expected bounded plain JSON')
|
|
276
|
+
}
|
|
277
|
+
return { ok: checks.errors.length === 0, errors: checks.errors }
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function inspectResult(request, result, checks) {
|
|
281
|
+
if (!closed(result, ['schema', 'requestId', 'requestDigest', 'task', 'rubricVersion', 'scope', 'provider', 'authority', 'mode', 'status', 'answers', 'usage', 'elapsedMs'], ['reason', 'contractVersion'], 'result', checks)) return
|
|
282
|
+
checks.check(result.schema === 'atelier-decision-result@v1', 'result.schema: unsupported schema')
|
|
283
|
+
version(result, 'result', checks)
|
|
284
|
+
checks.check(result.requestId === request.id, 'result.requestId: request binding mismatch')
|
|
285
|
+
checks.check(result.requestDigest === digest(request), 'result.requestDigest: request binding mismatch')
|
|
286
|
+
checks.check(result.task === request.task && result.rubricVersion === request.rubricVersion, 'result: task or rubric binding mismatch')
|
|
287
|
+
scope(result.scope, 'result.scope', checks)
|
|
288
|
+
checks.check(canonical(result.scope) === canonical(request.scope), 'result.scope: request binding mismatch')
|
|
289
|
+
if (closed(result.provider, ['id', 'model'], [], 'result.provider', checks)) {
|
|
290
|
+
checks.check(identifier(result.provider.id), 'result.provider.id: invalid identifier')
|
|
291
|
+
checks.check(text(result.provider.model, 128), 'result.provider.model: expected nonempty model within limit')
|
|
292
|
+
}
|
|
293
|
+
checks.check(result.authority === 'proposal-only', 'result.authority: proposal-only required')
|
|
294
|
+
checks.check(result.mode === 'shadow' || result.mode === 'advisory', 'result.mode: unsupported mode')
|
|
295
|
+
checks.check(typeof result.elapsedMs === 'number' && Number.isFinite(result.elapsedMs) && result.elapsedMs >= 0, 'result.elapsedMs: expected finite nonnegative number')
|
|
296
|
+
if (result.usage !== null && closed(result.usage, ['inputTokens', 'outputTokens'], [], 'result.usage', checks)) {
|
|
297
|
+
checks.check([result.usage.inputTokens, result.usage.outputTokens].every((value) => Number.isSafeInteger(value) && value >= 0), 'result.usage: expected nonnegative safe integers')
|
|
298
|
+
}
|
|
299
|
+
if (!record(result.answers, 0, 64, 'result.answers', checks)) return
|
|
300
|
+
if (result.status === 'assessed') {
|
|
301
|
+
checks.check(!Object.hasOwn(result, 'reason'), 'result.reason: not allowed for assessed result')
|
|
302
|
+
inspectAnswers(request.questions, result.answers, checks, 'result.answers')
|
|
303
|
+
} else if (result.status === 'abstained') {
|
|
304
|
+
checks.check(Object.keys(result.answers).length === 0, 'result.answers: abstention must have no answers')
|
|
305
|
+
checks.check(REASONS.has(result.reason), 'result.reason: unsupported abstention reason')
|
|
306
|
+
} else {
|
|
307
|
+
checks.check(false, 'result.status: unsupported status')
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** Validate integrity and distributions; validity never establishes correctness. */
|
|
312
|
+
export function validateDecisionResult(input, output) {
|
|
313
|
+
const { request, checks } = inspected(input)
|
|
314
|
+
if (checks.errors.length) return { ok: false, errors: ['request: invalid decision request'] }
|
|
315
|
+
try {
|
|
316
|
+
inspectResult(request, jsonSnapshot(output), checks)
|
|
317
|
+
} catch {
|
|
318
|
+
checks.check(false, 'result: expected bounded plain JSON')
|
|
319
|
+
}
|
|
320
|
+
return { ok: checks.errors.length === 0, errors: checks.errors }
|
|
321
|
+
}
|
|
@@ -35,6 +35,19 @@ export function compileDisclosurePatterns(patternDocs = []) {
|
|
|
35
35
|
})
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
export function scanDisclosureText(text, { denylistPatterns = [] } = {}) {
|
|
39
|
+
if (typeof text !== 'string' || Buffer.byteLength(text) > 4 * 1024 * 1024) throw new Error('disclosure text unavailable or exceeds bounds')
|
|
40
|
+
const findings = []
|
|
41
|
+
const patterns = [...STRUCTURAL_DISCLOSURE_PATTERNS, ...denylistPatterns]
|
|
42
|
+
for (const [index,line] of text.split('\n').entries()) {
|
|
43
|
+
for (const {pattern,label} of patterns) {
|
|
44
|
+
pattern.lastIndex = 0
|
|
45
|
+
if (pattern.test(line)) findings.push({label,line:index+1})
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return {ok:findings.length===0,findings}
|
|
49
|
+
}
|
|
50
|
+
|
|
38
51
|
export function scanDisclosureContent({
|
|
39
52
|
root = process.cwd(),
|
|
40
53
|
staged = false,
|
|
@@ -191,8 +191,23 @@ function formatAjvError(error) {
|
|
|
191
191
|
return `${location} ${error.message ?? 'failed schema validation'}`
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
// One compiled validator per schema object, for as long as the schema lives:
|
|
195
|
+
// compilation is a pure function of the schema, and a registered contract is
|
|
196
|
+
// validated many times per run (a large manifest, every journal entry).
|
|
197
|
+
const compiledValidators = new WeakMap()
|
|
198
|
+
|
|
199
|
+
function validatorFor(schema) {
|
|
200
|
+
if (schema === null || typeof schema !== 'object') return ajvForSchema().compile(schema)
|
|
201
|
+
let validate = compiledValidators.get(schema)
|
|
202
|
+
if (!validate) {
|
|
203
|
+
validate = ajvForSchema().compile(schema)
|
|
204
|
+
compiledValidators.set(schema, validate)
|
|
205
|
+
}
|
|
206
|
+
return validate
|
|
207
|
+
}
|
|
208
|
+
|
|
194
209
|
export function validateJsonSchema(schema, doc) {
|
|
195
|
-
const validate =
|
|
210
|
+
const validate = validatorFor(schema)
|
|
196
211
|
if (validate(doc)) return []
|
|
197
212
|
return asArray(validate.errors).map(formatAjvError)
|
|
198
213
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import { BASE_MIGRATIONS } from '../upgrade/upgrade.mjs'
|
|
3
|
+
import { validReviewArtifact } from '../collaboration/review-contracts.mjs'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import { readBoundedSource } from '../readiness-protocols/source-read.mjs'
|
|
6
|
+
|
|
7
|
+
const rootVersion = JSON.parse(
|
|
8
|
+
fs.readFileSync(
|
|
9
|
+
fileURLToPath(new URL('../../package.json', import.meta.url)),
|
|
10
|
+
'utf8',
|
|
11
|
+
),
|
|
12
|
+
).version
|
|
13
|
+
|
|
14
|
+
export function inspectPackLifecycle(project, packs) {
|
|
15
|
+
const declared =
|
|
16
|
+
project.config.ext?.['mnstry.atelier']?.extensionPackLifecycle
|
|
17
|
+
if (!declared)
|
|
18
|
+
return {
|
|
19
|
+
ok: packs.length === 0,
|
|
20
|
+
schema: 'atelier-pack-lifecycle-report@v1',
|
|
21
|
+
rootVersion,
|
|
22
|
+
entries: packs.map((pack) => ({
|
|
23
|
+
id: pack.id,
|
|
24
|
+
status: 'legacy-unqualified',
|
|
25
|
+
admitted: false,
|
|
26
|
+
})),
|
|
27
|
+
errors: packs.length
|
|
28
|
+
? [
|
|
29
|
+
'extension-pack lifecycle declaration required for evidence-bound review',
|
|
30
|
+
]
|
|
31
|
+
: [],
|
|
32
|
+
}
|
|
33
|
+
let policy
|
|
34
|
+
try {
|
|
35
|
+
policy = JSON.parse(readBoundedSource(project.configDir, declared).text)
|
|
36
|
+
} catch {
|
|
37
|
+
return {
|
|
38
|
+
ok: false,
|
|
39
|
+
errors: ['lifecycle declaration unavailable or unsafe'],
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (
|
|
43
|
+
!validReviewArtifact('lifecycle', policy) ||
|
|
44
|
+
policy.schema !== 'atelier-pack-lifecycle@v1' ||
|
|
45
|
+
!Array.isArray(policy.packs) ||
|
|
46
|
+
policy.packs.length > 100 ||
|
|
47
|
+
Object.keys(policy).some(
|
|
48
|
+
(key) => !['schema', 'packs', 'ext', 'contractVersion'].includes(key),
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
return { ok: false, errors: ['invalid lifecycle declaration'] }
|
|
52
|
+
const ids = new Set(),
|
|
53
|
+
errors = []
|
|
54
|
+
for (const entry of policy.packs) {
|
|
55
|
+
if (
|
|
56
|
+
!entry ||
|
|
57
|
+
typeof entry.id !== 'string' ||
|
|
58
|
+
ids.has(entry.id) ||
|
|
59
|
+
!Array.isArray(entry.compatibleRootVersions) ||
|
|
60
|
+
!entry.compatibleRootVersions.length ||
|
|
61
|
+
entry.compatibleRootVersions.some(
|
|
62
|
+
(version) => typeof version !== 'string',
|
|
63
|
+
) ||
|
|
64
|
+
!['active', 'deprecated', 'retired'].includes(entry.status) ||
|
|
65
|
+
typeof entry.version !== 'string' ||
|
|
66
|
+
!/^sha256:[a-f0-9]{64}$/.test(entry.digest ?? '') ||
|
|
67
|
+
Object.keys(entry).some(
|
|
68
|
+
(key) =>
|
|
69
|
+
![
|
|
70
|
+
'id',
|
|
71
|
+
'version',
|
|
72
|
+
'digest',
|
|
73
|
+
'compatibleRootVersions',
|
|
74
|
+
'status',
|
|
75
|
+
'replacement',
|
|
76
|
+
'migrationId',
|
|
77
|
+
'ext',
|
|
78
|
+
].includes(key),
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
errors.push('invalid or duplicate lifecycle pack entry')
|
|
82
|
+
if (
|
|
83
|
+
entry?.migrationId &&
|
|
84
|
+
!BASE_MIGRATIONS.some(
|
|
85
|
+
(migration) =>
|
|
86
|
+
migration.id === entry.migrationId &&
|
|
87
|
+
migration.class === 'extension_pack' &&
|
|
88
|
+
migration.active !== false,
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
errors.push('unknown pack migration; owner migration unavailable')
|
|
92
|
+
ids.add(entry?.id)
|
|
93
|
+
}
|
|
94
|
+
const entries = packs.map((pack) => {
|
|
95
|
+
const entry = policy.packs.find((item) => item?.id === pack.id)
|
|
96
|
+
const admitted = Boolean(
|
|
97
|
+
entry &&
|
|
98
|
+
entry.version === pack.version &&
|
|
99
|
+
entry.digest === pack.digest &&
|
|
100
|
+
entry.compatibleRootVersions?.includes(rootVersion) &&
|
|
101
|
+
entry.status !== 'retired',
|
|
102
|
+
)
|
|
103
|
+
if (!admitted)
|
|
104
|
+
errors.push(`pack ${pack.id} is not qualified for new execution`)
|
|
105
|
+
return {
|
|
106
|
+
id: pack.id,
|
|
107
|
+
status: entry?.status ?? 'legacy-unqualified',
|
|
108
|
+
admitted,
|
|
109
|
+
replacement: entry?.replacement ?? null,
|
|
110
|
+
migrationId: entry?.migrationId ?? null,
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
return {
|
|
114
|
+
ok: errors.length === 0,
|
|
115
|
+
schema: 'atelier-pack-lifecycle-report@v1',
|
|
116
|
+
rootVersion,
|
|
117
|
+
entries,
|
|
118
|
+
errors,
|
|
119
|
+
policy,
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// This report names the existing data-only upgrade operation. It neither
|
|
124
|
+
// changes declarations nor admits a replacement; the source owner applies it.
|
|
125
|
+
export function planPackLifecycleMigration(project, packs) {
|
|
126
|
+
const report = inspectPackLifecycle(project, packs)
|
|
127
|
+
const migration = BASE_MIGRATIONS.find(
|
|
128
|
+
(entry) => entry.class === 'extension_pack' && entry.active !== false,
|
|
129
|
+
)
|
|
130
|
+
return {
|
|
131
|
+
ok: report.ok,
|
|
132
|
+
schema: 'atelier-pack-migration-plan@v1',
|
|
133
|
+
dryRun: true,
|
|
134
|
+
writes: false,
|
|
135
|
+
current: report,
|
|
136
|
+
migration: migration
|
|
137
|
+
? {
|
|
138
|
+
id: migration.id,
|
|
139
|
+
files: migration.files,
|
|
140
|
+
requiredPostChecks: migration.requiredPostChecks,
|
|
141
|
+
authority: migration.authority,
|
|
142
|
+
}
|
|
143
|
+
: null,
|
|
144
|
+
steps: [
|
|
145
|
+
'Retain the prior package, pack declarations, lock and historical records.',
|
|
146
|
+
'Review replacement term and protocol meanings in the source owner workflow.',
|
|
147
|
+
'Update the declared version, digest and exact compatible root versions together.',
|
|
148
|
+
'Use the existing upgrade migration to verify and refresh the lock after owner review.',
|
|
149
|
+
'Create a new evidence-bound run; old runs retain their original meaning.',
|
|
150
|
+
],
|
|
151
|
+
rollback:
|
|
152
|
+
'Restore the prior declarations, package and lock together. Retain contribution and evidence history.',
|
|
153
|
+
}
|
|
154
|
+
}
|