approval-md 0.0.1 → 0.1.0
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/LICENSE +176 -0
- package/NOTICE +5 -0
- package/README.md +909 -4
- package/SPEC.md +445 -32
- package/cli.js +29 -3
- package/dist/src/adapters/agentmail.js +1200 -0
- package/dist/src/adapters/agentmail.js.map +1 -0
- package/dist/src/adapters/conformance.js +461 -0
- package/dist/src/adapters/conformance.js.map +1 -0
- package/dist/src/adapters/contract.js +941 -0
- package/dist/src/adapters/contract.js.map +1 -0
- package/dist/src/adapters/email.js +749 -0
- package/dist/src/adapters/email.js.map +1 -0
- package/dist/src/adapters/env-passphrase.js +132 -0
- package/dist/src/adapters/env-passphrase.js.map +1 -0
- package/dist/src/adapters/registry.js +76 -0
- package/dist/src/adapters/registry.js.map +1 -0
- package/dist/src/adapters/smtp.js +499 -0
- package/dist/src/adapters/smtp.js.map +1 -0
- package/dist/src/adapters/vault-provider.js +161 -0
- package/dist/src/adapters/vault-provider.js.map +1 -0
- package/dist/src/channels/batch.js +121 -0
- package/dist/src/channels/batch.js.map +1 -0
- package/dist/src/channels/cli.js +468 -0
- package/dist/src/channels/cli.js.map +1 -0
- package/dist/src/channels/conformance.js +445 -0
- package/dist/src/channels/conformance.js.map +1 -0
- package/dist/src/channels/contract.js +494 -0
- package/dist/src/channels/contract.js.map +1 -0
- package/dist/src/channels/payload-view.js +43 -0
- package/dist/src/channels/payload-view.js.map +1 -0
- package/dist/src/channels/render-queue.js +564 -0
- package/dist/src/channels/render-queue.js.map +1 -0
- package/dist/src/channels/tagging.js +723 -0
- package/dist/src/channels/tagging.js.map +1 -0
- package/dist/src/channels/telegram.js +3190 -0
- package/dist/src/channels/telegram.js.map +1 -0
- package/dist/src/channels/web.js +903 -0
- package/dist/src/channels/web.js.map +1 -0
- package/dist/src/cli/adapter.js +278 -0
- package/dist/src/cli/adapter.js.map +1 -0
- package/dist/src/cli/amend.js +2171 -0
- package/dist/src/cli/amend.js.map +1 -0
- package/dist/src/cli/args.js +86 -0
- package/dist/src/cli/args.js.map +1 -0
- package/dist/src/cli/attest.js +307 -0
- package/dist/src/cli/attest.js.map +1 -0
- package/dist/src/cli/audit-card.js +201 -0
- package/dist/src/cli/audit-card.js.map +1 -0
- package/dist/src/cli/audit.js +460 -0
- package/dist/src/cli/audit.js.map +1 -0
- package/dist/src/cli/channel-telegram.js +2063 -0
- package/dist/src/cli/channel-telegram.js.map +1 -0
- package/dist/src/cli/channel-web.js +357 -0
- package/dist/src/cli/channel-web.js.map +1 -0
- package/dist/src/cli/channel.js +438 -0
- package/dist/src/cli/channel.js.map +1 -0
- package/dist/src/cli/checkpoint-tap.js +238 -0
- package/dist/src/cli/checkpoint-tap.js.map +1 -0
- package/dist/src/cli/coverage.js +343 -0
- package/dist/src/cli/coverage.js.map +1 -0
- package/dist/src/cli/daemon.js +631 -0
- package/dist/src/cli/daemon.js.map +1 -0
- package/dist/src/cli/doctor.js +2648 -0
- package/dist/src/cli/doctor.js.map +1 -0
- package/dist/src/cli/env.js +302 -0
- package/dist/src/cli/env.js.map +1 -0
- package/dist/src/cli/execute.js +1682 -0
- package/dist/src/cli/execute.js.map +1 -0
- package/dist/src/cli/exit-codes.js +82 -0
- package/dist/src/cli/exit-codes.js.map +1 -0
- package/dist/src/cli/feedback.js +205 -0
- package/dist/src/cli/feedback.js.map +1 -0
- package/dist/src/cli/gate-window.js +294 -0
- package/dist/src/cli/gate-window.js.map +1 -0
- package/dist/src/cli/gate.js +557 -0
- package/dist/src/cli/gate.js.map +1 -0
- package/dist/src/cli/git-scope.js +295 -0
- package/dist/src/cli/git-scope.js.map +1 -0
- package/dist/src/cli/gloss-attach.js +107 -0
- package/dist/src/cli/gloss-attach.js.map +1 -0
- package/dist/src/cli/gloss-codex-child.js +149 -0
- package/dist/src/cli/gloss-codex-child.js.map +1 -0
- package/dist/src/cli/gloss-codex.js +255 -0
- package/dist/src/cli/gloss-codex.js.map +1 -0
- package/dist/src/cli/gloss-options.js +79 -0
- package/dist/src/cli/gloss-options.js.map +1 -0
- package/dist/src/cli/gloss.js +362 -0
- package/dist/src/cli/gloss.js.map +1 -0
- package/dist/src/cli/help.js +2217 -0
- package/dist/src/cli/help.js.map +1 -0
- package/dist/src/cli/hook.js +2743 -0
- package/dist/src/cli/hook.js.map +1 -0
- package/dist/src/cli/import.js +175 -0
- package/dist/src/cli/import.js.map +1 -0
- package/dist/src/cli/init.js +336 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/instructions.js +262 -0
- package/dist/src/cli/instructions.js.map +1 -0
- package/dist/src/cli/journal.js +238 -0
- package/dist/src/cli/journal.js.map +1 -0
- package/dist/src/cli/log-advance.js +749 -0
- package/dist/src/cli/log-advance.js.map +1 -0
- package/dist/src/cli/log-anchor.js +387 -0
- package/dist/src/cli/log-anchor.js.map +1 -0
- package/dist/src/cli/log-checkpoint.js +128 -0
- package/dist/src/cli/log-checkpoint.js.map +1 -0
- package/dist/src/cli/log-sync.js +849 -0
- package/dist/src/cli/log-sync.js.map +1 -0
- package/dist/src/cli/log-verbs.js +354 -0
- package/dist/src/cli/log-verbs.js.map +1 -0
- package/dist/src/cli/long-help.js +148 -0
- package/dist/src/cli/long-help.js.map +1 -0
- package/dist/src/cli/main.js +1056 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/cli/mcp.js +306 -0
- package/dist/src/cli/mcp.js.map +1 -0
- package/dist/src/cli/paths.js +79 -0
- package/dist/src/cli/paths.js.map +1 -0
- package/dist/src/cli/payload.js +253 -0
- package/dist/src/cli/payload.js.map +1 -0
- package/dist/src/cli/policy.js +229 -0
- package/dist/src/cli/policy.js.map +1 -0
- package/dist/src/cli/preflight.js +888 -0
- package/dist/src/cli/preflight.js.map +1 -0
- package/dist/src/cli/progress.js +112 -0
- package/dist/src/cli/progress.js.map +1 -0
- package/dist/src/cli/prompt.js +312 -0
- package/dist/src/cli/prompt.js.map +1 -0
- package/dist/src/cli/records.js +66 -0
- package/dist/src/cli/records.js.map +1 -0
- package/dist/src/cli/render.js +132 -0
- package/dist/src/cli/render.js.map +1 -0
- package/dist/src/cli/sandbox.js +150 -0
- package/dist/src/cli/sandbox.js.map +1 -0
- package/dist/src/cli/scaffold.js +137 -0
- package/dist/src/cli/scaffold.js.map +1 -0
- package/dist/src/cli/setup-adapter.js +475 -0
- package/dist/src/cli/setup-adapter.js.map +1 -0
- package/dist/src/cli/setup-channel.js +635 -0
- package/dist/src/cli/setup-channel.js.map +1 -0
- package/dist/src/cli/setup-checkpoint.js +196 -0
- package/dist/src/cli/setup-checkpoint.js.map +1 -0
- package/dist/src/cli/setup-common.js +376 -0
- package/dist/src/cli/setup-common.js.map +1 -0
- package/dist/src/cli/setup-flow.js +476 -0
- package/dist/src/cli/setup-flow.js.map +1 -0
- package/dist/src/cli/setup-service.js +308 -0
- package/dist/src/cli/setup-service.js.map +1 -0
- package/dist/src/cli/setup.js +473 -0
- package/dist/src/cli/setup.js.map +1 -0
- package/dist/src/cli/style.js +469 -0
- package/dist/src/cli/style.js.map +1 -0
- package/dist/src/cli/token.js +274 -0
- package/dist/src/cli/token.js.map +1 -0
- package/dist/src/cli/up.js +847 -0
- package/dist/src/cli/up.js.map +1 -0
- package/dist/src/cli/usage.js +91 -0
- package/dist/src/cli/usage.js.map +1 -0
- package/dist/src/cli/values.js +189 -0
- package/dist/src/cli/values.js.map +1 -0
- package/dist/src/cli/vault.js +362 -0
- package/dist/src/cli/vault.js.map +1 -0
- package/dist/src/cli/verb-registry.js +2173 -0
- package/dist/src/cli/verb-registry.js.map +1 -0
- package/dist/src/cli/wordmark.js +52 -0
- package/dist/src/cli/wordmark.js.map +1 -0
- package/dist/src/core/advance-cycle.js +200 -0
- package/dist/src/core/advance-cycle.js.map +1 -0
- package/dist/src/core/agents-md.js +747 -0
- package/dist/src/core/agents-md.js.map +1 -0
- package/dist/src/core/attest.js +577 -0
- package/dist/src/core/attest.js.map +1 -0
- package/dist/src/core/audit.js +882 -0
- package/dist/src/core/audit.js.map +1 -0
- package/dist/src/core/budgets.js +449 -0
- package/dist/src/core/budgets.js.map +1 -0
- package/dist/src/core/checkpoint.js +738 -0
- package/dist/src/core/checkpoint.js.map +1 -0
- package/dist/src/core/child-env.js +86 -0
- package/dist/src/core/child-env.js.map +1 -0
- package/dist/src/core/clock.js +43 -0
- package/dist/src/core/clock.js.map +1 -0
- package/dist/src/core/command-class.js +2321 -0
- package/dist/src/core/command-class.js.map +1 -0
- package/dist/src/core/coverage-sources/adapter.js +71 -0
- package/dist/src/core/coverage-sources/adapter.js.map +1 -0
- package/dist/src/core/coverage-sources/gh.js +136 -0
- package/dist/src/core/coverage-sources/gh.js.map +1 -0
- package/dist/src/core/coverage-sources/git.js +269 -0
- package/dist/src/core/coverage-sources/git.js.map +1 -0
- package/dist/src/core/coverage.js +337 -0
- package/dist/src/core/coverage.js.map +1 -0
- package/dist/src/core/credential-spec.js +23 -0
- package/dist/src/core/credential-spec.js.map +1 -0
- package/dist/src/core/dark-session.js +714 -0
- package/dist/src/core/dark-session.js.map +1 -0
- package/dist/src/core/decision-refusal.js +265 -0
- package/dist/src/core/decision-refusal.js.map +1 -0
- package/dist/src/core/env-file.js +837 -0
- package/dist/src/core/env-file.js.map +1 -0
- package/dist/src/core/execute.js +1233 -0
- package/dist/src/core/execute.js.map +1 -0
- package/dist/src/core/frontmatter.js +100 -0
- package/dist/src/core/frontmatter.js.map +1 -0
- package/dist/src/core/gate-window.js +506 -0
- package/dist/src/core/gate-window.js.map +1 -0
- package/dist/src/core/gate.js +2947 -0
- package/dist/src/core/gate.js.map +1 -0
- package/dist/src/core/git-run.js +93 -0
- package/dist/src/core/git-run.js.map +1 -0
- package/dist/src/core/harness-version.js +210 -0
- package/dist/src/core/harness-version.js.map +1 -0
- package/dist/src/core/harness-wait.js +58 -0
- package/dist/src/core/harness-wait.js.map +1 -0
- package/dist/src/core/head-retry.js +121 -0
- package/dist/src/core/head-retry.js.map +1 -0
- package/dist/src/core/instance.js +319 -0
- package/dist/src/core/instance.js.map +1 -0
- package/dist/src/core/intake-limits.js +350 -0
- package/dist/src/core/intake-limits.js.map +1 -0
- package/dist/src/core/jcs.js +132 -0
- package/dist/src/core/jcs.js.map +1 -0
- package/dist/src/core/journal.js +200 -0
- package/dist/src/core/journal.js.map +1 -0
- package/dist/src/core/live-draw.js +703 -0
- package/dist/src/core/live-draw.js.map +1 -0
- package/dist/src/core/log-reconcile.js +136 -0
- package/dist/src/core/log-reconcile.js.map +1 -0
- package/dist/src/core/log.js +546 -0
- package/dist/src/core/log.js.map +1 -0
- package/dist/src/core/loop.js +476 -0
- package/dist/src/core/loop.js.map +1 -0
- package/dist/src/core/md-fence.js +74 -0
- package/dist/src/core/md-fence.js.map +1 -0
- package/dist/src/core/money.js +195 -0
- package/dist/src/core/money.js.map +1 -0
- package/dist/src/core/payload-census.js +146 -0
- package/dist/src/core/payload-census.js.map +1 -0
- package/dist/src/core/payload-store.js +340 -0
- package/dist/src/core/payload-store.js.map +1 -0
- package/dist/src/core/payload.js +80 -0
- package/dist/src/core/payload.js.map +1 -0
- package/dist/src/core/policy-diff.js +565 -0
- package/dist/src/core/policy-diff.js.map +1 -0
- package/dist/src/core/policy-expectations.js +394 -0
- package/dist/src/core/policy-expectations.js.map +1 -0
- package/dist/src/core/policy-explain.js +230 -0
- package/dist/src/core/policy-explain.js.map +1 -0
- package/dist/src/core/policy-load.js +524 -0
- package/dist/src/core/policy-load.js.map +1 -0
- package/dist/src/core/policy-match.js +467 -0
- package/dist/src/core/policy-match.js.map +1 -0
- package/dist/src/core/policy-proposal.js +458 -0
- package/dist/src/core/policy-proposal.js.map +1 -0
- package/dist/src/core/prompt-layout.js +422 -0
- package/dist/src/core/prompt-layout.js.map +1 -0
- package/dist/src/core/protected-path-guard.js +1087 -0
- package/dist/src/core/protected-path-guard.js.map +1 -0
- package/dist/src/core/registration.js +39 -0
- package/dist/src/core/registration.js.map +1 -0
- package/dist/src/core/reindex.js +336 -0
- package/dist/src/core/reindex.js.map +1 -0
- package/dist/src/core/sampler.js +388 -0
- package/dist/src/core/sampler.js.map +1 -0
- package/dist/src/core/sandbox.js +424 -0
- package/dist/src/core/sandbox.js.map +1 -0
- package/dist/src/core/seal.js +290 -0
- package/dist/src/core/seal.js.map +1 -0
- package/dist/src/core/state.js +1009 -0
- package/dist/src/core/state.js.map +1 -0
- package/dist/src/core/task-file.js +464 -0
- package/dist/src/core/task-file.js.map +1 -0
- package/dist/src/core/telegram-config.js +114 -0
- package/dist/src/core/telegram-config.js.map +1 -0
- package/dist/src/core/token.js +578 -0
- package/dist/src/core/token.js.map +1 -0
- package/dist/src/core/validate.js +0 -0
- package/dist/src/core/validate.js.map +1 -0
- package/dist/src/core/values.js +153 -0
- package/dist/src/core/values.js.map +1 -0
- package/dist/src/core/vault.js +612 -0
- package/dist/src/core/vault.js.map +1 -0
- package/dist/src/core/verified-snapshot.js +506 -0
- package/dist/src/core/verified-snapshot.js.map +1 -0
- package/dist/src/core/verify.js +549 -0
- package/dist/src/core/verify.js.map +1 -0
- package/dist/src/core/version.js +9 -0
- package/dist/src/core/version.js.map +1 -0
- package/dist/src/core/wysiwys.js +728 -0
- package/dist/src/core/wysiwys.js.map +1 -0
- package/dist/src/daemon/advance-child.js +78 -0
- package/dist/src/daemon/advance-child.js.map +1 -0
- package/dist/src/daemon/advance.js +849 -0
- package/dist/src/daemon/advance.js.map +1 -0
- package/dist/src/daemon/audit.js +90 -0
- package/dist/src/daemon/audit.js.map +1 -0
- package/dist/src/daemon/daemon.js +1988 -0
- package/dist/src/daemon/daemon.js.map +1 -0
- package/dist/src/daemon/dark-session.js +119 -0
- package/dist/src/daemon/dark-session.js.map +1 -0
- package/dist/src/daemon/draw-child.js +132 -0
- package/dist/src/daemon/draw-child.js.map +1 -0
- package/dist/src/daemon/draw.js +458 -0
- package/dist/src/daemon/draw.js.map +1 -0
- package/dist/src/daemon/git-evidence.js +345 -0
- package/dist/src/daemon/git-evidence.js.map +1 -0
- package/dist/src/daemon/projection.js +233 -0
- package/dist/src/daemon/projection.js.map +1 -0
- package/dist/src/daemon/prune.js +376 -0
- package/dist/src/daemon/prune.js.map +1 -0
- package/dist/src/mcp/http.js +343 -0
- package/dist/src/mcp/http.js.map +1 -0
- package/dist/src/mcp/server.js +594 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/docs/cli-reference.md +5363 -0
- package/package.json +43 -4
- package/schema/.gitkeep +0 -0
- package/schema/LICENSE +117 -0
- package/schema/envelope.schema.json +137 -0
- package/schema/event.schema.json +1810 -0
- package/schema/fixtures/envelope/invalid/action-missing-idempotency-key.json +15 -0
- package/schema/fixtures/envelope/invalid/action-unknown-class-format.json +14 -0
- package/schema/fixtures/envelope/invalid/confidence-out-of-range.json +11 -0
- package/schema/fixtures/envelope/invalid/est-cost-bare-number.json +14 -0
- package/schema/fixtures/envelope/invalid/est-cost-noncanonical-string.json +14 -0
- package/schema/fixtures/envelope/invalid/malformed-assignee.json +10 -0
- package/schema/fixtures/envelope/invalid/malformed-created-by.json +7 -0
- package/schema/fixtures/envelope/invalid/malformed-max-latency.json +11 -0
- package/schema/fixtures/envelope/invalid/malformed-payload-hash.json +14 -0
- package/schema/fixtures/envelope/invalid/max-cost-bare-number.json +11 -0
- package/schema/fixtures/envelope/invalid/missing-origin.json +3 -0
- package/schema/fixtures/envelope/invalid/negative-est-cost.json +14 -0
- package/schema/fixtures/envelope/invalid/unknown-state.json +7 -0
- package/schema/fixtures/envelope/invalid/unknown-top-level-field.json +8 -0
- package/schema/fixtures/envelope/valid/action-payload-hash.json +17 -0
- package/schema/fixtures/envelope/valid/actions-without-budget.json +18 -0
- package/schema/fixtures/envelope/valid/canonical.json +25 -0
- package/schema/fixtures/envelope/valid/minimal.json +7 -0
- package/schema/fixtures/envelope/valid/multi-action-executed.json +30 -0
- package/schema/fixtures/envelope/valid/record-write-stage.json +22 -0
- package/schema/fixtures/event/invalid/approval-granted-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/approval-granted-empty-batch-delivery-id.json +17 -0
- package/schema/fixtures/event/invalid/approval-granted-fifth-reaction.json +16 -0
- package/schema/fixtures/event/invalid/approval-granted-missing-actor.json +14 -0
- package/schema/fixtures/event/invalid/approval-requested-missing-action-key.json +14 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-agent-policy-drift.json +15 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-missing-reason.json +15 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-system-actor.json +15 -0
- package/schema/fixtures/event/invalid/audit-decision-refused-human-actor.json +17 -0
- package/schema/fixtures/event/invalid/audit-decision-refused-missing-code.json +16 -0
- package/schema/fixtures/event/invalid/audit-reviewed-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/audit-reviewed-loved-no-note.json +16 -0
- package/schema/fixtures/event/invalid/audit-reviewed-system-actor.json +15 -0
- package/schema/fixtures/event/invalid/bad-actor-prefix.json +15 -0
- package/schema/fixtures/event/invalid/est-cost-bare-number.json +17 -0
- package/schema/fixtures/event/invalid/execution-completed-fabricated-exit-code.json +16 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-empty-id.json +18 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-extra-field.json +19 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-id-not-string.json +18 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-missing-adapter.json +17 -0
- package/schema/fixtures/event/invalid/execution-failed-open-reported-by.json +16 -0
- package/schema/fixtures/event/invalid/execution-indeterminate-open-reason.json +14 -0
- package/schema/fixtures/event/invalid/execution-reconciled-agent-actor.json +17 -0
- package/schema/fixtures/event/invalid/execution-started-negative-env-stripped.json +16 -0
- package/schema/fixtures/event/invalid/gate-bypassed-missing-opened-seq.json +15 -0
- package/schema/fixtures/event/invalid/gate-closed-non-integer-opened-seq.json +13 -0
- package/schema/fixtures/event/invalid/gate-opened-agent-actor.json +16 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-absolute-path.json +14 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-agent-actor.json +14 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-missing-organ-path.json +13 -0
- package/schema/fixtures/event/invalid/harness-unknown-kind.json +18 -0
- package/schema/fixtures/event/invalid/harness-version-multiline.json +16 -0
- package/schema/fixtures/event/invalid/log-checkpoint-agent-actor.json +17 -0
- package/schema/fixtures/event/invalid/log-checkpoint-missing-signature.json +16 -0
- package/schema/fixtures/event/invalid/log-checkpoint-short-signed-hash.json +17 -0
- package/schema/fixtures/event/invalid/log-checkpoint-unknown-signature-alg.json +17 -0
- package/schema/fixtures/event/invalid/malformed-ts.json +15 -0
- package/schema/fixtures/event/invalid/missing-alg.json +14 -0
- package/schema/fixtures/event/invalid/missing-hash.json +14 -0
- package/schema/fixtures/event/invalid/non-integer-seq.json +15 -0
- package/schema/fixtures/event/invalid/payload-pruned-human-actor.json +14 -0
- package/schema/fixtures/event/invalid/payload-pruned-missing-hash.json +14 -0
- package/schema/fixtures/event/invalid/policy-declined-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/policy-proposed-missing-diff.json +19 -0
- package/schema/fixtures/event/invalid/policy-proposed-system-actor.json +26 -0
- package/schema/fixtures/event/invalid/short-hash.json +15 -0
- package/schema/fixtures/event/invalid/unknown-alg.json +15 -0
- package/schema/fixtures/event/invalid/unknown-event-type.json +15 -0
- package/schema/fixtures/event/invalid/unknown-top-level-field.json +16 -0
- package/schema/fixtures/event/valid/approval-expired.json +15 -0
- package/schema/fixtures/event/valid/approval-granted-batch.json +19 -0
- package/schema/fixtures/event/valid/approval-granted-reaction.json +16 -0
- package/schema/fixtures/event/valid/approval-granted.json +15 -0
- package/schema/fixtures/event/valid/approval-rejected.json +15 -0
- package/schema/fixtures/event/valid/approval-requested.json +19 -0
- package/schema/fixtures/event/valid/approval-revoked.json +15 -0
- package/schema/fixtures/event/valid/approval-withdrawn-policy-drift.json +17 -0
- package/schema/fixtures/event/valid/approval-withdrawn.json +16 -0
- package/schema/fixtures/event/valid/audit-decision-refused.json +20 -0
- package/schema/fixtures/event/valid/audit-reviewed-reaction.json +17 -0
- package/schema/fixtures/event/valid/audit-reviewed.json +15 -0
- package/schema/fixtures/event/valid/audit-sampled.json +14 -0
- package/schema/fixtures/event/valid/budget-exceeded.json +21 -0
- package/schema/fixtures/event/valid/envelope-drift.json +16 -0
- package/schema/fixtures/event/valid/execution-completed-harness-report.json +16 -0
- package/schema/fixtures/event/valid/execution-completed-provider-ref.json +18 -0
- package/schema/fixtures/event/valid/execution-completed.json +15 -0
- package/schema/fixtures/event/valid/execution-failed-harness-report.json +16 -0
- package/schema/fixtures/event/valid/execution-failed.json +15 -0
- package/schema/fixtures/event/valid/execution-indeterminate.json +15 -0
- package/schema/fixtures/event/valid/execution-reconciled.json +17 -0
- package/schema/fixtures/event/valid/execution-started-env-stripped.json +17 -0
- package/schema/fixtures/event/valid/execution-started.json +14 -0
- package/schema/fixtures/event/valid/gate-bypassed-harness-version.json +18 -0
- package/schema/fixtures/event/valid/gate-bypassed.json +19 -0
- package/schema/fixtures/event/valid/gate-closed.json +14 -0
- package/schema/fixtures/event/valid/gate-opened.json +16 -0
- package/schema/fixtures/event/valid/gate-organ-attested.json +14 -0
- package/schema/fixtures/event/valid/genesis-null-prev.json +14 -0
- package/schema/fixtures/event/valid/log-checkpoint.json +17 -0
- package/schema/fixtures/event/valid/payload-pruned-orphan.json +13 -0
- package/schema/fixtures/event/valid/payload-pruned.json +17 -0
- package/schema/fixtures/event/valid/policy-declined.json +16 -0
- package/schema/fixtures/event/valid/policy-proposed.json +35 -0
- package/schema/fixtures/event/valid/policy-updated.json +14 -0
- package/schema/fixtures/event/valid/reconciliation-required.json +18 -0
- package/schema/fixtures/event/valid/reconciliation-satisfied.json +17 -0
- package/schema/fixtures/event/valid/route-accepted.json +15 -0
- package/schema/fixtures/event/valid/route-proposed.json +16 -0
- package/schema/fixtures/event/valid/spec-example.json +15 -0
- package/schema/fixtures/event/valid/task-registered-harness-version.json +23 -0
- package/schema/fixtures/event/valid/task-registered.json +14 -0
- package/schema/fixtures/hash/known-answer-pre-121.json +74 -0
- package/schema/fixtures/hash/known-answer.json +74 -0
- package/schema/fixtures/policy/invalid/bad-approval-ttl.json +7 -0
- package/schema/fixtures/policy/invalid/bad-web-port.json +4 -0
- package/schema/fixtures/policy/invalid/checkpoint-key-not-base64.json +6 -0
- package/schema/fixtures/policy/invalid/class-rule-missing-autonomy.json +9 -0
- package/schema/fixtures/policy/invalid/empty-class-key.json +6 -0
- package/schema/fixtures/policy/invalid/live-rate-on-human-only.json +7 -0
- package/schema/fixtures/policy/invalid/malformed-class-key.json +6 -0
- package/schema/fixtures/policy/invalid/missing-version.json +8 -0
- package/schema/fixtures/policy/invalid/negative-limit.json +9 -0
- package/schema/fixtures/policy/invalid/non-numeric-limit.json +9 -0
- package/schema/fixtures/policy/invalid/non-positive-max-pending.json +9 -0
- package/schema/fixtures/policy/invalid/on-expiry-grant.json +8 -0
- package/schema/fixtures/policy/invalid/payload-retention-bare-number.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-compound.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-fractional.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-zero.json +4 -0
- package/schema/fixtures/policy/invalid/protected-paths-escape.json +4 -0
- package/schema/fixtures/policy/invalid/protected-paths-glob.json +4 -0
- package/schema/fixtures/policy/invalid/retro-rate-on-human-only.json +7 -0
- package/schema/fixtures/policy/invalid/retro-rate-on-manual.json +7 -0
- package/schema/fixtures/policy/invalid/retro-rate-zero.json +7 -0
- package/schema/fixtures/policy/invalid/sample-rate-too-high.json +5 -0
- package/schema/fixtures/policy/invalid/sampling-secret-env-empty.json +7 -0
- package/schema/fixtures/policy/invalid/sampling-secret-env-not-string.json +6 -0
- package/schema/fixtures/policy/invalid/skew-tolerance-compound.json +6 -0
- package/schema/fixtures/policy/invalid/unknown-autonomy.json +7 -0
- package/schema/fixtures/policy/invalid/unknown-class-rule-key.json +6 -0
- package/schema/fixtures/policy/invalid/unknown-top-level-key.json +7 -0
- package/schema/fixtures/policy/invalid/vault-passphrase-env-empty.json +6 -0
- package/schema/fixtures/policy/invalid/vault-passphrase-literal.json +6 -0
- package/schema/fixtures/policy/invalid/version-not-string.json +4 -0
- package/schema/fixtures/policy/valid/canonical.json +47 -0
- package/schema/fixtures/policy/valid/checkpoint-keys.json +18 -0
- package/schema/fixtures/policy/valid/class-approvers-limits.json +25 -0
- package/schema/fixtures/policy/valid/class-retro-rate.json +17 -0
- package/schema/fixtures/policy/valid/global-budgets.json +19 -0
- package/schema/fixtures/policy/valid/human-only.json +9 -0
- package/schema/fixtures/policy/valid/minimal.json +6 -0
- package/schema/fixtures/policy/valid/protected-paths.json +10 -0
- package/schema/fixtures/policy/valid/record-namespace.json +13 -0
- package/schema/fixtures/policy/valid/request-volume-limits.json +26 -0
- package/schema/fixtures/policy/valid/retention-and-sampling-secret.json +16 -0
- package/schema/fixtures/policy/valid/skew-tolerance.json +15 -0
- package/schema/fixtures/policy/valid/vault-passphrase-env.json +14 -0
- package/schema/fixtures/policy/valid/wildcards.json +15 -0
- package/schema/fixtures/policy-md/invalid/alias-bomb.md +15 -0
- package/schema/fixtures/policy-md/invalid/no-fence.md +7 -0
- package/schema/fixtures/policy-md/invalid/protected-route-not-a-subclass.md +16 -0
- package/schema/fixtures/policy-md/invalid/schema-invalid-autonomy.md +16 -0
- package/schema/fixtures/policy-md/invalid/schema-invalid-read-proof.md +17 -0
- package/schema/fixtures/policy-md/invalid/two-fences.md +19 -0
- package/schema/fixtures/policy-md/invalid/unclosed-fence.md +11 -0
- package/schema/fixtures/policy-md/invalid/wrong-info-string.md +11 -0
- package/schema/fixtures/policy-md/invalid/yaml-syntax-error.md +13 -0
- package/schema/fixtures/policy-md/precedence/both/APPROVAL.md +7 -0
- package/schema/fixtures/policy-md/precedence/both/APPROVALS.md +7 -0
- package/schema/fixtures/policy-md/precedence/fallback-only/APPROVALS.md +7 -0
- package/schema/fixtures/policy-md/valid/canonical.md +50 -0
- package/schema/fixtures/policy-md/valid/daemon-read-proof.md +18 -0
- package/schema/fixtures/policy-md/valid/minimal.md +3 -0
- package/schema/fixtures/policy-md/valid/prose-lookalikes.md +54 -0
- package/schema/fixtures/policy-md/valid/routed-protected-paths.md +49 -0
- package/schema/fixtures/policy-md/valid/with-values.md +79 -0
- package/schema/fixtures/sample-record/invalid/bad-date-time.json +4 -0
- package/schema/fixtures/sample-record/invalid/missing-required-field.json +3 -0
- package/schema/fixtures/sample-record/invalid/unknown-top-level-field.json +5 -0
- package/schema/fixtures/sample-record/invalid/wrong-type.json +4 -0
- package/schema/fixtures/sample-record/valid/minimal.json +4 -0
- package/schema/fixtures/sample-record/valid/with-note.json +5 -0
- package/schema/fixtures/values/invalid/class-shaped.json +9 -0
- package/schema/fixtures/values/invalid/duplicate-entry.json +4 -0
- package/schema/fixtures/values/invalid/non-string-item.json +4 -0
- package/schema/fixtures/values/invalid/over-cap.json +26 -0
- package/schema/fixtures/values/invalid/unknown-key.json +5 -0
- package/schema/fixtures/values/invalid/version-string.json +1 -0
- package/schema/fixtures/values/valid/empty-lists.json +7 -0
- package/schema/fixtures/values/valid/full.json +20 -0
- package/schema/fixtures/values/valid/minimal.json +1 -0
- package/schema/fixtures/values-md/invalid/schema-invalid.md +62 -0
- package/schema/fixtures/values-md/invalid/two-blocks.md +69 -0
- package/schema/fixtures/values-md/invalid/unterminated.md +61 -0
- package/schema/fixtures/values-md/invalid/yaml-error.md +63 -0
- package/schema/fixtures/values-md/valid/absent.md +50 -0
- package/schema/fixtures/values-md/valid/with-values.md +79 -0
- package/schema/policy.schema.json +481 -0
- package/schema/sample-record.schema.json +26 -0
- package/schema/values.schema.json +55 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vault as a {@link CredentialProvider} (SPEC.md §10.4; APRV-68).
|
|
3
|
+
*
|
|
4
|
+
* `adapters/contract.ts` left one seam open and named the task that would fill
|
|
5
|
+
* it: "No vault. {@link CredentialProvider} is the seam a real vault implements
|
|
6
|
+
* (APRV-68)." This module is that implementation, and it is deliberately thin.
|
|
7
|
+
* Everything about *when* a credential may be read belongs to the contract;
|
|
8
|
+
* everything about *how* the bytes are stored belongs to `core/vault.ts`. What
|
|
9
|
+
* is left here is the translation between the two vocabularies.
|
|
10
|
+
*
|
|
11
|
+
* ## The structural rule
|
|
12
|
+
*
|
|
13
|
+
* A provider built here is only ever handed to
|
|
14
|
+
* {@link executeThroughAdapter} through {@link AdapterExecuteOptions.credentials}.
|
|
15
|
+
* The contract wraps it in a window that closes the instant `act` returns, so
|
|
16
|
+
* every read is inside a verified, consumed, single-use token — which is what
|
|
17
|
+
* SPEC.md §10.4 means by "the credentials only answer to tokens". Three things
|
|
18
|
+
* hold that rule up, and none of them is a convention:
|
|
19
|
+
*
|
|
20
|
+
* 1. `core/vault.ts` exports exactly one function that returns a credential
|
|
21
|
+
* value (`getCredential`), and this module is its only caller in the
|
|
22
|
+
* repository. `tests/vault.test.ts` pins both halves.
|
|
23
|
+
* 2. There is no CLI verb that prints a value. `approval vault` can set, list
|
|
24
|
+
* (names), and remove; it cannot show. See `src/cli/vault.ts`.
|
|
25
|
+
* 3. The contract scans everything an adapter returns for the values the
|
|
26
|
+
* provider handed out and redacts them, so a leak by a careless adapter is
|
|
27
|
+
* caught mechanically rather than reviewed for.
|
|
28
|
+
*
|
|
29
|
+
* ## What this defends, and what it does not
|
|
30
|
+
*
|
|
31
|
+
* Exactly what the vault module's own threat model says, restated because a
|
|
32
|
+
* reader arriving from the adapter side deserves it here too. **Defended:**
|
|
33
|
+
* credentials at rest, and casual reads by an agent that can read files in the
|
|
34
|
+
* working tree — the ciphertext hides the names as well as the values.
|
|
35
|
+
* **Not defended (SPEC.md §11, plainly):** a compromised host, and an agent that
|
|
36
|
+
* can read the passphrase environment variable. Such an agent does not need this
|
|
37
|
+
* provider; it can decrypt the file directly. The vault raises the cost of a
|
|
38
|
+
* leak from reading a file to owning the session, and claims nothing beyond
|
|
39
|
+
* that.
|
|
40
|
+
*
|
|
41
|
+
* Total and synchronous, as {@link CredentialProvider} requires: nothing here
|
|
42
|
+
* throws, nothing blocks on a human, and every failure is one of the three
|
|
43
|
+
* {@link CREDENTIAL_REFUSAL_CODES}.
|
|
44
|
+
*/
|
|
45
|
+
import { defaultSourceRunner } from "../core/env-file.js";
|
|
46
|
+
import { getCredential, passphraseFrom, vaultPathFor, } from "../core/vault.js";
|
|
47
|
+
import { passphraseUnderGrant } from "./env-passphrase.js";
|
|
48
|
+
/**
|
|
49
|
+
* How a vault refusal is reported to an adapter.
|
|
50
|
+
*
|
|
51
|
+
* The contract's union has three members and they answer three different
|
|
52
|
+
* questions, so the mapping is by *repair* rather than by severity:
|
|
53
|
+
*
|
|
54
|
+
* - **`credential-unavailable`** — nothing is configured yet. No vault, no such
|
|
55
|
+
* name, no passphrase in the environment. The operator has something to do.
|
|
56
|
+
* - **`credential-refused`** — the vault exists and would not open: a wrong
|
|
57
|
+
* passphrase, an altered file, a format this build does not read, an
|
|
58
|
+
* unreadable path. SPEC.md's phrase for this branch is "a locked vault", and
|
|
59
|
+
* it is distinct because the repair is investigation, not configuration.
|
|
60
|
+
* - **`credential-window-closed`** — never produced here. It is the contract's
|
|
61
|
+
* own verdict about *when* the question was asked, and this module has no way
|
|
62
|
+
* to know that.
|
|
63
|
+
*/
|
|
64
|
+
function mapRefusal(code) {
|
|
65
|
+
switch (code) {
|
|
66
|
+
case "vault-absent":
|
|
67
|
+
case "passphrase-unset":
|
|
68
|
+
case "credential-absent":
|
|
69
|
+
case "invalid-name":
|
|
70
|
+
return "credential-unavailable";
|
|
71
|
+
case "vault-io":
|
|
72
|
+
case "vault-malformed":
|
|
73
|
+
case "vault-version-unsupported":
|
|
74
|
+
case "vault-unreadable":
|
|
75
|
+
case "empty-value":
|
|
76
|
+
case "vault-write-failed":
|
|
77
|
+
return "credential-refused";
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* A provider that answers from the encrypted vault.
|
|
82
|
+
*
|
|
83
|
+
* **Lazy, and cached for the life of the provider.** The vault is not opened
|
|
84
|
+
* until an adapter actually asks for something, so an execution that needs no
|
|
85
|
+
* credential pays no scrypt cost and touches no ciphertext. Once opened, the
|
|
86
|
+
* derived value for a name is remembered, because a provider's life *is* one
|
|
87
|
+
* `act` call: the contract closes it when `act` returns, so the cache cannot
|
|
88
|
+
* outlive the token window it was built for, and the alternative — a fresh
|
|
89
|
+
* ~100 ms key derivation per credential — would put a visible tax on an adapter
|
|
90
|
+
* that needs two.
|
|
91
|
+
*
|
|
92
|
+
* The passphrase is read from the environment on every open rather than
|
|
93
|
+
* captured at construction, so a provider built before the operator exported the
|
|
94
|
+
* variable is not permanently poisoned.
|
|
95
|
+
*
|
|
96
|
+
* **The scoped fallback (APRV-168).** When {@link VaultProviderOptions.envFilePath}
|
|
97
|
+
* is supplied AND the contract has told this provider it is inside a token
|
|
98
|
+
* window, a passphrase absent from the environment is resolved from the
|
|
99
|
+
* instance's `.approval/env` instead. That is the one narrowing of
|
|
100
|
+
* `core/env-file.ts`'s "no verb reads this file" rule, and the whole argument
|
|
101
|
+
* for it lives in `adapters/env-passphrase.ts`. The short form: the authority is
|
|
102
|
+
* the token, and a human tapped Approve for exactly this action. The value is
|
|
103
|
+
* used to derive one key and reaches no environment, no argv, no log, and no
|
|
104
|
+
* message; the cache is dropped when the window closes, so a passphrase resolved
|
|
105
|
+
* under one grant opens nothing under the next.
|
|
106
|
+
*
|
|
107
|
+
* Messages name the environment VARIABLE and the credential NAME, and never a
|
|
108
|
+
* value: an adapter's failure message is one of the strings the contract hands
|
|
109
|
+
* back to a caller, and a diagnostic that quoted the secret would defeat the
|
|
110
|
+
* redaction guard by putting the secret in the one place the guard cannot know
|
|
111
|
+
* to look for it.
|
|
112
|
+
*/
|
|
113
|
+
export function vaultCredentialProvider(location, options) {
|
|
114
|
+
const vaultPath = "vaultPath" in location ? location.vaultPath : vaultPathFor(location.logPath);
|
|
115
|
+
const { passphraseEnv } = options;
|
|
116
|
+
const cache = new Map();
|
|
117
|
+
// The live grant, set by the contract when the credential window opens and
|
|
118
|
+
// cleared when it closes. Never read except by the fallback below, and the
|
|
119
|
+
// fallback is unreachable while it is null.
|
|
120
|
+
let live = null;
|
|
121
|
+
return {
|
|
122
|
+
grant(grant) {
|
|
123
|
+
live = grant;
|
|
124
|
+
// A cached credential was derived under the previous window's authority.
|
|
125
|
+
// Dropping the cache with the grant means the next window pays for its
|
|
126
|
+
// own passphrase rather than inheriting one.
|
|
127
|
+
if (grant === null)
|
|
128
|
+
cache.clear();
|
|
129
|
+
},
|
|
130
|
+
get(name) {
|
|
131
|
+
const cached = cache.get(name);
|
|
132
|
+
if (cached !== undefined)
|
|
133
|
+
return { ok: true, value: cached };
|
|
134
|
+
const passphrase = passphraseFrom(passphraseEnv, options.env ?? process.env) ??
|
|
135
|
+
// APRV-168. Only inside a window the contract opened against a token a
|
|
136
|
+
// human's grant minted, and only for the one variable the policy names.
|
|
137
|
+
// The value goes straight into the key derivation below: it is not
|
|
138
|
+
// exported, not cached on its own, and it appears in no message on
|
|
139
|
+
// either branch.
|
|
140
|
+
(options.envFilePath === undefined
|
|
141
|
+
? null
|
|
142
|
+
: passphraseUnderGrant(live, options.envFilePath, passphraseEnv, options.sourceRunner ?? defaultSourceRunner));
|
|
143
|
+
if (passphrase === null) {
|
|
144
|
+
return {
|
|
145
|
+
ok: false,
|
|
146
|
+
code: "credential-unavailable",
|
|
147
|
+
message: `credential ${JSON.stringify(name)} lives in the vault at ${vaultPath}, and the passphrase variable ${passphraseEnv} is unset or empty in this process${options.envFilePath === undefined
|
|
148
|
+
? ""
|
|
149
|
+
: `, and it did not resolve from ${options.envFilePath} either`}. The policy names the variable and never the value (SPEC.md §5.2, §10.4); export it in the environment that runs the adapter, or give it a line in the environment source map.`,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const result = getCredential(vaultPath, passphrase, name);
|
|
153
|
+
if (result.ok) {
|
|
154
|
+
cache.set(name, result.value);
|
|
155
|
+
return { ok: true, value: result.value };
|
|
156
|
+
}
|
|
157
|
+
return { ok: false, code: mapRefusal(result.code), message: result.message };
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=vault-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault-provider.js","sourceRoot":"","sources":["../../../src/adapters/vault-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batching: one human gesture over many requests, without losing audit
|
|
3
|
+
* granularity (SPEC.md §10.3, requirement B7).
|
|
4
|
+
*
|
|
5
|
+
* > Channels MAY present multiple pending requests as a batch and collect one
|
|
6
|
+
* > human gesture over the set, and SHOULD for high-volume `record.*` classes.
|
|
7
|
+
* > The log never batches: each granted or rejected request receives its own
|
|
8
|
+
* > `approval.granted` or `approval.rejected` event carrying the batch's channel
|
|
9
|
+
* > delivery id in its payload, so audit granularity survives ergonomic
|
|
10
|
+
* > grouping. A batch MUST NOT mix `manual` classes with differing
|
|
11
|
+
* > payload-display requirements in a way that hides any full payload behind the
|
|
12
|
+
* > fold of another.
|
|
13
|
+
*
|
|
14
|
+
* Two halves, and this module is both: {@link assembleBatch} enforces the
|
|
15
|
+
* forbidden mix *before* anything is delivered, and {@link recordBatchDecisions}
|
|
16
|
+
* records one gate decision per member afterwards.
|
|
17
|
+
*
|
|
18
|
+
* ## The B7 operationalization — FLAGGED FOR HUMAN REVIEW
|
|
19
|
+
*
|
|
20
|
+
* "Hides any full payload behind the fold of another" is a sentence about
|
|
21
|
+
* rendering, and rendering is the one thing the runtime cannot inspect. So B7 is
|
|
22
|
+
* operationalized here as a property of the *batch material* rather than of the
|
|
23
|
+
* pixels:
|
|
24
|
+
*
|
|
25
|
+
* > A batch MUST carry every member's full payload whole. The forbidden mix is
|
|
26
|
+
* > any member whose `fullPayload` is absent or truncated while the batch
|
|
27
|
+
* > contains more than one distinct payload — because with one payload there is
|
|
28
|
+
* > no "another" to hide behind, and with two or more, a member the channel was
|
|
29
|
+
* > not given in full is a member the approver cannot have read.
|
|
30
|
+
*
|
|
31
|
+
* Concretely, {@link assembleBatch} refuses when the batch holds more than one
|
|
32
|
+
* distinct `payload_hash` **and** any member's `fullPayload` is `null` or
|
|
33
|
+
* `truncated`. A single-payload batch (the same bytes requested twice, or one
|
|
34
|
+
* request) is always assemblable, and a multi-payload batch is assemblable
|
|
35
|
+
* exactly when every member arrives whole.
|
|
36
|
+
*
|
|
37
|
+
* What this deliberately does **not** try to be: a judgment about whether a
|
|
38
|
+
* particular channel's layout actually put payload #2 below the fold. That is a
|
|
39
|
+
* per-channel rendering property, and `channels/conformance.ts` checks the
|
|
40
|
+
* observable half of it (every member's full payload text present in the
|
|
41
|
+
* rendering, delineated from the claimed summary). The interpretation to
|
|
42
|
+
* confirm is the reduction of "differing payload-display requirements" to
|
|
43
|
+
* "distinct payload bytes": it is the reading that makes the rule mechanical,
|
|
44
|
+
* and it is stricter than the spec's letter in one direction (two whole payloads
|
|
45
|
+
* are fine only if both are whole) and silent in another (it says nothing about
|
|
46
|
+
* a channel that renders both whole and then collapses one). A human should
|
|
47
|
+
* confirm this is the intended reading before APRV-23/25/26 build against it.
|
|
48
|
+
*/
|
|
49
|
+
import { recordChannelDecision, } from "./contract.js";
|
|
50
|
+
export { BATCH_DELIVERY_ID_FIELD, BATCH_DELIVERY_NOTE_PREFIX, batchDeliveryIdOf, batchNote, } from "./contract.js";
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// assembleBatch
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
/** The one way a batch can be refused (SPEC.md §10.3, B7). */
|
|
55
|
+
export const BATCH_REFUSAL_CODE = "batch-forbidden-mix";
|
|
56
|
+
/**
|
|
57
|
+
* Assemble `requests` into a batch, or refuse the forbidden mix.
|
|
58
|
+
*
|
|
59
|
+
* The returned batch carries every member request **whole** — the same
|
|
60
|
+
* {@link ChannelRequest} objects a channel would receive individually, tags and
|
|
61
|
+
* full payloads included. A channel therefore has everything it needs to render
|
|
62
|
+
* each member in full; if it chooses not to, that is a rendering defect the
|
|
63
|
+
* conformance suite catches, not a gap in the material it was handed.
|
|
64
|
+
*
|
|
65
|
+
* `deliveryId` is left unset: it is assigned by the channel at `notify`.
|
|
66
|
+
*/
|
|
67
|
+
export function assembleBatch(requests) {
|
|
68
|
+
const hashes = new Set(requests.map((request) => request.payload_hash.value));
|
|
69
|
+
if (hashes.size > 1) {
|
|
70
|
+
const hidden = requests.filter((request) => {
|
|
71
|
+
const rendering = request.fullPayload.value;
|
|
72
|
+
return rendering === null || rendering.truncated;
|
|
73
|
+
});
|
|
74
|
+
if (hidden.length > 0) {
|
|
75
|
+
const keys = hidden.map((request) => request.action_key.value).join(", ");
|
|
76
|
+
return {
|
|
77
|
+
ok: false,
|
|
78
|
+
code: BATCH_REFUSAL_CODE,
|
|
79
|
+
message: `batch refused: it carries ${hashes.size} distinct payloads and ${hidden.length} member(s) whose full payload is absent or truncated (${keys}). SPEC.md §10.3 forbids a batch that hides any full payload behind the fold of another; a member the channel was never given in full is a member the approver cannot have read, so the set is refused before delivery rather than approved in part.`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return { ok: true, batch: { requests } };
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Record one gate decision per batch member.
|
|
87
|
+
*
|
|
88
|
+
* **Unit decisions mean unit outcomes.** The log never batches (SPEC.md §10.3),
|
|
89
|
+
* so this is a loop over `decide()` and not a transaction: each member is
|
|
90
|
+
* checked and appended on its own, each carries the batch delivery id in its
|
|
91
|
+
* payload as the first-class `batch_delivery_id` field (amended SPEC.md §10.3,
|
|
92
|
+
* APRV-38), and a member that refuses does **not** stop the rest. That is
|
|
93
|
+
* deliberate and it is the only coherent semantics available — an "all or
|
|
94
|
+
* nothing" batch would have to un-append events the log forbids un-appending,
|
|
95
|
+
* and abandoning the remaining members because the third one expired would
|
|
96
|
+
* discard a human's answer to the other four. So: partial success is a real
|
|
97
|
+
* outcome, `ok` is false when any member refused, and `results` says exactly
|
|
98
|
+
* which ones landed. A caller that wants atomicity does not want a batch.
|
|
99
|
+
*
|
|
100
|
+
* Ordering: members are recorded in the order given. Each append passes the
|
|
101
|
+
* gate's compare-and-append precondition against the head *it* read, so a
|
|
102
|
+
* concurrent writer refuses one member with `append-failed`/`head-moved`
|
|
103
|
+
* rather than corrupting the set.
|
|
104
|
+
*/
|
|
105
|
+
export function recordBatchDecisions(logPath, decisions, batchDeliveryId, actorOptions, gateOptions = {}) {
|
|
106
|
+
const results = [];
|
|
107
|
+
for (const decision of decisions) {
|
|
108
|
+
const recorded = recordChannelDecision(logPath, { ...decision, batchDeliveryId }, actorOptions, gateOptions);
|
|
109
|
+
results.push({
|
|
110
|
+
action_key: decision.action_key,
|
|
111
|
+
outcome: recorded.outcome,
|
|
112
|
+
...(recorded.token === undefined ? {} : { token: recorded.token }),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
ok: results.every((entry) => entry.outcome.ok),
|
|
117
|
+
results,
|
|
118
|
+
batchDeliveryId,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=batch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch.js","sourceRoot":"","sources":["../../../src/channels/batch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAGH,OAAO,EACL,qBAAqB,GAOtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,iBAAiB,EACjB,SAAS,GAEV,MAAM,eAAe,CAAC;AAEvB,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E,8DAA8D;AAC9D,MAAM,CAAC,MAAM,kBAAkB,GAAG,qBAAqB,CAAC;AAUxD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAC,QAA0B;IACtD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;IAE9E,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;YACzC,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC;YAC5C,OAAO,SAAS,KAAK,IAAI,IAAI,SAAS,CAAC,SAAS,CAAC;QACnD,CAAC,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1E,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,6BAA6B,MAAM,CAAC,IAAI,0BAA0B,MAAM,CAAC,MAAM,yDAAyD,IAAI,sPAAsP;aAC5Y,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC;AAC3C,CAAC;AAsBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAe,EACf,SAA4B,EAC5B,eAA2B,EAC3B,YAAiC,EACjC,WAAW,GAAkB,EAAE;IAE/B,MAAM,OAAO,GAAwB,EAAE,CAAC;IACxC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,qBAAqB,CACpC,OAAO,EACP,EAAE,GAAG,QAAQ,EAAE,eAAe,EAAE,EAChC,YAAY,EACZ,WAAW,CACZ,CAAC;QACF,OAAO,CAAC,IAAI,CAAC;YACX,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,GAAG,CAAC,QAAQ,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;SACnE,CAAC,CAAC;IACL,CAAC;IACD,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9C,OAAO;QACP,eAAe;KAChB,CAAC;AACJ,CAAC"}
|