@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,43 @@
|
|
|
1
|
+
export const stateAxes = Object.freeze(['hovered', 'focused', 'pressed', 'selected', 'disabled', 'pending', 'dragging', 'invalid'])
|
|
2
|
+
export const EDIT_CODE_POINT_LIMIT = 32768
|
|
3
|
+
const toneLabels = Object.freeze({ neutral: '', info: 'Information', warning: 'Warning', danger: 'Attention', success: 'Success' })
|
|
4
|
+
export function toneLabel(tone) {
|
|
5
|
+
if (!Object.hasOwn(toneLabels, tone)) throw new TypeError('invalid presentation tone')
|
|
6
|
+
return toneLabels[tone]
|
|
7
|
+
}
|
|
8
|
+
// Delivery is not business success. Describe the settling request's outcome,
|
|
9
|
+
// independently of unrelated failures, and do not hide concurrent deliveries.
|
|
10
|
+
export function deliveryMessage(outcome, pendingCount = 0) {
|
|
11
|
+
if (!['sending', 'delivered', 'failed', 'suppressed'].includes(outcome) || !Number.isSafeInteger(pendingCount) || pendingCount < 0) throw new TypeError('invalid delivery state')
|
|
12
|
+
if (outcome === 'sending') return 'Sending request. Awaiting host state.'
|
|
13
|
+
if (outcome === 'suppressed') return 'Request already pending. No additional request sent.'
|
|
14
|
+
const message = outcome === 'failed' ? 'Request delivery failed. Host state has not been confirmed.' : 'Request delivered. Awaiting host state.'
|
|
15
|
+
return message + (pendingCount ? ' Other requests are still pending.' : '')
|
|
16
|
+
}
|
|
17
|
+
// Match the schema's Unicode code-point limit; never truncate a host draft.
|
|
18
|
+
export function editValueError(value) {
|
|
19
|
+
if (typeof value !== 'string') return 'Draft must be text.'
|
|
20
|
+
let count = 0
|
|
21
|
+
for (const point of value) {
|
|
22
|
+
const code = point.codePointAt(0)
|
|
23
|
+
if (code >= 0xd800 && code <= 0xdfff) return 'Draft contains an incomplete Unicode character.'
|
|
24
|
+
if (++count > EDIT_CODE_POINT_LIMIT) return 'Draft exceeds 32768 characters. It remains in the editor; no request was sent.'
|
|
25
|
+
}
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
export function presentationState(input = {}) {
|
|
29
|
+
if (!input || Object.getPrototypeOf(input) !== Object.prototype) throw new TypeError('invalid state')
|
|
30
|
+
for (const key of Object.keys(input)) if (!stateAxes.includes(key) || typeof input[key] !== 'boolean') throw new TypeError('unknown state axis')
|
|
31
|
+
return Object.freeze(Object.fromEntries(stateAxes.map(key => [key, input[key] ?? false])))
|
|
32
|
+
}
|
|
33
|
+
export function resizeRequest(width, value) {
|
|
34
|
+
if (!width || ![width.min, width.max, width.value, value].every(Number.isFinite) || width.min > width.max || width.value < width.min || width.value > width.max) throw new TypeError('invalid resize range')
|
|
35
|
+
return Math.max(width.min, Math.min(width.max, value))
|
|
36
|
+
}
|
|
37
|
+
export function keyboardResize(width, key, direction = 'ltr') {
|
|
38
|
+
if (!['ltr', 'rtl'].includes(direction)) throw new TypeError('invalid direction')
|
|
39
|
+
if (key === 'Home') return resizeRequest(width, width.min)
|
|
40
|
+
if (key === 'End') return resizeRequest(width, width.max)
|
|
41
|
+
if (!['ArrowLeft', 'ArrowRight'].includes(key)) return null
|
|
42
|
+
return resizeRequest(width, width.value + (key === 'ArrowRight' ? 1 : -1) * (direction === 'rtl' ? -1 : 1))
|
|
43
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { resolveTokens, tokenVariables } from './tokens.mjs'
|
|
2
|
+
|
|
3
|
+
export function presentationStyles(theme = 'light', overrides = {}, rootId) {
|
|
4
|
+
if (rootId !== undefined && !/^[a-z][a-z0-9-]{0,63}$/.test(rootId)) throw new TypeError('invalid style root')
|
|
5
|
+
const tokens = resolveTokens(theme, overrides)
|
|
6
|
+
const scope = rootId === undefined ? '[data-ap-root]' : '[data-ap-root="' + rootId + '"]'
|
|
7
|
+
return scope + '{' + tokenVariables(tokens) + '}\n' + structuralStyles.replaceAll('[data-ap-root]', scope).replaceAll('NARROW_WIDTH', String(tokens.layout.narrow)).replaceAll('HEADING_SCALE', String(tokens.typography.heading / tokens.typography.body)).replaceAll('SMALL_SCALE', String(tokens.typography.small / tokens.typography.body))
|
|
8
|
+
}
|
|
9
|
+
const structuralStyles = String.raw`
|
|
10
|
+
[data-ap-root] { box-sizing:border-box; container-type:inline-size; color:var(--ap-color-text); background:var(--ap-color-page); font-family:var(--ap-typography-family); font-size:var(--ap-typography-body); line-height:var(--ap-typography-line-height); padding:var(--ap-spacing-large); --ap-pad:var(--ap-density-comfortable); }
|
|
11
|
+
[data-ap-root][data-density="compact"] { --ap-pad:var(--ap-density-compact); }
|
|
12
|
+
[data-ap-root] *,[data-ap-root] *::before,[data-ap-root] *::after { box-sizing:border-box; }
|
|
13
|
+
[data-ap-root] [hidden] { display:none !important; }
|
|
14
|
+
[data-ap-root] :is(h1,h2,h3,p,figure,dl) { margin:0 0 var(--ap-spacing-medium); }
|
|
15
|
+
[data-ap-root] h1 { font-size:calc(HEADING_SCALE * 1.3em); }
|
|
16
|
+
[data-ap-root] h2,[data-ap-root] h3 { font-size:calc(HEADING_SCALE * 1em); }
|
|
17
|
+
[data-ap-root] :is(p,li,dd,dt,a,button,label,output) { overflow-wrap:anywhere; }
|
|
18
|
+
[data-ap-root] :is(input,textarea,button) { font:inherit; }
|
|
19
|
+
[data-ap-root] a { color:var(--ap-color-accent); text-underline-offset:.18em; }
|
|
20
|
+
[data-ap-root] :is(button,input,textarea,select,.ap-link) { min-block-size:var(--ap-density-target); min-inline-size:var(--ap-density-target); }
|
|
21
|
+
[data-ap-root] :is(button,.ap-link) { display:inline-flex; align-items:center; justify-content:center; gap:var(--ap-spacing-medium); padding:var(--ap-spacing-medium) var(--ap-spacing-large); color:var(--ap-color-accent); background:var(--ap-color-panel); border:var(--ap-border-width) solid var(--ap-color-border); border-radius:var(--ap-border-radius); cursor:pointer; transition:background-color var(--ap-motion-duration),color var(--ap-motion-duration); }
|
|
22
|
+
[data-ap-root] .ap-link { justify-content:flex-start; text-decoration:underline; border-color:transparent; border-radius:0; background:transparent; }
|
|
23
|
+
[data-ap-root] .ap-skip:not(:focus) { position:absolute; inline-size:1px; block-size:1px; min-inline-size:0; min-block-size:0; padding:0; border:0; overflow:hidden; clip-path:inset(50%); white-space:nowrap; }
|
|
24
|
+
[data-ap-root] .ap-skip:focus { position:fixed; inset-block-start:var(--ap-spacing-large); inset-inline-start:var(--ap-spacing-large); z-index:1; background:var(--ap-color-panel); }
|
|
25
|
+
[data-ap-root] .ap-header { max-inline-size:var(--ap-layout-content); margin-inline:auto; }
|
|
26
|
+
@media (hover:hover) { [data-ap-root] :is(button,.ap-link):not(:disabled):hover { background:var(--ap-color-selected); } }
|
|
27
|
+
[data-ap-root] :is(button,.ap-link):not(:disabled):active { background:var(--ap-color-accent); color:var(--ap-color-on-accent); }
|
|
28
|
+
[data-ap-root] :focus-visible { outline:var(--ap-state-focus-width) solid var(--ap-color-focus); outline-offset:var(--ap-state-focus-offset); }
|
|
29
|
+
[data-ap-root] :disabled { opacity:var(--ap-state-disabled-opacity); cursor:not-allowed; }
|
|
30
|
+
[data-ap-root] [aria-pressed="true"] { background:var(--ap-color-selected); border-width:2px; }
|
|
31
|
+
[data-ap-root] [aria-invalid="true"] { border:2px solid var(--ap-color-danger); }
|
|
32
|
+
[data-ap-root] [data-ap-dragging="true"] { outline:var(--ap-state-focus-width) dashed var(--ap-color-focus); outline-offset:var(--ap-state-focus-offset); }
|
|
33
|
+
[data-ap-root] .ap-error { color:var(--ap-color-danger); }
|
|
34
|
+
[data-ap-root] .ap-muted { color:var(--ap-color-muted); font-size:calc(SMALL_SCALE * 1em); }
|
|
35
|
+
[data-ap-root] .ap-workspace { display:flex; flex-wrap:wrap; gap:var(--ap-spacing-large); max-inline-size:var(--ap-layout-content); margin-inline:auto; align-items:flex-start; }
|
|
36
|
+
[data-ap-root] .ap-pane { flex:var(--ap-pane-weight,1) 1 var(--ap-pane-basis,320px); min-inline-size:min(100%,var(--ap-layout-pane-min)); max-inline-size:100%; padding:var(--ap-pad); background:var(--ap-color-panel); border:var(--ap-border-width) solid var(--ap-color-border); border-radius:var(--ap-border-radius); }
|
|
37
|
+
[data-ap-root] .ap-pane-body { display:grid; gap:var(--ap-spacing-section); }
|
|
38
|
+
[data-ap-root] .ap-pane-header { border-block-end:var(--ap-border-width) solid var(--ap-color-border); margin-block-end:var(--ap-spacing-large); padding-block-end:var(--ap-spacing-medium); }
|
|
39
|
+
[data-ap-root] .ap-block { min-inline-size:0; }
|
|
40
|
+
[data-ap-root] :is(.ap-nav,.ap-actions,.ap-resize) { display:flex; gap:var(--ap-spacing-medium); flex-wrap:wrap; align-items:center; margin-block:var(--ap-spacing-medium); }
|
|
41
|
+
[data-ap-root] .ap-resize input { flex:1; max-inline-size:100%; }
|
|
42
|
+
[data-ap-root] input:not([type=range]),[data-ap-root] textarea { display:block; inline-size:100%; padding:var(--ap-spacing-medium); color:var(--ap-color-text); background:var(--ap-color-panel); border:var(--ap-border-width) solid var(--ap-color-border); border-radius:var(--ap-border-radius); }
|
|
43
|
+
[data-ap-root] input[aria-invalid="true"],[data-ap-root] textarea[aria-invalid="true"] { border:2px solid var(--ap-color-danger); }
|
|
44
|
+
[data-ap-root] textarea { min-block-size:10rem; resize:vertical; }
|
|
45
|
+
[data-ap-root] label { display:block; }
|
|
46
|
+
[data-ap-root] :is(ul,ol) { padding-inline-start:var(--ap-spacing-section); }
|
|
47
|
+
[data-ap-root] li { margin-block:var(--ap-spacing-medium); }
|
|
48
|
+
[data-ap-root] dt { font-weight:600; }
|
|
49
|
+
[data-ap-root] dd { margin-inline-start:0; }
|
|
50
|
+
[data-ap-root] table { border-collapse:collapse; inline-size:100%; table-layout:fixed; }
|
|
51
|
+
[data-ap-root] :is(th,td) { text-align:start; border-block-end:var(--ap-border-width) solid var(--ap-color-border); padding:var(--ap-spacing-medium); overflow-wrap:anywhere; }
|
|
52
|
+
[data-ap-root] pre { white-space:pre-wrap; overflow-wrap:anywhere; font-family:monospace; padding:var(--ap-spacing-medium); border:var(--ap-border-width) solid var(--ap-color-border); }
|
|
53
|
+
[data-ap-root] .ap-diff { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--ap-spacing-large); }
|
|
54
|
+
[data-ap-root] img { max-inline-size:100%; block-size:auto; }
|
|
55
|
+
[data-ap-root] .ap-notice { border-inline-start:4px solid var(--ap-color-border); padding-inline-start:var(--ap-spacing-large); }
|
|
56
|
+
[data-ap-root] .ap-tone { font-weight:600; }
|
|
57
|
+
[data-ap-root] [data-tone=info],[data-ap-root] [data-tone=warning] { border-color:var(--ap-color-accent); }
|
|
58
|
+
[data-ap-root] [data-tone=warning] { border-inline-start-style:dashed; }
|
|
59
|
+
[data-ap-root] [data-tone=danger] { border-color:var(--ap-color-danger); }
|
|
60
|
+
[data-ap-root] [data-tone=success] { border-color:var(--ap-color-success); }
|
|
61
|
+
[data-ap-root] dialog { max-inline-size:min(36rem,calc(100vw - 2rem)); max-block-size:calc(100dvh - 2rem); overflow:auto; color:var(--ap-color-text); background:var(--ap-color-panel); padding:var(--ap-spacing-section); border:var(--ap-border-width) solid var(--ap-color-border); border-radius:var(--ap-border-radius); box-shadow:0 var(--ap-elevation-offset) var(--ap-elevation-blur) rgb(0 0 0 / var(--ap-elevation-opacity)); }
|
|
62
|
+
[data-ap-root] dialog::backdrop { background:rgb(0 0 0 / .6); }
|
|
63
|
+
@container (max-width:NARROW_WIDTHpx) { [data-ap-root] .ap-workspace { flex-direction:column; } [data-ap-root] .ap-pane { flex:none; inline-size:100%; } [data-ap-root] .ap-resize { display:none; } [data-ap-root] .ap-diff { grid-template-columns:1fr; } }
|
|
64
|
+
@media (prefers-reduced-motion:reduce) { [data-ap-root] *,[data-ap-root] *::before,[data-ap-root] *::after { transition:none !important; animation:none !important; scroll-behavior:auto !important; } }
|
|
65
|
+
@media (forced-colors:active) { [data-ap-root] :focus-visible { outline-color:Highlight; } [data-ap-root] [aria-pressed=true] { border-color:Highlight; } }
|
|
66
|
+
@media print { [data-ap-root] { --ap-color-text:#000;--ap-color-page:#fff;--ap-color-panel:#fff;--ap-color-muted:#333;--ap-color-selected:#fff;--ap-color-accent:#000;--ap-color-danger:#000;--ap-color-success:#000; color:#000;background:#fff;padding:0; } [data-ap-root] :is(.ap-skip,.ap-nav,.ap-resize,button,dialog) { display:none !important; } [data-ap-root] .ap-workspace { display:block; } [data-ap-root] .ap-pane { border:0;break-inside:auto; } [data-ap-root] a { color:#000; } }
|
|
67
|
+
`
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const freeze = value => {
|
|
2
|
+
Object.values(value).forEach(v => { if (v && typeof v === 'object') freeze(v) })
|
|
3
|
+
return Object.freeze(value)
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export const tokenContract = freeze({
|
|
7
|
+
color: { page: '#f5f6f8', panel: '#ffffff', text: '#18202b', muted: '#495668', accent: '#164fa3', onAccent: '#ffffff', border: '#718096', selected: '#e1ecff', danger: '#a51d32', success: '#21633d', focus: '#164fa3' },
|
|
8
|
+
typography: { body: 16, small: 14, heading: 22, lineHeight: 1.5, family: 'sans-serif' },
|
|
9
|
+
spacing: { small: 4, medium: 8, large: 16, section: 24 },
|
|
10
|
+
layout: { content: 1280, narrow: 768, paneMin: 240 },
|
|
11
|
+
density: { comfortable: 12, compact: 8, target: 44 },
|
|
12
|
+
elevation: { offset: 4, blur: 16, opacity: 0.15 },
|
|
13
|
+
border: { width: 1, radius: 8 },
|
|
14
|
+
motion: { duration: 120 },
|
|
15
|
+
state: { focusWidth: 3, focusOffset: 3, disabledOpacity: 0.7 },
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const dark = { page: '#141a23', panel: '#202938', text: '#f3f6fb', muted: '#c0cbda', accent: '#a4c6ff', onAccent: '#12213b', border: '#8b9bb1', selected: '#304566', danger: '#ffacb9', success: '#9cdfb1', focus: '#a4c6ff' }
|
|
19
|
+
const range = {
|
|
20
|
+
'typography.body': [16, 32], 'typography.small': [14, 32], 'typography.heading': [18, 48], 'typography.lineHeight': [1.4, 2],
|
|
21
|
+
'spacing.small': [2, 16], 'spacing.medium': [4, 32], 'spacing.large': [8, 64], 'spacing.section': [16, 96],
|
|
22
|
+
'layout.content': [320, 2400], 'layout.narrow': [480, 1200], 'layout.paneMin': [160, 480],
|
|
23
|
+
'density.comfortable': [8, 24], 'density.compact': [4, 16], 'density.target': [44, 64],
|
|
24
|
+
'elevation.offset': [0, 16], 'elevation.blur': [0, 40], 'elevation.opacity': [0, 0.4],
|
|
25
|
+
'border.width': [1, 4], 'border.radius': [0, 24], 'motion.duration': [0, 250],
|
|
26
|
+
'state.focusWidth': [3, 6], 'state.focusOffset': [2, 6], 'state.disabledOpacity': [0.7, 1],
|
|
27
|
+
}
|
|
28
|
+
export function contrastRatio(a, b) {
|
|
29
|
+
const lum = hex => {
|
|
30
|
+
const c = hex.slice(1).match(/../g).map(x => parseInt(x, 16) / 255).map(x => x <= .04045 ? x / 12.92 : ((x + .055) / 1.055) ** 2.4)
|
|
31
|
+
return c[0] * .2126 + c[1] * .7152 + c[2] * .0722
|
|
32
|
+
}
|
|
33
|
+
const x = lum(a), y = lum(b)
|
|
34
|
+
return (Math.max(x, y) + .05) / (Math.min(x, y) + .05)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function resolveTokens(theme = 'light', overrides = {}) {
|
|
38
|
+
if (!['light', 'dark'].includes(theme)) throw new TypeError('unknown theme')
|
|
39
|
+
if (!overrides || Object.getPrototypeOf(overrides) !== Object.prototype) throw new TypeError('invalid token overrides')
|
|
40
|
+
const result = structuredClone(tokenContract)
|
|
41
|
+
if (theme === 'dark') result.color = { ...dark }
|
|
42
|
+
for (const [group, values] of Object.entries(overrides)) {
|
|
43
|
+
if (!Object.hasOwn(result, group) || !values || Object.getPrototypeOf(values) !== Object.prototype) throw new TypeError('unknown token group')
|
|
44
|
+
for (const [key, value] of Object.entries(values)) {
|
|
45
|
+
if (!Object.hasOwn(result[group], key)) throw new TypeError('unknown token')
|
|
46
|
+
const bounds = range[group + '.' + key]
|
|
47
|
+
const valid = group === 'color' ? typeof value === 'string' && /^#[0-9a-f]{6}$/i.test(value)
|
|
48
|
+
: key === 'family' ? ['sans-serif', 'serif', 'monospace'].includes(value)
|
|
49
|
+
: bounds && typeof value === 'number' && Number.isFinite(value) && value >= bounds[0] && value <= bounds[1]
|
|
50
|
+
if (!valid) throw new TypeError('invalid token value')
|
|
51
|
+
result[group][key] = value
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
for (const background of ['page', 'panel', 'selected']) {
|
|
55
|
+
for (const foreground of ['text', 'muted', 'accent', 'danger', 'success']) {
|
|
56
|
+
if (contrastRatio(result.color[foreground], result.color[background]) < 4.5) throw new TypeError('text token contrast floor')
|
|
57
|
+
}
|
|
58
|
+
for (const foreground of ['border', 'focus']) {
|
|
59
|
+
if (contrastRatio(result.color[foreground], result.color[background]) < 3) throw new TypeError('boundary token contrast floor')
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (contrastRatio(result.color.onAccent, result.color.accent) < 4.5) throw new TypeError('accent label contrast floor')
|
|
63
|
+
return freeze(result)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function tokenVariables(tokens) {
|
|
67
|
+
const checked = resolveTokens('light', tokens)
|
|
68
|
+
const unitless = new Set(['typography.lineHeight', 'elevation.opacity', 'state.disabledOpacity'])
|
|
69
|
+
return Object.entries(checked).flatMap(([group, values]) => Object.entries(values).map(([key, value]) => {
|
|
70
|
+
const name = (group + '-' + key).replace(/[A-Z]/g, c => '-' + c.toLowerCase())
|
|
71
|
+
const suffix = typeof value === 'number' ? unitless.has(group + '.' + key) ? '' : group === 'motion' ? 'ms' : 'px' : ''
|
|
72
|
+
return '--ap-' + name + ':' + value + suffix
|
|
73
|
+
})).join(';')
|
|
74
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { htmlEscape as escape } from '../html-primitives.mjs'
|
|
2
|
+
import { assertPresentation } from './contract.mjs'
|
|
3
|
+
import { presentationStyles } from './styles.mjs'
|
|
4
|
+
import { toneLabel } from './state.mjs'
|
|
5
|
+
|
|
6
|
+
const domId = (model, kind, id) => escape(model.id + ':' + kind + ':' + id)
|
|
7
|
+
const noticeTypes = new Set(['status', 'refusal', 'decision', 'receipt', 'offer', 'review', 'publication'])
|
|
8
|
+
|
|
9
|
+
export function renderPresentation(model, { tokenOverrides = {}, interactive = true } = {}) {
|
|
10
|
+
assertPresentation(model)
|
|
11
|
+
const nodes = new Map(model.nodes.map(node => [node.id, node]))
|
|
12
|
+
const renderAction = node => {
|
|
13
|
+
if (!interactive) return '<p>' + escape(node.label) + ' (host action; unavailable in document)' + (node.reason ? ': ' + escape(node.reason) : '') + '</p>'
|
|
14
|
+
const name = domId(model, 'node', node.id)
|
|
15
|
+
const reason = node.reason ? '<p class="ap-muted" id="' + name + ':reason">' + escape(node.reason) + '</p>' : ''
|
|
16
|
+
return '<div class="ap-action"><button type="button" id="' + name + '" data-ap-action="' + escape(node.id) + '"' +
|
|
17
|
+
(node.disabled ? ' disabled' : '') + (node.reason ? ' aria-describedby="' + name + ':reason"' : '') +
|
|
18
|
+
(node.shortcut ? ' aria-keyshortcuts="' + escape(node.shortcut.aria) + '"' : '') + '>' +
|
|
19
|
+
escape(node.label) + (node.shortcut ? ' <kbd aria-hidden="true">' + escape(node.shortcut.display) + '</kbd>' : '') +
|
|
20
|
+
'</button>' + reason + '</div>'
|
|
21
|
+
}
|
|
22
|
+
const renderNode = node => {
|
|
23
|
+
const name = domId(model, 'node', node.id), labelId = name + ':label'
|
|
24
|
+
const heading = '<h3 id="' + labelId + '">' + escape(node.label) + '</h3>'
|
|
25
|
+
let body
|
|
26
|
+
if (node.type === 'action') return renderAction(node)
|
|
27
|
+
if (node.type === 'text') body = '<p>' + escape(node.text) + '</p>'
|
|
28
|
+
if (node.type === 'preview') body = '<pre>' + escape(node.text) + '</pre>'
|
|
29
|
+
if (node.type === 'diff') body = '<div class="ap-diff"><div><h4>' + escape(node.beforeLabel) + '</h4><pre>' + escape(node.before) + '</pre></div><div><h4>' + escape(node.afterLabel) + '</h4><pre>' + escape(node.after) + '</pre></div></div>'
|
|
30
|
+
if (node.type === 'media') body = '<figure><img src="' + escape(node.src) + '" alt="' + escape(node.alt) + '" loading="lazy">' + (node.caption ? '<figcaption>' + escape(node.caption) + '</figcaption>' : '') + '</figure>'
|
|
31
|
+
if (node.items) {
|
|
32
|
+
const tag = node.type === 'sequence' ? 'ol' : 'ul'
|
|
33
|
+
body = '<' + tag + '>' + node.items.map((item, index) => '<li' + (interactive && node.reorderable ? ' draggable="true" data-ap-drag="' + escape(node.id) + '" data-ap-item="' + escape(item.id) + '"' : '') + '>' +
|
|
34
|
+
(!interactive ? '<span>' + escape(item.label) + (item.selected ? ' (selected)' : '') + '</span>' : item.href ? '<a class="ap-link" href="' + escape(item.href) + '">' + escape(item.label) + '</a>' :
|
|
35
|
+
'<button type="button" data-ap-select="' + escape(node.id) + '" data-ap-item="' + escape(item.id) + '"' +
|
|
36
|
+
(typeof item.selected === 'boolean' ? ' aria-pressed="' + item.selected + '"' : '') + '>' + escape(item.label) + '</button>') +
|
|
37
|
+
(item.detail ? '<p class="ap-muted">' + escape(item.detail) + '</p>' : '') +
|
|
38
|
+
(interactive && node.reorderable ? '<div class="ap-actions"><button type="button" data-ap-move="' + escape(node.id) + '" data-ap-item="' + escape(item.id) + '" data-ap-position="' + (index - 1) + '"' + (index === 0 ? ' disabled' : '') + ' aria-label="Move ' + escape(item.label) + ' earlier">Move earlier</button><button type="button" data-ap-move="' + escape(node.id) + '" data-ap-item="' + escape(item.id) + '" data-ap-position="' + (index + 1) + '"' + (index === node.items.length - 1 ? ' disabled' : '') + ' aria-label="Move ' + escape(item.label) + ' later">Move later</button></div>' : '') + '</li>').join('') + '</' + tag + '>'
|
|
39
|
+
if (node.type === 'graph') {
|
|
40
|
+
const labels = new Map(node.items.map(item => [item.id, item.label]))
|
|
41
|
+
body += '<table><caption>' + escape(node.label) + ' relationships</caption><thead><tr><th scope="col">From</th><th scope="col">Relationship</th><th scope="col">To</th></tr></thead><tbody>' +
|
|
42
|
+
node.edges.map(edge => '<tr><td>' + escape(labels.get(edge.from)) + '</td><td>' + escape(edge.label) + '</td><td>' + escape(labels.get(edge.to)) + '</td></tr>').join('') + '</tbody></table>'
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (noticeTypes.has(node.type)) body = '<div class="ap-notice" data-tone="' + escape(node.tone) + '">' + (toneLabel(node.tone) ? '<p class="ap-tone">' + toneLabel(node.tone) + '</p>' : '') + '<p' + (node.type === 'status' ? ' role="status"' : '') + '>' + escape(node.text) + '</p><dl>' +
|
|
46
|
+
node.details.map(entry => '<dt>' + escape(entry.label) + '</dt><dd>' + escape(entry.value) + '</dd>').join('') + '</dl><div class="ap-actions">' + node.actions.map(ref => renderAction(nodes.get(ref))).join('') + '</div></div>'
|
|
47
|
+
if (node.type === 'field' || node.type === 'editor') {
|
|
48
|
+
if (!interactive) return '<section class="ap-block" id="' + name + '">' + heading + '<pre>' + escape(node.value) + '</pre>' + (node.error ? '<p>' + escape(node.error) + '</p>' : '') + '</section>'
|
|
49
|
+
const fieldId = name + ':input'
|
|
50
|
+
const attrs = ' id="' + fieldId + '" data-ap-edit="' + escape(node.id) + '"' +
|
|
51
|
+
(node.disabled ? ' disabled' : '') + (node.required ? ' required' : '') +
|
|
52
|
+
(node.error ? ' aria-invalid="true" aria-describedby="' + name + ':error"' : '')
|
|
53
|
+
const field = node.type === 'editor' ? '<textarea' + attrs + '>' + escape(node.value) + '</textarea>'
|
|
54
|
+
: '<input type="' + escape(node.input) + '"' + attrs + ' value="' + escape(node.value) + '">'
|
|
55
|
+
return '<div class="ap-block" id="' + name + '"><label for="' + fieldId + '">' + escape(node.label) + (node.required ? ' (required)' : '') + '</label>' + field +
|
|
56
|
+
(node.error ? '<p class="ap-error" id="' + name + ':error">' + escape(node.error) + '</p>' : '') +
|
|
57
|
+
'<p class="ap-error" data-ap-edit-error="' + escape(node.id) + '" id="' + fieldId + ':limit" aria-live="polite" hidden></p></div>'
|
|
58
|
+
}
|
|
59
|
+
return '<section class="ap-block" id="' + name + '" aria-labelledby="' + labelId + '">' + heading + body + '</section>'
|
|
60
|
+
}
|
|
61
|
+
const primary = model.panes.find(pane => pane.role === 'primary')
|
|
62
|
+
const totalWeight = model.panes.reduce((sum, pane) => sum + (pane.width?.value ?? 50), 0)
|
|
63
|
+
const nav = model.navigation.map(item => '<a class="ap-link" href="#' + domId(model, 'pane', item.target) + '">' + escape(item.label) + '</a>').join('')
|
|
64
|
+
const panes = model.panes.map(pane => {
|
|
65
|
+
const weight = pane.width?.value ?? 50, share = weight / totalWeight
|
|
66
|
+
const name = domId(model, 'pane', pane.id), heading = name + ':label'
|
|
67
|
+
const resize = interactive && pane.width ? '<div class="ap-resize"><label for="' + name + ':resize">Width of ' + escape(pane.label) + '</label><input id="' + name + ':resize" type="range" data-ap-resize="' + escape(pane.id) + '" min="' + pane.width.min + '" max="' + pane.width.max + '" value="' + pane.width.value + '" aria-controls="' + name + '"><button type="button" data-ap-step="' + escape(pane.id) + '" data-ap-delta="-1" aria-label="Decrease width of ' + escape(pane.label) + '">−</button><button type="button" data-ap-step="' + escape(pane.id) + '" data-ap-delta="1" aria-label="Increase width of ' + escape(pane.label) + '">+</button></div>' : ''
|
|
68
|
+
return '<section class="ap-pane" id="' + name + '" tabindex="-1" aria-labelledby="' + heading + '" data-ap-pane="' + escape(pane.id) + '"' +
|
|
69
|
+
' style="--ap-pane-weight:' + weight + ';--ap-pane-basis:calc(' + (share * 100) + '% - var(--ap-spacing-large) * ' + ((model.panes.length - 1) * share) + ')"' + '><header class="ap-pane-header"><h2 id="' + heading + '">' + escape(pane.label) + '</h2>' + resize + '</header><div class="ap-pane-body">' +
|
|
70
|
+
pane.blocks.map(ref => renderNode(nodes.get(ref))).join('') + '</div></section>'
|
|
71
|
+
}).join('')
|
|
72
|
+
return '<div data-ap-root="' + escape(model.id) + '" data-density="' + escape(model.density) + '" dir="' + escape(model.direction) + '" lang="' + escape(model.lang) + '"><style>' + presentationStyles(model.theme, tokenOverrides, model.id) + '</style>' +
|
|
73
|
+
'<a class="ap-link ap-skip" href="#' + domId(model, 'pane', primary.id) + '">Skip to ' + escape(primary.label) + '</a><header class="ap-header"><h1>' + escape(model.title) + '</h1><nav class="ap-nav" aria-label="Workspace">' + nav + '</nav></header>' +
|
|
74
|
+
'<main class="ap-workspace">' + panes + '</main><output data-ap-delivery aria-live="polite"></output>' +
|
|
75
|
+
(interactive ? '<dialog data-ap-confirm aria-labelledby="' + escape(model.id) + ':confirm:title" aria-describedby="' + escape(model.id) + ':confirm:description"><h2 id="' + escape(model.id) + ':confirm:title" data-ap-confirm-title></h2><p id="' + escape(model.id) + ':confirm:description" data-ap-confirm-description></p><div class="ap-actions"><button type="button" data-ap-cancel autofocus>Cancel</button><button type="button" data-ap-confirm-action>Continue</button></div></dialog>' : '') + '</div>'
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function renderPresentationDocument(model, options = {}) {
|
|
79
|
+
assertPresentation(model)
|
|
80
|
+
return '<!doctype html><html lang="' + escape(model.lang) + '" dir="' + escape(model.direction) + '"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>' + escape(model.title) + '</title></head><body>' + renderPresentation(model, options) + '</body></html>'
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function renderReadOnlyDocument(model, options = {}) {
|
|
84
|
+
return renderPresentationDocument(model, { ...options, interactive: false })
|
|
85
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function renderReviewPage() {
|
|
2
|
+
return `<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Review workspace</title>
|
|
3
|
+
<style>body{font:18px/1.6 system-ui;max-width:850px;margin:3rem auto;padding:0 1.5rem;color:#202524;background:#faf9f5}h1,h2{line-height:1.2}label{display:block;margin-top:1rem}input,textarea,select,button{font:inherit;padding:.5rem;max-width:100%;box-sizing:border-box}textarea{width:100%;min-height:7rem}button{cursor:pointer;margin:.5rem .5rem .5rem 0}button:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible{outline:3px solid #1c695c;outline-offset:3px}pre{white-space:pre-wrap;overflow-wrap:anywhere;background:#eee;padding:1rem}article{border-top:1px solid #b8b8af;padding:1rem 0}.note{color:#49534e}#status{position:sticky;top:0;background:#faf9f5;padding:.5rem;border-bottom:1px solid #aaa}</style>
|
|
4
|
+
<h1>Review workspace</h1><p class="note">Questions and corrections stay yours. Decisions record local judgment; source changes require a separate owner action.</p>
|
|
5
|
+
<p id="status" role="status" aria-live="polite">Ready to open a document or a run.</p>
|
|
6
|
+
<label>Your name <input id="reviewer" autocomplete="name"></label><p class="note">Recorded as locally asserted identity.</p>
|
|
7
|
+
<h2>Read and respond</h2><form id="open-document"><label>Repository <input id="repo" required></label><label>Document path <input id="path" required placeholder="README.md"></label><button>Open document</button></form>
|
|
8
|
+
<pre id="document" tabindex="0" aria-label="Source document"></pre>
|
|
9
|
+
<label>Passage (copy its exact wording)<textarea id="anchor"></textarea></label>
|
|
10
|
+
<label>Response type<select id="response-type"><option value="question">Question</option><option value="correction">Correction</option><option value="discussion">Discuss</option></select></label>
|
|
11
|
+
<label>Your response<textarea id="wording"></textarea></label><button id="save-response">Save response</button><button id="save-position">Save reading position</button>
|
|
12
|
+
<h2>Review a run</h2><form id="open-run"><label>Run ID <input id="run-id" required></label><button>Open run</button></form><div id="run"></div>
|
|
13
|
+
<h2>Recorded contributions</h2><p>Viewing a document does not record agreement. Old responses remain attached to their original revision.</p><div id="history"></div>
|
|
14
|
+
<script type="module">
|
|
15
|
+
const el=id=>document.getElementById(id);let nonce='',doc=null,run=null,history=[],pending=null,responseTarget='response-'+crypto.randomUUID();
|
|
16
|
+
const drafts=new Map();
|
|
17
|
+
const status=message=>el('status').textContent=message;
|
|
18
|
+
async function request(url,body){if(!url.startsWith('/api/review/'))throw new Error('Invalid local route');
|
|
19
|
+
const options=body?{method:'POST',headers:{'Content-Type':'application/json','X-Atelier-Nonce':nonce},body:JSON.stringify(body)}:{};
|
|
20
|
+
const response=await fetch('/api/review/'+url.slice('/api/review/'.length),options);const result=await response.json();if(!response.ok||!result.ok)throw new Error(result.error||'Request failed');return result}
|
|
21
|
+
async function auth(){nonce=(await request('/api/review/session',{})).mutationNonce}
|
|
22
|
+
function para(parent,text){const p=document.createElement('p');p.textContent=text;parent.append(p);return p}
|
|
23
|
+
async function refresh(){history=(await request('/api/review/records')).records;el('history').replaceChildren();for(const record of history){const a=document.createElement('article');para(a,record.input.kind+' · asserted by '+record.identity.name+' · '+record.recordedAt);para(a,record.input.wording||record.input.rationale||'Reading position saved');para(a,'Revision: '+(record.input.documentDigest||record.input.evidenceDigest));if(record.input.anchor)para(a,record.input.anchor);if(record.input.kind==='position'){const resume=document.createElement('button');resume.textContent='Resume '+record.input.repo+'/'+record.input.path;resume.onclick=()=>{el('repo').value=record.input.repo;el('path').value=record.input.path;el('open-document').requestSubmit()};a.append(resume)}if(record.input.kind==='decision'){para(a,'Decision: '+record.input.decision+' · claim '+record.input.targetId+' · version '+record.version);if(record.input.revision)para(a,'Proposed successor wording: '+record.input.revision);if(record.input.decision==='accepted'){const button=document.createElement('button');button.textContent='Inspect owner handoff';button.onclick=async()=>{try{const result=await request('/api/review/handoff?'+new URLSearchParams({id:record.input.requestId}));const pre=document.createElement('pre');pre.textContent=JSON.stringify(result,null,2);a.append(pre)}catch(error){status(error.message)}};a.append(button)}}el('history').append(a)}}
|
|
24
|
+
function version(kind,targetId,extra){return history.filter(r=>r.input.kind===kind&&r.input.targetId===targetId&&(r.input.runId||null)===(extra.runId||null)&&(r.input.repo||null)===(extra.repo||null)&&(r.input.path||null)===(extra.path||null)&&(kind!=='position'||r.input.reviewer===el('reviewer').value.trim())).length}
|
|
25
|
+
async function save(kind,targetId,extra){const input={kind,targetId,expectedVersion:version(kind,targetId,extra),reviewer:el('reviewer').value.trim(),...extra};const fingerprint=JSON.stringify(input);if(!pending||pending.fingerprint!==fingerprint)pending={fingerprint,input:{...input,requestId:crypto.randomUUID()}};const attempt=pending;status('Saving…');try{await auth();await request('/api/review/contributions',attempt.input);if(pending===attempt)pending=null;status('Saved to local history.');await refresh().catch(()=>status('Saved; history refresh unavailable.'));return true}catch(error){const recovery=error.message.includes('stale contribution version')?'Reopen the run to inspect newer decisions before saving again.':error.message.includes('document changed')?'Reopen the document and explicitly select a passage in its new revision.':error.message.includes('claim evidence changed')?'Create a new run against current evidence before deciding.':'Retry with Save after the active writer or service is available.';status('Not confirmed saved: '+error.message+'. Your wording remains here. '+recovery)}}
|
|
26
|
+
el('open-document').onsubmit=async event=>{event.preventDefault();try{const repo=el('repo').value,path=el('path').value;const loaded=await request('/api/review/document?'+new URLSearchParams({repo,path}));doc={repo,path,...loaded};el('document').textContent=doc.text;await refresh();const positions=history.filter(r=>r.input.kind==='position'&&r.input.repo===repo&&r.input.path===path&&r.input.documentDigest===doc.digest&&r.input.reviewer===el('reviewer').value.trim());const last=positions.at(-1);if(last){el('anchor').value=last.input.anchor;const range=document.createRange();const node=el('document').firstChild;range.setStart(node,Math.min(last.input.position,node.length));range.collapse(true);const selection=window.getSelection();selection.removeAllRanges();selection.addRange(range);el('document').focus();const rect=range.getBoundingClientRect();if(rect.height)window.scrollBy(0,rect.top-window.innerHeight/3)}status('Document opened. Previous contributions are listed below.')}catch(error){status(error.message)}};
|
|
27
|
+
el('save-response').onclick=async()=>{if(!doc)return status('Open a document first.');if(await save('response',responseTarget,{repo:doc.repo,path:doc.path,documentDigest:doc.digest,anchor:el('anchor').value,responseType:el('response-type').value,wording:el('wording').value})){el('wording').value='';responseTarget='response-'+crypto.randomUUID()}};
|
|
28
|
+
el('save-position').onclick=()=>{if(!doc)return status('Open a document first.');const anchor=el('anchor').value;return save('position','reading-position',{repo:doc.repo,path:doc.path,documentDigest:doc.digest,anchor,position:Math.max(0,doc.text.indexOf(anchor))})};
|
|
29
|
+
el('open-run').onsubmit=async event=>{event.preventDefault();try{run=await request('/api/review/run?'+new URLSearchParams({id:el('run-id').value}));await refresh();el('run').replaceChildren();para(el('run'),'Answer completeness: '+run.run.score+'%. This is not evidence confidence or runtime readiness.');para(el('run'),run.current?'Evidence currently matches.':'Historical evidence: current acceptance unavailable.');for(const [index,claim] of run.run.claims.entries()){const a=document.createElement('article');para(a,claim.subject+' — '+claim.predicate+' — '+claim.object);para(a,'Claim ID: '+claim.claimId);para(a,claim.notes.join(' '));const mapped=run.snapshot.claimEvidence[index];para(a,'Mapped evidence: '+mapped.status);for(const source of mapped.resolved){para(a,source.id+' · '+source.repo+'/'+source.path);para(a,source.excerpt)}if(mapped.unresolved.length)para(a,'Unresolved references: '+mapped.unresolved.join(', '));para(a,'Evidence: operator answers and the recorded source inventory. Source presence alone does not substantiate this relationship.');const details=document.createElement('details');const summary=document.createElement('summary');summary.textContent='Inspect recorded inputs and source identities';details.append(summary);const pre=document.createElement('pre');pre.textContent=JSON.stringify({mapping:mapped,answers:run.snapshot.answers,sources:run.snapshot.sources,limitations:run.snapshot.limitations},null,2);details.append(pre);a.append(details);para(a,'Acceptance records your judgment and prepares an owner handoff. It does not apply, publish or promote the claim.');const label=document.createElement('label');label.textContent='Reason or proposed revision';const reason=document.createElement('textarea');const draftKey=run.run.runId+':'+claim.claimId;reason.value=drafts.get(draftKey)||'';reason.oninput=()=>drafts.set(draftKey,reason.value);label.append(reason);a.append(label);for(const decision of ['accepted','rejected','revised']){const button=document.createElement('button');button.textContent={accepted:'Accept',rejected:'Reject',revised:'Propose revision'}[decision];button.disabled=!run.current||(decision==='accepted'&&(mapped.status!=='linked-source'||run.run.blockers.length>0));button.onclick=()=>save('decision',claim.claimId,{runId:run.run.runId,evidenceDigest:run.snapshot.digest,decision,rationale:reason.value,...(decision==='revised'?{revision:reason.value}:{})});a.append(button)}el('run').append(a)}}catch(error){status(error.message)}};
|
|
30
|
+
refresh().catch(error=>status('History unavailable: '+error.message));
|
|
31
|
+
</script></html>`
|
|
32
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Quote data as Markdown text, including control characters in path names.
|
|
2
|
+
const quote = (value) => JSON.stringify(value)
|
|
3
|
+
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
4
|
+
.replace(/[\\`*_{}\[\]()#+.!|]/g, '\\$&')
|
|
5
|
+
|
|
6
|
+
export function renderUpgradeExplanation(report) {
|
|
7
|
+
return [
|
|
8
|
+
'# Atelier upgrade for review',
|
|
9
|
+
'',
|
|
10
|
+
`Plan: ${quote(report.planDigest)}`,
|
|
11
|
+
`Source commit: ${quote(report.baseHead)}`,
|
|
12
|
+
`Candidate branch: ${quote(report.branch)}`,
|
|
13
|
+
`Expires: ${quote(report.expiresAt)}`,
|
|
14
|
+
'',
|
|
15
|
+
report.bindingsCurrent ? 'The saved plan matches the observed workspace. This report does not record approval.' : 'The saved plan cannot currently be applied:',
|
|
16
|
+
...report.blockers.map((reason) => `- ${quote(reason)}`),
|
|
17
|
+
'',
|
|
18
|
+
'## Proposed changes',
|
|
19
|
+
...report.writes.map((write) => `- ${quote(write.action)} ${quote(write.path)} (${quote(write.owner)}): ${quote(write.before?.digest ?? 'absent')} → ${quote(write.after.digest)}; mode ${quote(write.after.mode)}.`),
|
|
20
|
+
'',
|
|
21
|
+
'## Consent and outcome',
|
|
22
|
+
'Approval covers these prepared lock/projection bytes and a local Git commit through existing hooks. The source branch remains unchanged.',
|
|
23
|
+
'Package installation, merge, publication and service activation require their own named authority. This report does not authorize any action.',
|
|
24
|
+
'The digest identifies content; the agent must obtain and retain the owner decision. The CLI does not authenticate human approval.',
|
|
25
|
+
'',
|
|
26
|
+
`Recovery: ${report.recovery}.`,
|
|
27
|
+
...report.caveats.map((note) => `- ${note}`),
|
|
28
|
+
'',
|
|
29
|
+
`Installed executor evidence: ${quote(report.provenance.executorDigest)}`,
|
|
30
|
+
`Source inventory: ${report.provenance.sourceFiles} files. Upstream release authentication: not supplied by this transaction.`,
|
|
31
|
+
'',
|
|
32
|
+
].join('\n')
|
|
33
|
+
}
|