approval-md 0.0.1 → 0.2.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 +940 -4
- package/SPEC.md +476 -34
- package/cli.js +29 -3
- package/dist/src/adapters/agentmail.d.ts +426 -0
- package/dist/src/adapters/agentmail.js +1200 -0
- package/dist/src/adapters/agentmail.js.map +1 -0
- package/dist/src/adapters/conformance.d.ts +149 -0
- package/dist/src/adapters/conformance.js +461 -0
- package/dist/src/adapters/conformance.js.map +1 -0
- package/dist/src/adapters/contract.d.ts +628 -0
- package/dist/src/adapters/contract.js +1035 -0
- package/dist/src/adapters/contract.js.map +1 -0
- package/dist/src/adapters/email.d.ts +324 -0
- package/dist/src/adapters/email.js +749 -0
- package/dist/src/adapters/email.js.map +1 -0
- package/dist/src/adapters/env-passphrase.d.ts +93 -0
- package/dist/src/adapters/env-passphrase.js +132 -0
- package/dist/src/adapters/env-passphrase.js.map +1 -0
- package/dist/src/adapters/public.d.ts +11 -0
- package/dist/src/adapters/public.js +11 -0
- package/dist/src/adapters/public.js.map +1 -0
- package/dist/src/adapters/registry.d.ts +59 -0
- package/dist/src/adapters/registry.js +77 -0
- package/dist/src/adapters/registry.js.map +1 -0
- package/dist/src/adapters/smtp.d.ts +213 -0
- package/dist/src/adapters/smtp.js +499 -0
- package/dist/src/adapters/smtp.js.map +1 -0
- package/dist/src/adapters/vault-provider.d.ts +114 -0
- package/dist/src/adapters/vault-provider.js +161 -0
- package/dist/src/adapters/vault-provider.js.map +1 -0
- package/dist/src/adapters/zzz.d.ts +66 -0
- package/dist/src/adapters/zzz.js +299 -0
- package/dist/src/adapters/zzz.js.map +1 -0
- package/dist/src/channels/batch.d.ts +109 -0
- package/dist/src/channels/batch.js +121 -0
- package/dist/src/channels/batch.js.map +1 -0
- package/dist/src/channels/cli.d.ts +193 -0
- package/dist/src/channels/cli.js +468 -0
- package/dist/src/channels/cli.js.map +1 -0
- package/dist/src/channels/conformance.d.ts +92 -0
- package/dist/src/channels/conformance.js +445 -0
- package/dist/src/channels/conformance.js.map +1 -0
- package/dist/src/channels/contract.d.ts +623 -0
- package/dist/src/channels/contract.js +494 -0
- package/dist/src/channels/contract.js.map +1 -0
- package/dist/src/channels/payload-view.d.ts +35 -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.d.ts +149 -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.d.ts +196 -0
- package/dist/src/channels/tagging.js +723 -0
- package/dist/src/channels/tagging.js.map +1 -0
- package/dist/src/channels/telegram.d.ts +1832 -0
- package/dist/src/channels/telegram.js +3190 -0
- package/dist/src/channels/telegram.js.map +1 -0
- package/dist/src/channels/web.d.ts +341 -0
- package/dist/src/channels/web.js +903 -0
- package/dist/src/channels/web.js.map +1 -0
- package/dist/src/cli/adapter.d.ts +90 -0
- package/dist/src/cli/adapter.js +288 -0
- package/dist/src/cli/adapter.js.map +1 -0
- package/dist/src/cli/amend.d.ts +59 -0
- package/dist/src/cli/amend.js +2171 -0
- package/dist/src/cli/amend.js.map +1 -0
- package/dist/src/cli/args.d.ts +43 -0
- package/dist/src/cli/args.js +86 -0
- package/dist/src/cli/args.js.map +1 -0
- package/dist/src/cli/attest.d.ts +41 -0
- package/dist/src/cli/attest.js +307 -0
- package/dist/src/cli/attest.js.map +1 -0
- package/dist/src/cli/audit-card.d.ts +62 -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.d.ts +59 -0
- package/dist/src/cli/audit.js +460 -0
- package/dist/src/cli/audit.js.map +1 -0
- package/dist/src/cli/channel-telegram.d.ts +806 -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.d.ts +131 -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.d.ts +71 -0
- package/dist/src/cli/channel.js +438 -0
- package/dist/src/cli/channel.js.map +1 -0
- package/dist/src/cli/checkpoint-tap.d.ts +169 -0
- package/dist/src/cli/checkpoint-tap.js +238 -0
- package/dist/src/cli/checkpoint-tap.js.map +1 -0
- package/dist/src/cli/codex.d.ts +2 -0
- package/dist/src/cli/codex.js +172 -0
- package/dist/src/cli/codex.js.map +1 -0
- package/dist/src/cli/coverage.d.ts +61 -0
- package/dist/src/cli/coverage.js +343 -0
- package/dist/src/cli/coverage.js.map +1 -0
- package/dist/src/cli/daemon.d.ts +120 -0
- package/dist/src/cli/daemon.js +631 -0
- package/dist/src/cli/daemon.js.map +1 -0
- package/dist/src/cli/doctor.d.ts +129 -0
- package/dist/src/cli/doctor.js +2762 -0
- package/dist/src/cli/doctor.js.map +1 -0
- package/dist/src/cli/env.d.ts +65 -0
- package/dist/src/cli/env.js +302 -0
- package/dist/src/cli/env.js.map +1 -0
- package/dist/src/cli/execute.d.ts +202 -0
- package/dist/src/cli/execute.js +1682 -0
- package/dist/src/cli/execute.js.map +1 -0
- package/dist/src/cli/exit-codes.d.ts +73 -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.d.ts +60 -0
- package/dist/src/cli/feedback.js +205 -0
- package/dist/src/cli/feedback.js.map +1 -0
- package/dist/src/cli/gate-window.d.ts +40 -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.d.ts +68 -0
- package/dist/src/cli/gate.js +557 -0
- package/dist/src/cli/gate.js.map +1 -0
- package/dist/src/cli/git-scope.d.ts +190 -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.d.ts +85 -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.d.ts +9 -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.d.ts +24 -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.d.ts +42 -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.d.ts +265 -0
- package/dist/src/cli/gloss.js +362 -0
- package/dist/src/cli/gloss.js.map +1 -0
- package/dist/src/cli/help.d.ts +103 -0
- package/dist/src/cli/help.js +2339 -0
- package/dist/src/cli/help.js.map +1 -0
- package/dist/src/cli/hook-codex.d.ts +78 -0
- package/dist/src/cli/hook-codex.js +167 -0
- package/dist/src/cli/hook-codex.js.map +1 -0
- package/dist/src/cli/hook.d.ts +331 -0
- package/dist/src/cli/hook.js +2849 -0
- package/dist/src/cli/hook.js.map +1 -0
- package/dist/src/cli/import.d.ts +35 -0
- package/dist/src/cli/import.js +175 -0
- package/dist/src/cli/import.js.map +1 -0
- package/dist/src/cli/init.d.ts +84 -0
- package/dist/src/cli/init.js +336 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/instructions.d.ts +23 -0
- package/dist/src/cli/instructions.js +262 -0
- package/dist/src/cli/instructions.js.map +1 -0
- package/dist/src/cli/journal.d.ts +41 -0
- package/dist/src/cli/journal.js +238 -0
- package/dist/src/cli/journal.js.map +1 -0
- package/dist/src/cli/log-advance.d.ts +287 -0
- package/dist/src/cli/log-advance.js +840 -0
- package/dist/src/cli/log-advance.js.map +1 -0
- package/dist/src/cli/log-anchor.d.ts +176 -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.d.ts +22 -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.d.ts +243 -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.d.ts +16 -0
- package/dist/src/cli/log-verbs.js +360 -0
- package/dist/src/cli/log-verbs.js.map +1 -0
- package/dist/src/cli/long-help.d.ts +70 -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.d.ts +77 -0
- package/dist/src/cli/main.js +1206 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/cli/mcp.d.ts +52 -0
- package/dist/src/cli/mcp.js +306 -0
- package/dist/src/cli/mcp.js.map +1 -0
- package/dist/src/cli/paths.d.ts +56 -0
- package/dist/src/cli/paths.js +79 -0
- package/dist/src/cli/paths.js.map +1 -0
- package/dist/src/cli/payload.d.ts +58 -0
- package/dist/src/cli/payload.js +253 -0
- package/dist/src/cli/payload.js.map +1 -0
- package/dist/src/cli/policy.d.ts +43 -0
- package/dist/src/cli/policy.js +229 -0
- package/dist/src/cli/policy.js.map +1 -0
- package/dist/src/cli/preflight.d.ts +363 -0
- package/dist/src/cli/preflight.js +1175 -0
- package/dist/src/cli/preflight.js.map +1 -0
- package/dist/src/cli/progress.d.ts +78 -0
- package/dist/src/cli/progress.js +112 -0
- package/dist/src/cli/progress.js.map +1 -0
- package/dist/src/cli/prompt.d.ts +209 -0
- package/dist/src/cli/prompt.js +312 -0
- package/dist/src/cli/prompt.js.map +1 -0
- package/dist/src/cli/quickstart.d.ts +46 -0
- package/dist/src/cli/quickstart.js +297 -0
- package/dist/src/cli/quickstart.js.map +1 -0
- package/dist/src/cli/records.d.ts +34 -0
- package/dist/src/cli/records.js +66 -0
- package/dist/src/cli/records.js.map +1 -0
- package/dist/src/cli/render.d.ts +22 -0
- package/dist/src/cli/render.js +132 -0
- package/dist/src/cli/render.js.map +1 -0
- package/dist/src/cli/sandbox.d.ts +51 -0
- package/dist/src/cli/sandbox.js +150 -0
- package/dist/src/cli/sandbox.js.map +1 -0
- package/dist/src/cli/scaffold.d.ts +79 -0
- package/dist/src/cli/scaffold.js +137 -0
- package/dist/src/cli/scaffold.js.map +1 -0
- package/dist/src/cli/setup-adapter.d.ts +137 -0
- package/dist/src/cli/setup-adapter.js +509 -0
- package/dist/src/cli/setup-adapter.js.map +1 -0
- package/dist/src/cli/setup-channel.d.ts +117 -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.d.ts +57 -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.d.ts +275 -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.d.ts +287 -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.d.ts +96 -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.d.ts +202 -0
- package/dist/src/cli/setup.js +473 -0
- package/dist/src/cli/setup.js.map +1 -0
- package/dist/src/cli/style.d.ts +320 -0
- package/dist/src/cli/style.js +469 -0
- package/dist/src/cli/style.js.map +1 -0
- package/dist/src/cli/token.d.ts +39 -0
- package/dist/src/cli/token.js +274 -0
- package/dist/src/cli/token.js.map +1 -0
- package/dist/src/cli/up.d.ts +155 -0
- package/dist/src/cli/up.js +849 -0
- package/dist/src/cli/up.js.map +1 -0
- package/dist/src/cli/usage.d.ts +37 -0
- package/dist/src/cli/usage.js +91 -0
- package/dist/src/cli/usage.js.map +1 -0
- package/dist/src/cli/values.d.ts +40 -0
- package/dist/src/cli/values.js +189 -0
- package/dist/src/cli/values.js.map +1 -0
- package/dist/src/cli/vault.d.ts +59 -0
- package/dist/src/cli/vault.js +362 -0
- package/dist/src/cli/vault.js.map +1 -0
- package/dist/src/cli/verb-registry.d.ts +76 -0
- package/dist/src/cli/verb-registry.js +2341 -0
- package/dist/src/cli/verb-registry.js.map +1 -0
- package/dist/src/cli/wordmark.d.ts +31 -0
- package/dist/src/cli/wordmark.js +52 -0
- package/dist/src/cli/wordmark.js.map +1 -0
- package/dist/src/codex/doctor.d.ts +13 -0
- package/dist/src/codex/doctor.js +41 -0
- package/dist/src/codex/doctor.js.map +1 -0
- package/dist/src/codex/manifest.d.ts +49 -0
- package/dist/src/codex/manifest.js +103 -0
- package/dist/src/codex/manifest.js.map +1 -0
- package/dist/src/codex/templates.d.ts +41 -0
- package/dist/src/codex/templates.js +319 -0
- package/dist/src/codex/templates.js.map +1 -0
- package/dist/src/codex/trust.d.ts +19 -0
- package/dist/src/codex/trust.js +183 -0
- package/dist/src/codex/trust.js.map +1 -0
- package/dist/src/codex/workspace-plan.d.ts +131 -0
- package/dist/src/codex/workspace-plan.js +561 -0
- package/dist/src/codex/workspace-plan.js.map +1 -0
- package/dist/src/core/actor.d.ts +2 -0
- package/dist/src/core/actor.js +5 -0
- package/dist/src/core/actor.js.map +1 -0
- package/dist/src/core/advance-cycle.d.ts +170 -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.d.ts +276 -0
- package/dist/src/core/agents-md.js +747 -0
- package/dist/src/core/agents-md.js.map +1 -0
- package/dist/src/core/apply-patch.d.ts +49 -0
- package/dist/src/core/apply-patch.js +266 -0
- package/dist/src/core/apply-patch.js.map +1 -0
- package/dist/src/core/attest.d.ts +420 -0
- package/dist/src/core/attest.js +589 -0
- package/dist/src/core/attest.js.map +1 -0
- package/dist/src/core/audit.d.ts +492 -0
- package/dist/src/core/audit.js +882 -0
- package/dist/src/core/audit.js.map +1 -0
- package/dist/src/core/budgets.d.ts +238 -0
- package/dist/src/core/budgets.js +449 -0
- package/dist/src/core/budgets.js.map +1 -0
- package/dist/src/core/checkpoint.d.ts +500 -0
- package/dist/src/core/checkpoint.js +738 -0
- package/dist/src/core/checkpoint.js.map +1 -0
- package/dist/src/core/child-env.d.ts +88 -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.d.ts +52 -0
- package/dist/src/core/clock.js +43 -0
- package/dist/src/core/clock.js.map +1 -0
- package/dist/src/core/command-class.d.ts +543 -0
- package/dist/src/core/command-class.js +2356 -0
- package/dist/src/core/command-class.js.map +1 -0
- package/dist/src/core/coverage-sources/adapter.d.ts +40 -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.d.ts +48 -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.d.ts +101 -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.d.ts +217 -0
- package/dist/src/core/coverage.js +337 -0
- package/dist/src/core/coverage.js.map +1 -0
- package/dist/src/core/credential-spec.d.ts +72 -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.d.ts +331 -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.d.ts +185 -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.d.ts +450 -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.d.ts +858 -0
- package/dist/src/core/execute.js +1271 -0
- package/dist/src/core/execute.js.map +1 -0
- package/dist/src/core/frontmatter.d.ts +78 -0
- package/dist/src/core/frontmatter.js +100 -0
- package/dist/src/core/frontmatter.js.map +1 -0
- package/dist/src/core/gate-window.d.ts +312 -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.d.ts +1364 -0
- package/dist/src/core/gate.js +3002 -0
- package/dist/src/core/gate.js.map +1 -0
- package/dist/src/core/git-run.d.ts +73 -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.d.ts +157 -0
- package/dist/src/core/harness-version.js +211 -0
- package/dist/src/core/harness-version.js.map +1 -0
- package/dist/src/core/harness-wait.d.ts +55 -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.d.ts +107 -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.d.ts +253 -0
- package/dist/src/core/instance.js +319 -0
- package/dist/src/core/instance.js.map +1 -0
- package/dist/src/core/intake-limits.d.ts +247 -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.d.ts +52 -0
- package/dist/src/core/jcs.js +132 -0
- package/dist/src/core/jcs.js.map +1 -0
- package/dist/src/core/journal.d.ts +144 -0
- package/dist/src/core/journal.js +200 -0
- package/dist/src/core/journal.js.map +1 -0
- package/dist/src/core/live-draw.d.ts +436 -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.d.ts +89 -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-subscribe.d.ts +36 -0
- package/dist/src/core/log-subscribe.js +162 -0
- package/dist/src/core/log-subscribe.js.map +1 -0
- package/dist/src/core/log.d.ts +278 -0
- package/dist/src/core/log.js +546 -0
- package/dist/src/core/log.js.map +1 -0
- package/dist/src/core/loop.d.ts +274 -0
- package/dist/src/core/loop.js +487 -0
- package/dist/src/core/loop.js.map +1 -0
- package/dist/src/core/md-fence.d.ts +41 -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.d.ts +147 -0
- package/dist/src/core/money.js +195 -0
- package/dist/src/core/money.js.map +1 -0
- package/dist/src/core/payload-census.d.ts +74 -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.d.ts +175 -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.d.ts +71 -0
- package/dist/src/core/payload.js +80 -0
- package/dist/src/core/payload.js.map +1 -0
- package/dist/src/core/policy-diff.d.ts +292 -0
- package/dist/src/core/policy-diff.js +588 -0
- package/dist/src/core/policy-diff.js.map +1 -0
- package/dist/src/core/policy-expectations.d.ts +199 -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.d.ts +150 -0
- package/dist/src/core/policy-explain.js +258 -0
- package/dist/src/core/policy-explain.js.map +1 -0
- package/dist/src/core/policy-load.d.ts +527 -0
- package/dist/src/core/policy-load.js +536 -0
- package/dist/src/core/policy-load.js.map +1 -0
- package/dist/src/core/policy-match.d.ts +281 -0
- package/dist/src/core/policy-match.js +478 -0
- package/dist/src/core/policy-match.js.map +1 -0
- package/dist/src/core/policy-proposal.d.ts +265 -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.d.ts +221 -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.d.ts +453 -0
- package/dist/src/core/protected-path-guard.js +1566 -0
- package/dist/src/core/protected-path-guard.js.map +1 -0
- package/dist/src/core/registration.d.ts +25 -0
- package/dist/src/core/registration.js +39 -0
- package/dist/src/core/registration.js.map +1 -0
- package/dist/src/core/reindex.d.ts +99 -0
- package/dist/src/core/reindex.js +336 -0
- package/dist/src/core/reindex.js.map +1 -0
- package/dist/src/core/sampler.d.ts +313 -0
- package/dist/src/core/sampler.js +388 -0
- package/dist/src/core/sampler.js.map +1 -0
- package/dist/src/core/sandbox.d.ts +290 -0
- package/dist/src/core/sandbox.js +424 -0
- package/dist/src/core/sandbox.js.map +1 -0
- package/dist/src/core/seal.d.ts +165 -0
- package/dist/src/core/seal.js +290 -0
- package/dist/src/core/seal.js.map +1 -0
- package/dist/src/core/state.d.ts +505 -0
- package/dist/src/core/state.js +1009 -0
- package/dist/src/core/state.js.map +1 -0
- package/dist/src/core/task-file.d.ts +185 -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.d.ts +93 -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.d.ts +409 -0
- package/dist/src/core/token.js +561 -0
- package/dist/src/core/token.js.map +1 -0
- package/dist/src/core/validate.d.ts +138 -0
- package/dist/src/core/validate.js +0 -0
- package/dist/src/core/validate.js.map +1 -0
- package/dist/src/core/values.d.ts +137 -0
- package/dist/src/core/values.js +153 -0
- package/dist/src/core/values.js.map +1 -0
- package/dist/src/core/vault.d.ts +291 -0
- package/dist/src/core/vault.js +612 -0
- package/dist/src/core/vault.js.map +1 -0
- package/dist/src/core/verified-snapshot.d.ts +204 -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.d.ts +336 -0
- package/dist/src/core/verify.js +549 -0
- package/dist/src/core/verify.js.map +1 -0
- package/dist/src/core/version.d.ts +8 -0
- package/dist/src/core/version.js +9 -0
- package/dist/src/core/version.js.map +1 -0
- package/dist/src/core/wysiwys.d.ts +370 -0
- package/dist/src/core/wysiwys.js +728 -0
- package/dist/src/core/wysiwys.js.map +1 -0
- package/dist/src/daemon/advance-child.d.ts +39 -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.d.ts +466 -0
- package/dist/src/daemon/advance.js +849 -0
- package/dist/src/daemon/advance.js.map +1 -0
- package/dist/src/daemon/audit.d.ts +87 -0
- package/dist/src/daemon/audit.js +90 -0
- package/dist/src/daemon/audit.js.map +1 -0
- package/dist/src/daemon/daemon.d.ts +1180 -0
- package/dist/src/daemon/daemon.js +1988 -0
- package/dist/src/daemon/daemon.js.map +1 -0
- package/dist/src/daemon/dark-session.d.ts +64 -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.d.ts +36 -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.d.ts +154 -0
- package/dist/src/daemon/draw.js +458 -0
- package/dist/src/daemon/draw.js.map +1 -0
- package/dist/src/daemon/git-evidence.d.ts +173 -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.d.ts +180 -0
- package/dist/src/daemon/projection.js +233 -0
- package/dist/src/daemon/projection.js.map +1 -0
- package/dist/src/daemon/prune.d.ts +207 -0
- package/dist/src/daemon/prune.js +376 -0
- package/dist/src/daemon/prune.js.map +1 -0
- package/dist/src/mcp/http.d.ts +113 -0
- package/dist/src/mcp/http.js +343 -0
- package/dist/src/mcp/http.js.map +1 -0
- package/dist/src/mcp/server.d.ts +265 -0
- package/dist/src/mcp/server.js +602 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/docs/adapter-api.md +106 -0
- package/docs/cli-reference.md +5716 -0
- package/docs/codex-enforced-session.md +30 -0
- package/package.json +53 -4
- package/schema/.gitkeep +0 -0
- package/schema/LICENSE +117 -0
- package/schema/codex-instance.schema.json +82 -0
- package/schema/envelope.schema.json +137 -0
- package/schema/event.schema.json +1811 -0
- package/schema/fixtures/codex-instance/invalid/unpinned-codex-version.json +40 -0
- package/schema/fixtures/codex-instance/valid/canonical.json +40 -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 +501 -0
- package/schema/sample-record.schema.json +26 -0
- package/schema/values.schema.json +55 -0
- package/templates/codex/README.md +9 -0
|
@@ -0,0 +1,849 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The daemon's log advance, on a cadence and through the gate (APRV-204).
|
|
3
|
+
*
|
|
4
|
+
* ## Why the daemon does this at all
|
|
5
|
+
*
|
|
6
|
+
* The committed log is this project's truth, and until this module its
|
|
7
|
+
* freshness depended on somebody remembering to run `approval log advance`. The
|
|
8
|
+
* daemon is already the committed log's sole writer in the primary checkout, it
|
|
9
|
+
* is already awake, and since the seq 7413 ceremony `log.advance` resolves
|
|
10
|
+
* `supervised-live 0.1` — a class that needs no hand on the keyboard for nine
|
|
11
|
+
* runs in ten. So the bookkeeping moves here, and the human's part shrinks to
|
|
12
|
+
* the tenth run and the merge.
|
|
13
|
+
*
|
|
14
|
+
* ## It borrows every mechanism and owns none
|
|
15
|
+
*
|
|
16
|
+
* - The advance itself is `cli/log-advance.ts`'s `logAdvance`, called
|
|
17
|
+
* unmodified: the same append lock, the same chain verify, the same staged-set
|
|
18
|
+
* refusal, the same commit-on-the-remote-without-a-checkout. A second
|
|
19
|
+
* implementation would be a second thing to get wrong about the one file
|
|
20
|
+
* nothing may rewind.
|
|
21
|
+
* - The authorization is `core/gate.ts`'s `register` + `request` and
|
|
22
|
+
* `core/execute.ts`'s `startExecution` / `finishExecution`, in that order and
|
|
23
|
+
* with no shortcuts. The daemon proposes as `agent:daemon` and the runtime
|
|
24
|
+
* decides, exactly as it decides for a session.
|
|
25
|
+
* - The cadence and the reporting are the only decisions here, and neither
|
|
26
|
+
* touches the log.
|
|
27
|
+
*
|
|
28
|
+
* ## What happens when the gate says no
|
|
29
|
+
*
|
|
30
|
+
* A `supervised-live` draw that SELECTS this advance, a class amended to
|
|
31
|
+
* `manual`, a policy that will not load: all three end the attempt with nothing
|
|
32
|
+
* committed and nothing pushed. The outcome is reported on the daemon's event
|
|
33
|
+
* stream and readable afterwards from the log itself, and the next tick tries
|
|
34
|
+
* again — after the cadence interval, never immediately, because the
|
|
35
|
+
* last-attempt clock is set by a refusal exactly as it is by a success. There
|
|
36
|
+
* is no retry loop and no backoff ladder: the cadence IS the backoff.
|
|
37
|
+
*
|
|
38
|
+
* ## The merge: armed here, performed by nobody (APRV-284)
|
|
39
|
+
*
|
|
40
|
+
* This file still calls no merge. What it does is ask `logAdvance` to ARM one:
|
|
41
|
+
* `gh pr merge <branch> --merge --auto` on the day's records pull request, so
|
|
42
|
+
* the pull request lands when its checks and the repository's branch rules say
|
|
43
|
+
* it may, and never earlier. Before APRV-284 it sat at CLEAN until a person
|
|
44
|
+
* clicked, which is the failure mode CLAUDE.md's workflow item 7 is written
|
|
45
|
+
* against — and clicking was never a review, because a records branch carries
|
|
46
|
+
* only the log, `QUEUE.md` and `.approval/payloads/`, the three paths CI's
|
|
47
|
+
* protected-path guard exempts.
|
|
48
|
+
*
|
|
49
|
+
* Two things keep that from being a daemon approving its own evidence. The arm
|
|
50
|
+
* is `vcs.push.main` in this project's taxonomy — the same class as the same
|
|
51
|
+
* command in a session's hands — and it rides the SAME `log.advance`
|
|
52
|
+
* authorization this cycle already holds rather than opening a second question
|
|
53
|
+
* or claiming a second grant: one authorization, one act. And `cli/log-advance.
|
|
54
|
+
* ts`'s `armAutoMerge` withholds the arm outright when the branch it pushed
|
|
55
|
+
* carries a path an advance may not carry, so what gets armed is only ever
|
|
56
|
+
* evidence. `--no-advance-auto-merge` turns the whole thing off.
|
|
57
|
+
*
|
|
58
|
+
* ## The self-perpetuation trap, and how the trigger avoids it
|
|
59
|
+
*
|
|
60
|
+
* One advance cycle appends three records of its own (`task.registered`,
|
|
61
|
+
* `execution.started`, `execution.completed`). Two of them land before the
|
|
62
|
+
* commit and ride it; the third lands after, so every successful advance leaves
|
|
63
|
+
* the log one record ahead of the records branch. A trigger that counted those
|
|
64
|
+
* would advance forever on an idle repository. So the TRIGGER counts only
|
|
65
|
+
* records that are not an advance's own bookkeeping (`PublishedState.substantive`),
|
|
66
|
+
* while the count REPORTED to an operator is the honest raw one.
|
|
67
|
+
*
|
|
68
|
+
* ## Where the pieces live
|
|
69
|
+
*
|
|
70
|
+
* The cycle's vocabulary (the actor, the class, the task and key shapes, and
|
|
71
|
+
* reading the last cycle back out of the log) is `core/advance-cycle.ts`, and
|
|
72
|
+
* the git-side "what is already published" question is `cli/log-advance.ts`.
|
|
73
|
+
* Both are shared with `approval doctor`, which reports the same numbers in a
|
|
74
|
+
* different process — and a CLI module may not import the daemon.
|
|
75
|
+
*/
|
|
76
|
+
import { spawn } from "node:child_process";
|
|
77
|
+
import { fileURLToPath } from "node:url";
|
|
78
|
+
import { tick } from "../core/clock.js";
|
|
79
|
+
import { ADVANCE_ACTOR, ADVANCE_CLASS, RESOLVE_DANGLING_COMMAND, advanceActionKey, advanceTaskId, danglingAdvances, openAdvanceRequest, proveDanglingAdvances, } from "../core/advance-cycle.js";
|
|
80
|
+
import { childEnvironment } from "../core/child-env.js";
|
|
81
|
+
import { finishExecution, startExecution, } from "../core/execute.js";
|
|
82
|
+
import { register, request } from "../core/gate.js";
|
|
83
|
+
import { attemptsOf, withHeadRetry } from "../core/head-retry.js";
|
|
84
|
+
import { payloadHash } from "../core/payload.js";
|
|
85
|
+
import { loadPolicy } from "../core/policy-load.js";
|
|
86
|
+
import { defaultRecordsBranch, logAdvance, publishedState, } from "../cli/log-advance.js";
|
|
87
|
+
import { repoRoot } from "../cli/git-scope.js";
|
|
88
|
+
/**
|
|
89
|
+
* How often the daemon advances, absent the record-count trigger: 15 minutes.
|
|
90
|
+
*
|
|
91
|
+
* Chosen against what a records pull request costs and what staleness costs. A
|
|
92
|
+
* shorter interval publishes a pull request whose diff is a handful of lines
|
|
93
|
+
* and burns a CI run for each; a longer one leaves the guards that read the
|
|
94
|
+
* committed log (the CI protected-path cross-check especially) looking at a log
|
|
95
|
+
* that is hours behind the decisions it is supposed to evidence.
|
|
96
|
+
*/
|
|
97
|
+
export const DEFAULT_ADVANCE_INTERVAL_MS = 900_000;
|
|
98
|
+
/**
|
|
99
|
+
* How many unpublished records force an advance before the interval elapses: 20.
|
|
100
|
+
*
|
|
101
|
+
* The busy-hour case. Twenty records is a few minutes of an active session, and
|
|
102
|
+
* a records branch that far behind is a records branch a person starts doing
|
|
103
|
+
* arithmetic against.
|
|
104
|
+
*/
|
|
105
|
+
export const DEFAULT_ADVANCE_AFTER_RECORDS = 20;
|
|
106
|
+
/** The default cadence, spelled once. */
|
|
107
|
+
export function defaultCadence() {
|
|
108
|
+
return {
|
|
109
|
+
intervalMs: DEFAULT_ADVANCE_INTERVAL_MS,
|
|
110
|
+
afterRecords: DEFAULT_ADVANCE_AFTER_RECORDS,
|
|
111
|
+
remote: "origin",
|
|
112
|
+
base: null,
|
|
113
|
+
pr: true,
|
|
114
|
+
autoMerge: true,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// One gated attempt
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
/** The argv the payload binds to: the verb this attempt is authorization for. */
|
|
121
|
+
export function advanceArgv(cadence) {
|
|
122
|
+
return [
|
|
123
|
+
"approval",
|
|
124
|
+
"log",
|
|
125
|
+
"advance",
|
|
126
|
+
...(cadence.pr ? ["--pr"] : []),
|
|
127
|
+
// APRV-284. Present only when the arm is OFF, so the default cadence's argv
|
|
128
|
+
// is byte-identical to what it was before the arm existed and no payload
|
|
129
|
+
// hash or idempotency key moves under a running daemon.
|
|
130
|
+
...(cadence.pr && !cadence.autoMerge ? ["--no-auto-merge"] : []),
|
|
131
|
+
"--remote",
|
|
132
|
+
cadence.remote,
|
|
133
|
+
...(cadence.base === null ? [] : ["--base", cadence.base]),
|
|
134
|
+
];
|
|
135
|
+
}
|
|
136
|
+
function detail(cause) {
|
|
137
|
+
return cause instanceof Error ? cause.message : String(cause);
|
|
138
|
+
}
|
|
139
|
+
/** Build an {@link AdvanceAttempt} with this attempt's fixed fields filled in. */
|
|
140
|
+
function answerFor(ts, recordsPending, recordsBranch) {
|
|
141
|
+
return (over) => ({
|
|
142
|
+
ts,
|
|
143
|
+
recordsPending,
|
|
144
|
+
recordsBranch,
|
|
145
|
+
commit: null,
|
|
146
|
+
prUrl: null,
|
|
147
|
+
prCreated: false,
|
|
148
|
+
autoMerge: null,
|
|
149
|
+
autoMergeNote: null,
|
|
150
|
+
range: null,
|
|
151
|
+
code: null,
|
|
152
|
+
rebuilt: false,
|
|
153
|
+
rebuiltOn: null,
|
|
154
|
+
pendingFinish: null,
|
|
155
|
+
...over,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
// The bounded head-moved retry on the outcome record (APRV-233)
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
/**
|
|
162
|
+
* Append the advance's outcome, re-deriving it against a fresh head on
|
|
163
|
+
* `head-moved` (APRV-233).
|
|
164
|
+
*
|
|
165
|
+
* ## What went wrong without it
|
|
166
|
+
*
|
|
167
|
+
* Observed 2026-09-02 on Carter's terminal: the advance pushed
|
|
168
|
+
* `records-log-2026-09-02`, and then `execution.completed could not be
|
|
169
|
+
* appended: head moved` — a harness hook's record landed between this module's
|
|
170
|
+
* read and its append, the compare-and-append refused the stale write exactly
|
|
171
|
+
* as SPEC.md §11.1 invariant 5 requires, and `daemon-log-advance-1-13984` was
|
|
172
|
+
* left open. Every other writer on this path has re-derived and re-attempted
|
|
173
|
+
* since APRV-150; this one did not, and the cost of losing the race was an
|
|
174
|
+
* execution the log said had never ended.
|
|
175
|
+
*
|
|
176
|
+
* ## What an attempt is
|
|
177
|
+
*
|
|
178
|
+
* The WHOLE operation, not a re-append: {@link finishExecution} re-reads the
|
|
179
|
+
* verified log, re-runs its `not-started` / `already-finished` /
|
|
180
|
+
* `execution-delegated` checks against that read, and appends with the head
|
|
181
|
+
* THAT read observed. Nothing crosses an attempt except the outcome the caller
|
|
182
|
+
* observed, which is a fact about the world rather than a conclusion about the
|
|
183
|
+
* log, so no stale derivation can authorize a write. A verdict that genuinely
|
|
184
|
+
* changed in the window (another writer closed the execution first) is the
|
|
185
|
+
* verdict returned, and the bound is returned unchanged once it is spent, so
|
|
186
|
+
* the caller still fails closed.
|
|
187
|
+
*
|
|
188
|
+
* ## One retry, in one place
|
|
189
|
+
*
|
|
190
|
+
* The loop and the bound are `core/head-retry.ts`'s (APRV-236 lifted them there
|
|
191
|
+
* for every gate writer, and APRV-233's first draft of this function was the
|
|
192
|
+
* local copy that helper replaced). `attemptsOf` clamps a caller's
|
|
193
|
+
* `retryOnHeadMoved` downward only, so the test harness can pin the unretried
|
|
194
|
+
* shape at `1` and nothing can raise the ceiling; `withHeadRetry` re-runs the
|
|
195
|
+
* whole `finishExecution` and adds the attempt count to the message it finally
|
|
196
|
+
* hands back. What remains here is only which operation is retried.
|
|
197
|
+
*/
|
|
198
|
+
function finishWithHeadMovedRetry(logPath, actionKey, exitCode, options) {
|
|
199
|
+
return withHeadRetry(attemptsOf(options.retryOnHeadMoved), () => finishExecution(logPath, actionKey, exitCode, ADVANCE_ACTOR, options));
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Close an execution whose outcome an EARLIER tick observed and could not
|
|
203
|
+
* record (APRV-233).
|
|
204
|
+
*
|
|
205
|
+
* The second half of the fix, and the one that matters after a restart of the
|
|
206
|
+
* loop rather than of the process: the outcome is carried in memory as a
|
|
207
|
+
* {@link PendingAdvanceFinish} and re-attempted here, on the fresh head,
|
|
208
|
+
* before the next tick evaluates any trigger. It appends the same record the
|
|
209
|
+
* failed attempt would have appended, through the same bounded retry.
|
|
210
|
+
*
|
|
211
|
+
* `already-finished` is a SUCCESS here: something (a concurrent settle, a
|
|
212
|
+
* human's `approval execution resolve`) closed the cycle in the meantime, the
|
|
213
|
+
* log holds an outcome, and there is nothing left to carry.
|
|
214
|
+
*/
|
|
215
|
+
export function settleAdvanceFinish(input, pending) {
|
|
216
|
+
const result = finishWithHeadMovedRetry(input.logPath, pending.actionKey, pending.exitCode, {
|
|
217
|
+
policy: input.policy,
|
|
218
|
+
...(input.schemaDir === undefined ? {} : { schemaDir: input.schemaDir }),
|
|
219
|
+
...(input.clock === undefined ? {} : { clock: input.clock }),
|
|
220
|
+
...(input.retryOnHeadMoved === undefined ? {} : { retryOnHeadMoved: input.retryOnHeadMoved }),
|
|
221
|
+
...(input.afterFinishRead === undefined ? {} : { afterRead: input.afterFinishRead }),
|
|
222
|
+
...(pending.reason === undefined ? {} : { reason: pending.reason }),
|
|
223
|
+
...(pending.note === undefined ? {} : { note: pending.note }),
|
|
224
|
+
});
|
|
225
|
+
if (result.ok) {
|
|
226
|
+
return {
|
|
227
|
+
ok: true,
|
|
228
|
+
code: null,
|
|
229
|
+
message: `the outcome of ${pending.actionKey} is recorded (${result.event})`,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
if (result.code === "already-finished") {
|
|
233
|
+
return {
|
|
234
|
+
ok: true,
|
|
235
|
+
code: null,
|
|
236
|
+
message: `${pending.actionKey} was already closed by another writer; nothing was appended`,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
ok: false,
|
|
241
|
+
code: result.code,
|
|
242
|
+
message: `the outcome of ${pending.actionKey} still could not be recorded: ${result.message}`,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Every open advance cycle, with what this checkout's refs prove (APRV-264).
|
|
247
|
+
*
|
|
248
|
+
* One `publishedState` for the whole list rather than one per key: it is the
|
|
249
|
+
* git read on the tick's hot path, and the answer it gives is the same for
|
|
250
|
+
* every key — the highest published seq, and the ref that carries it.
|
|
251
|
+
*
|
|
252
|
+
* Read from the log rather than from this process's memory, so a daemon that
|
|
253
|
+
* restarted still sees that an advance HAPPENED. It is the fact the cadence was
|
|
254
|
+
* missing on 2026-09-02: with no outcome record, nothing but an in-process
|
|
255
|
+
* clock said the branch had just been pushed.
|
|
256
|
+
*/
|
|
257
|
+
export function proveDanglingAdvancesFor(input, records) {
|
|
258
|
+
const open = danglingAdvances(records);
|
|
259
|
+
if (open.length === 0)
|
|
260
|
+
return [];
|
|
261
|
+
const root = repoRoot(input.cwd) ?? input.cwd;
|
|
262
|
+
const today = input.today ?? tick(input.clock === undefined ? {} : { clock: input.clock });
|
|
263
|
+
const state = publishedState(root, input.logPath, records, input.cadence, today);
|
|
264
|
+
return proveDanglingAdvances(records, state);
|
|
265
|
+
}
|
|
266
|
+
/** Is this sweep's work done — nothing open, nothing owed to a person? */
|
|
267
|
+
export function sweepIsClear(sweep) {
|
|
268
|
+
return sweep.settled.length === 0 && sweep.outstanding.length === 0;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Close EVERY dangling advance cycle whose outcome the git refs still show
|
|
272
|
+
* (APRV-233, widened by APRV-264).
|
|
273
|
+
*
|
|
274
|
+
* ## The loop this breaks
|
|
275
|
+
*
|
|
276
|
+
* From Carter's terminal, 2026-09-02, with the APRV-211 build running: tick two
|
|
277
|
+
* advanced and pushed; its `execution.completed` lost the head race (expected
|
|
278
|
+
* 14874, found 14875) and `daemon-log-advance-1-14871` was left open. Tick
|
|
279
|
+
* three's authorization then reached `startExecution` on that same key and was
|
|
280
|
+
* refused `already-executed: an idempotency key is single-use and nothing here
|
|
281
|
+
* reconciles or reruns it` — which is the gate saying, correctly, that
|
|
282
|
+
* somebody has to close the books. Nobody did, so the span moved and the daemon
|
|
283
|
+
* pushed again, and again, every ninety seconds under a fifteen-minute
|
|
284
|
+
* interval.
|
|
285
|
+
*
|
|
286
|
+
* ## Why it sweeps rather than reconciles one
|
|
287
|
+
*
|
|
288
|
+
* APRV-233's version closed the LAST open cycle and returned. On 2026-09-05
|
|
289
|
+
* that was not enough: `approval status` listed FIVE, left by the 2026-09-02
|
|
290
|
+
* loop and by the restarts after it, the daemon refused one advance per tick
|
|
291
|
+
* naming one key each, and Carter closed all five by hand with five
|
|
292
|
+
* near-identical commands. One key per tick is a repair rate of one every
|
|
293
|
+
* thirty seconds behind a refusal that stops the cadence entirely, so the sweep
|
|
294
|
+
* takes every key it can prove in one pass and reports the rest together.
|
|
295
|
+
*
|
|
296
|
+
* ## What is and is not a guess here
|
|
297
|
+
*
|
|
298
|
+
* {@link settleAdvanceFinish} covers the case this process still holds the
|
|
299
|
+
* outcome for. This covers the other one — cycles left open by an earlier
|
|
300
|
+
* process, or by ticks whose memory of them is gone — and it closes each only
|
|
301
|
+
* on EVIDENCE: the records the cycle was authorized to publish are demonstrably
|
|
302
|
+
* on a records branch, read from git's own object store through the same
|
|
303
|
+
* `publishedState` the cadence and the doctor row read. When the evidence is
|
|
304
|
+
* there the outcome is recorded as the completion it was, with a note saying
|
|
305
|
+
* which ref proved it and that the RUNTIME observed it. When it is NOT there,
|
|
306
|
+
* nothing is written: the execution stays open for a person, the daemon warns
|
|
307
|
+
* once, and no new advance is started over work nobody has accounted for. A
|
|
308
|
+
* false `execution.failed` for an advance that actually published would be
|
|
309
|
+
* worse than the dangling record.
|
|
310
|
+
*
|
|
311
|
+
* The actor is {@link ADVANCE_ACTOR} and the record carries no
|
|
312
|
+
* `attested_by_human`: human attestation is for what a person went and looked
|
|
313
|
+
* at, and this is the runtime reading its own refs. The note says so in as many
|
|
314
|
+
* words, so no reader has to infer it from the actor field alone.
|
|
315
|
+
*
|
|
316
|
+
* This is a deliberate, narrow carve-out to `core/execute.ts`'s rule that
|
|
317
|
+
* nothing closes a dangling execution automatically. Narrow because it closes
|
|
318
|
+
* only executions THIS runtime started, for its own `log.advance` cycles, whose
|
|
319
|
+
* entire effect is a git ref this runtime can look at and did.
|
|
320
|
+
*/
|
|
321
|
+
export function sweepDanglingAdvances(input, records) {
|
|
322
|
+
const sweep = { settled: [], outstanding: [] };
|
|
323
|
+
// One git read for the whole sweep. Appending an outcome below cannot change
|
|
324
|
+
// the answer: `publishedState` counts only a committed copy that is a PREFIX
|
|
325
|
+
// of the working log, so a record appended after it was read leaves both the
|
|
326
|
+
// published seq and the ref that carries it exactly where they were.
|
|
327
|
+
for (const entry of proveDanglingAdvancesFor(input, records)) {
|
|
328
|
+
if (entry.provenBy === null) {
|
|
329
|
+
sweep.outstanding.push({
|
|
330
|
+
actionKey: entry.actionKey,
|
|
331
|
+
provenBy: null,
|
|
332
|
+
message: `${entry.actionKey} is an advance execution nobody closed, and no ref in this checkout carries seq ${entry.toSeq === null ? "the span it named" : String(entry.toSeq)}. Nothing is recorded for it: an outcome this runtime cannot observe is a human's to establish.`,
|
|
333
|
+
});
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
const finished = finishWithHeadMovedRetry(input.logPath, entry.actionKey, 0, {
|
|
337
|
+
policy: input.policy,
|
|
338
|
+
...(input.schemaDir === undefined ? {} : { schemaDir: input.schemaDir }),
|
|
339
|
+
...(input.clock === undefined ? {} : { clock: input.clock }),
|
|
340
|
+
...(input.retryOnHeadMoved === undefined ? {} : { retryOnHeadMoved: input.retryOnHeadMoved }),
|
|
341
|
+
...(input.afterFinishRead === undefined ? {} : { afterRead: input.afterFinishRead }),
|
|
342
|
+
note: {
|
|
343
|
+
code: "advance-reconciled",
|
|
344
|
+
message: `the outcome record was lost when this cycle ran; ${entry.provenBy} carries seq ${String(entry.toSeq)} in this checkout, so the advance completed. Observed by the runtime (${ADVANCE_ACTOR}) from that ref, not attested by a human`,
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
if (finished.ok) {
|
|
348
|
+
sweep.settled.push({
|
|
349
|
+
actionKey: entry.actionKey,
|
|
350
|
+
provenBy: entry.provenBy,
|
|
351
|
+
message: `${entry.actionKey} was left open by an earlier advance; ${entry.provenBy} carries seq ${String(entry.toSeq)}, so its completion is now recorded`,
|
|
352
|
+
});
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
// `already-finished` is somebody else closing the books between the read
|
|
356
|
+
// and the append, which is the outcome this wanted and not a failure.
|
|
357
|
+
if (finished.code === "already-finished") {
|
|
358
|
+
sweep.settled.push({
|
|
359
|
+
actionKey: entry.actionKey,
|
|
360
|
+
provenBy: entry.provenBy,
|
|
361
|
+
message: `${entry.actionKey} was closed by another writer before this sweep reached it; nothing was appended`,
|
|
362
|
+
});
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
sweep.outstanding.push({
|
|
366
|
+
actionKey: entry.actionKey,
|
|
367
|
+
provenBy: entry.provenBy,
|
|
368
|
+
message: `${entry.actionKey} could not be closed: ${finished.message}`,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
return sweep;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* The refusal that stands while any advance cycle is open, naming all of them.
|
|
375
|
+
*
|
|
376
|
+
* One sentence for every outstanding key rather than one refusal per key per
|
|
377
|
+
* tick: the operator's next act is the same command whichever key they read
|
|
378
|
+
* first, so the command is on the line and every key it will close is under it.
|
|
379
|
+
*/
|
|
380
|
+
export function unreconciledRefusal(keys) {
|
|
381
|
+
return `${String(keys.length)} advance execution(s) were started and their outcomes were never recorded; nothing new is authorized until they are closed (${keys.join(", ")}). \`approval status\` shows them; \`${RESOLVE_DANGLING_COMMAND}\` closes every one this checkout can prove and lists the rest.`;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Register, request, start. The authorization, and not one byte of side effect.
|
|
385
|
+
*
|
|
386
|
+
* The order is the whole point and none of it is optional: nothing is committed
|
|
387
|
+
* before the gate has answered, and nothing the gate answered for goes
|
|
388
|
+
* unrecorded. `execution.started` is appended BEFORE the advance runs, so the
|
|
389
|
+
* commit the advance builds carries the record of its own authorization; the
|
|
390
|
+
* `execution.completed` that follows lands after the commit and is published by
|
|
391
|
+
* the next advance, which is why the trigger discounts it.
|
|
392
|
+
*
|
|
393
|
+
* ## What APRV-211 changed: the tick asks the log what it already asked
|
|
394
|
+
*
|
|
395
|
+
* Observed live on 2026-09-02: three ticks over ONE owed advance put three
|
|
396
|
+
* questions on Carter's phone, because the idempotency key embedded the log
|
|
397
|
+
* HEAD and the head moved with each gated attempt's own two records. Two things
|
|
398
|
+
* fix it and both are here.
|
|
399
|
+
*
|
|
400
|
+
* 1. The key and the payload span end at `substantiveSeq` — the last unpublished
|
|
401
|
+
* record that is not an advance cycle's own bookkeeping — so a tick that
|
|
402
|
+
* appended only its own question computes the SAME key and the SAME hash.
|
|
403
|
+
* 2. Before it registers anything, the tick reads {@link openAdvanceRequest}.
|
|
404
|
+
* A question still open is ADOPTED (nothing is appended, the outcome is
|
|
405
|
+
* `gated`, and the human keeps the one question they already have); a grant
|
|
406
|
+
* on it is SPENT (straight to `startExecution` on that key, with the hash
|
|
407
|
+
* that key declared, so the single-use rule the log already enforces makes
|
|
408
|
+
* one decision authorise exactly one advance); a terminal answer on the
|
|
409
|
+
* CURRENT owed span is HONOURED (`advance-decided`, and nothing is asked
|
|
410
|
+
* again until the span itself changes).
|
|
411
|
+
*/
|
|
412
|
+
export function authorizeAdvance(input, records) {
|
|
413
|
+
const ts = tick(input.clock === undefined ? {} : { clock: input.clock });
|
|
414
|
+
const today = input.today ?? ts;
|
|
415
|
+
const root = repoRoot(input.cwd) ?? input.cwd;
|
|
416
|
+
const recordsBranch = defaultRecordsBranch(today);
|
|
417
|
+
const state = publishedState(root, input.logPath, records, input.cadence, today);
|
|
418
|
+
const answer = answerFor(ts, state.pending, recordsBranch);
|
|
419
|
+
const no = (over) => ({ authorized: false, attempt: answer(over) });
|
|
420
|
+
if (state.pending === 0) {
|
|
421
|
+
return no({
|
|
422
|
+
outcome: "nothing-owed",
|
|
423
|
+
message: `every record through seq ${String(state.publishedSeq)} is already on a records branch`,
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
// APRV-233. An advance cycle nobody closed the books on. Reaching
|
|
427
|
+
// `startExecution` from here produced the bare `already-executed` Carter saw
|
|
428
|
+
// on 2026-09-02 — the gate saying, correctly, that an idempotency key is
|
|
429
|
+
// single-use and nothing there reconciles or reruns it — after which the span
|
|
430
|
+
// moved and the branch was pushed all over again. The sweep belongs to
|
|
431
|
+
// {@link sweepDanglingAdvances}, which the caller runs first; whatever is
|
|
432
|
+
// still here is still a human's, and nothing is authorized over it.
|
|
433
|
+
//
|
|
434
|
+
// APRV-264: EVERY outstanding key, and the one command that clears them. The
|
|
435
|
+
// refusal used to name the last one only, so an operator holding five of them
|
|
436
|
+
// learned about the second after they had closed the first.
|
|
437
|
+
const unreconciled = danglingAdvances(records).map((entry) => entry.actionKey);
|
|
438
|
+
if (unreconciled.length > 0) {
|
|
439
|
+
return no({
|
|
440
|
+
outcome: "refused",
|
|
441
|
+
code: "advance-unreconciled",
|
|
442
|
+
message: unreconciledRefusal(unreconciled),
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
const gate = { policy: input.policy };
|
|
446
|
+
if (input.schemaDir !== undefined)
|
|
447
|
+
gate.schemaDir = input.schemaDir;
|
|
448
|
+
if (input.clock !== undefined)
|
|
449
|
+
gate.clock = input.clock;
|
|
450
|
+
const task = advanceTaskId(state.substantiveSeq);
|
|
451
|
+
const actionKey = advanceActionKey(state.publishedSeq + 1, state.substantiveSeq);
|
|
452
|
+
// The bytes this authorization binds to: the command, where it runs, and the
|
|
453
|
+
// seq span it publishes. The span is IN the hash deliberately (APRV-204). A
|
|
454
|
+
// payload identical across cycles would give the `supervised-live` draw the
|
|
455
|
+
// same verdict forever — a selected advance would stay selected on every
|
|
456
|
+
// later tick and the cadence would stop for good — and a span is not a
|
|
457
|
+
// re-roll: different records are a different action, which is exactly what
|
|
458
|
+
// §5.2's no-re-roll property means by "changing the bytes is a different
|
|
459
|
+
// request".
|
|
460
|
+
//
|
|
461
|
+
// It ends at the OWED span's end rather than at the head (APRV-211). The
|
|
462
|
+
// advance publishes whatever the log holds when it runs, which is this span
|
|
463
|
+
// plus this cycle's own two or three bookkeeping records; naming the owed
|
|
464
|
+
// span is what makes one owed advance one question, and the bookkeeping is
|
|
465
|
+
// not work anybody is being asked to approve.
|
|
466
|
+
const hash = payloadHash({
|
|
467
|
+
argv: advanceArgv(input.cadence),
|
|
468
|
+
cwd: root,
|
|
469
|
+
seq: { from: state.publishedSeq + 1, to: state.substantiveSeq },
|
|
470
|
+
});
|
|
471
|
+
// What this daemon already asked, and what became of it. Read from the
|
|
472
|
+
// verified records the caller handed in — the enforcement path never reads an
|
|
473
|
+
// unverified log (SPEC.md §11.1) — and aged against the policy's own TTL, so a
|
|
474
|
+
// question whose window lapsed stops being adopted whether or not the
|
|
475
|
+
// `approval.expired` record has been materialised yet.
|
|
476
|
+
const loaded = loadPolicy({
|
|
477
|
+
...(input.policy.file === undefined ? {} : { file: input.policy.file }),
|
|
478
|
+
...(input.policy.dir === undefined ? {} : { dir: input.policy.dir }),
|
|
479
|
+
...(input.schemaDir === undefined ? {} : { schemaDir: input.schemaDir }),
|
|
480
|
+
});
|
|
481
|
+
const open = openAdvanceRequest(records, ts, loaded.ok ? loaded.durations.approvalTtlMs : null);
|
|
482
|
+
const adopted = open !== null && open.actionKey === actionKey;
|
|
483
|
+
if (open !== null && open.state === "requested") {
|
|
484
|
+
// Whatever the span has done since, a question is standing. Asking a second
|
|
485
|
+
// one would multiply the taps this cadence exists to remove, and answering
|
|
486
|
+
// the standing one authorises the advance the moment the next tick runs.
|
|
487
|
+
return no({
|
|
488
|
+
outcome: "gated",
|
|
489
|
+
code: "advance-open",
|
|
490
|
+
message: `an advance question is already open as ${open.actionKey} and nobody has answered it; nothing was asked again and nothing was committed. Answering it authorises the next tick's advance.`,
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
if (adopted && open !== null && open.state !== "granted") {
|
|
494
|
+
return no({
|
|
495
|
+
outcome: "refused",
|
|
496
|
+
code: "advance-decided",
|
|
497
|
+
message: `the advance of seq ${String(state.publishedSeq + 1)}..${String(state.substantiveSeq)} was already answered (${open.state} as ${open.actionKey}); nothing is asked again until the owed span changes.`,
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
if (adopted && open !== null && open.state === "granted" && open.spent) {
|
|
501
|
+
// The grant was consumed — by the advance that failed, or by one whose
|
|
502
|
+
// `execution.completed` has not been published yet. Either way the
|
|
503
|
+
// authorisation is gone, and a fresh question about an unchanged span is a
|
|
504
|
+
// retry loop with a human in it. The reason the last one failed is on the
|
|
505
|
+
// `execution.failed` and on the doctor row.
|
|
506
|
+
return no({
|
|
507
|
+
outcome: "refused",
|
|
508
|
+
code: "advance-spent",
|
|
509
|
+
message: `the decision on ${open.actionKey} has already been spent on an execution; this owed span will not be asked again until it changes.`,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
// The grant this tick is riding was earned by an earlier tick's question, so
|
|
513
|
+
// there is nothing to declare and nothing to ask: the declaration and the
|
|
514
|
+
// question are in the log already, and re-asking is the defect. Straight to
|
|
515
|
+
// the execution, bound to the hash THAT request declared.
|
|
516
|
+
const declaredHash = adopted && open !== null && open.payloadHash !== null ? open.payloadHash : hash;
|
|
517
|
+
if (!adopted) {
|
|
518
|
+
const registered = register(input.logPath, {
|
|
519
|
+
task,
|
|
520
|
+
envelope: {
|
|
521
|
+
origin: { app: "approval-daemon", created_by: ADVANCE_ACTOR },
|
|
522
|
+
state: "proposed",
|
|
523
|
+
actions: [
|
|
524
|
+
{
|
|
525
|
+
class: ADVANCE_CLASS,
|
|
526
|
+
idempotency_key: actionKey,
|
|
527
|
+
summary: `log advance: seq ${String(state.publishedSeq + 1)}..${String(state.substantiveSeq)} onto ${recordsBranch}`,
|
|
528
|
+
reversible: true,
|
|
529
|
+
est_cost_usd: "0",
|
|
530
|
+
payload_hash: hash,
|
|
531
|
+
},
|
|
532
|
+
],
|
|
533
|
+
},
|
|
534
|
+
}, ADVANCE_ACTOR, gate);
|
|
535
|
+
// A re-attempt at an unchanged span finds its own registration already in
|
|
536
|
+
// the log. That is the retry working, not a failure: the cycle continues and
|
|
537
|
+
// the request below adopts the declaration this one already wrote.
|
|
538
|
+
if (!registered.ok && registered.code !== "task-already-registered") {
|
|
539
|
+
return no({ outcome: "refused", code: registered.code, message: registered.message });
|
|
540
|
+
}
|
|
541
|
+
const asked = request(input.logPath, {
|
|
542
|
+
task,
|
|
543
|
+
actionKey,
|
|
544
|
+
cls: ADVANCE_CLASS,
|
|
545
|
+
reversible: true,
|
|
546
|
+
est_cost_usd: "0",
|
|
547
|
+
summary: `log advance: seq ${String(state.publishedSeq + 1)}..${String(state.substantiveSeq)}`,
|
|
548
|
+
payload_hash: hash,
|
|
549
|
+
payload: { value: { argv: advanceArgv(input.cadence), cwd: root, seq: { from: state.publishedSeq + 1, to: state.substantiveSeq } } },
|
|
550
|
+
// APRV-211. The requester is this process, and it will open the grant
|
|
551
|
+
// itself through the sealed address this request publishes. No granting
|
|
552
|
+
// surface is handed a raw token, so no granting surface can print one on
|
|
553
|
+
// a terminal for an action nobody at that terminal is going to run.
|
|
554
|
+
delivery: "self",
|
|
555
|
+
}, ADVANCE_ACTOR, gate);
|
|
556
|
+
if (!asked.ok) {
|
|
557
|
+
return no({ outcome: "refused", code: asked.code, message: asked.message });
|
|
558
|
+
}
|
|
559
|
+
if (!asked.proceed) {
|
|
560
|
+
return no({
|
|
561
|
+
outcome: "gated",
|
|
562
|
+
code: asked.live?.reason ?? "manual",
|
|
563
|
+
message: `the gate sent this advance to a human (${asked.live === undefined
|
|
564
|
+
? `class ${ADVANCE_CLASS} resolves ${asked.autonomy}`
|
|
565
|
+
: `supervised-live draw: ${asked.live.reason} at rate ${String(asked.live.rate)}`}); nothing was committed. The question is in the queue as ${actionKey}, and the next tick adopts it rather than asking again.`,
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
const started = startExecution(input.logPath, actionKey, {
|
|
570
|
+
policy: input.policy,
|
|
571
|
+
presentedPayloadHash: declaredHash,
|
|
572
|
+
...(input.schemaDir === undefined ? {} : { schemaDir: input.schemaDir }),
|
|
573
|
+
...(input.clock === undefined ? {} : { clock: input.clock }),
|
|
574
|
+
}, ADVANCE_ACTOR);
|
|
575
|
+
if (!started.ok) {
|
|
576
|
+
return no({ outcome: "refused", code: started.code, message: started.message });
|
|
577
|
+
}
|
|
578
|
+
return {
|
|
579
|
+
authorized: true,
|
|
580
|
+
ts,
|
|
581
|
+
today,
|
|
582
|
+
root,
|
|
583
|
+
recordsBranch,
|
|
584
|
+
actionKey,
|
|
585
|
+
recordsPending: state.pending,
|
|
586
|
+
adopted,
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
/** Run the verb here, on this stack. The shutdown flush's path. */
|
|
590
|
+
function runVerbHere(input, auth) {
|
|
591
|
+
try {
|
|
592
|
+
return logAdvance({
|
|
593
|
+
cwd: auth.root,
|
|
594
|
+
remote: input.cadence.remote,
|
|
595
|
+
base: input.cadence.base,
|
|
596
|
+
pr: input.cadence.pr,
|
|
597
|
+
autoMerge: input.cadence.autoMerge,
|
|
598
|
+
branch: auth.recordsBranch,
|
|
599
|
+
today: auth.today,
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
catch (cause) {
|
|
603
|
+
return { ok: false, code: "log-advance-git-failed", message: `the advance threw: ${detail(cause)}` };
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Close the execution and report. The gate work, always in this process.
|
|
608
|
+
*
|
|
609
|
+
* A finish that is itself refused (an external writer moved the head between
|
|
610
|
+
* the advance and this append) leaves a dangling execution, which is the honest
|
|
611
|
+
* state and which nothing here auto-repairs: `approval status` surfaces it and
|
|
612
|
+
* a human resolves it. It is named in the reported message rather than
|
|
613
|
+
* swallowed.
|
|
614
|
+
*
|
|
615
|
+
* The verb's own refusal code and message travel onto `execution.failed`
|
|
616
|
+
* (APRV-211). Exit 1 with no reason was the reported half of the 2026-09-02
|
|
617
|
+
* incident: the daemon knew why the push failed and the log did not.
|
|
618
|
+
*/
|
|
619
|
+
function recordFinish(input, auth, advanced) {
|
|
620
|
+
const answer = answerFor(auth.ts, auth.recordsPending, auth.recordsBranch);
|
|
621
|
+
const exitCode = advanced.ok ? 0 : 1;
|
|
622
|
+
const reason = advanced.ok ? undefined : { code: advanced.code, message: advanced.message };
|
|
623
|
+
// APRV-234. A rebuilt branch is worth explaining even when the advance
|
|
624
|
+
// succeeded: an operator reading the doctor row a day later needs to know
|
|
625
|
+
// that today's records branch was re-parented on the trunk rather than
|
|
626
|
+
// stacked, and on which commit. A REPORT, never an authorization — nothing in
|
|
627
|
+
// the gate reads it back.
|
|
628
|
+
const note = advanced.ok && advanced.report.rebuilt === true
|
|
629
|
+
? {
|
|
630
|
+
code: "advance-rebuilt",
|
|
631
|
+
message: `the day's records branch was rebuilt on ${advanced.report.rebuiltOn?.ref ?? "the base"} ${(advanced.report.rebuiltOn?.sha ?? "").slice(0, 12)}${advanced.report.recordsBranch === auth.recordsBranch
|
|
632
|
+
? ""
|
|
633
|
+
: ` and opened as ${advanced.report.recordsBranch}`}`,
|
|
634
|
+
}
|
|
635
|
+
: undefined;
|
|
636
|
+
// APRV-233. Bounded, and each attempt a whole operation against a fresh head.
|
|
637
|
+
const finished = finishWithHeadMovedRetry(input.logPath, auth.actionKey, exitCode, {
|
|
638
|
+
policy: input.policy,
|
|
639
|
+
...(input.schemaDir === undefined ? {} : { schemaDir: input.schemaDir }),
|
|
640
|
+
...(input.clock === undefined ? {} : { clock: input.clock }),
|
|
641
|
+
...(input.retryOnHeadMoved === undefined ? {} : { retryOnHeadMoved: input.retryOnHeadMoved }),
|
|
642
|
+
...(input.afterFinishRead === undefined ? {} : { afterRead: input.afterFinishRead }),
|
|
643
|
+
...(reason === undefined ? {} : { reason }),
|
|
644
|
+
...(note === undefined ? {} : { note }),
|
|
645
|
+
});
|
|
646
|
+
// The bound is spent and the execution is open. The effect HAPPENED and this
|
|
647
|
+
// process knows how it ended, so the outcome is carried to the next tick
|
|
648
|
+
// rather than lost; until it is recorded the cadence treats this advance as
|
|
649
|
+
// still in hand and starts no other.
|
|
650
|
+
const pendingFinish = finished.ok
|
|
651
|
+
? null
|
|
652
|
+
: {
|
|
653
|
+
actionKey: auth.actionKey,
|
|
654
|
+
exitCode,
|
|
655
|
+
...(reason === undefined ? {} : { reason }),
|
|
656
|
+
...(note === undefined ? {} : { note }),
|
|
657
|
+
};
|
|
658
|
+
const dangling = finished.ok
|
|
659
|
+
? ""
|
|
660
|
+
: ` (the execution outcome could not be recorded: ${finished.message}; ${auth.actionKey} is left open and the next tick settles it before it advances anything)`;
|
|
661
|
+
if (!advanced.ok) {
|
|
662
|
+
return answer({
|
|
663
|
+
outcome: "failed",
|
|
664
|
+
code: advanced.code,
|
|
665
|
+
message: `${advanced.message}${dangling}`,
|
|
666
|
+
pendingFinish,
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
const report = advanced.report;
|
|
670
|
+
return answer({
|
|
671
|
+
outcome: report.commit === null ? "nothing-owed" : "advanced",
|
|
672
|
+
commit: report.commit,
|
|
673
|
+
prUrl: report.prUrl,
|
|
674
|
+
prCreated: report.prCreated ?? false,
|
|
675
|
+
autoMerge: report.autoMerge ?? null,
|
|
676
|
+
autoMergeNote: report.autoMergeNote ?? null,
|
|
677
|
+
range: report.range,
|
|
678
|
+
recordsBranch: report.recordsBranch,
|
|
679
|
+
rebuilt: report.rebuilt === true,
|
|
680
|
+
rebuiltOn: report.rebuiltOn === undefined ? null : report.rebuiltOn.ref,
|
|
681
|
+
pendingFinish,
|
|
682
|
+
message: report.commit === null
|
|
683
|
+
? "the records branch already carried these bytes; nothing was committed"
|
|
684
|
+
: `seq ${String(report.range?.from ?? 0)}..${String(report.range?.to ?? 0)} is on ${report.recordsBranch}${report.rebuilt === true
|
|
685
|
+
? ` (rebuilt on ${report.rebuiltOn?.ref ?? "the base"} ${(report.rebuiltOn?.sha ?? "").slice(0, 12)})`
|
|
686
|
+
: ""}${report.prUrl === null ? "" : ` (${report.prUrl})`}${armNote(report)}${dangling}`,
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* What the attempt's message says about the arm (APRV-284).
|
|
691
|
+
*
|
|
692
|
+
* Every outcome but `armed` names its reason: a pull request nobody armed is
|
|
693
|
+
* one a person still has to land, and an operator reading this line is the one
|
|
694
|
+
* who has to know that from the line rather than from the pull request.
|
|
695
|
+
*/
|
|
696
|
+
function armNote(report) {
|
|
697
|
+
switch (report.autoMerge ?? null) {
|
|
698
|
+
case null:
|
|
699
|
+
return "";
|
|
700
|
+
case "armed":
|
|
701
|
+
return ", auto-merge armed";
|
|
702
|
+
case "off":
|
|
703
|
+
return ", auto-merge not armed (--no-advance-auto-merge)";
|
|
704
|
+
case "withheld":
|
|
705
|
+
return `, auto-merge WITHHELD: ${report.autoMergeNote ?? "the branch carries more than an advance may"}`;
|
|
706
|
+
default:
|
|
707
|
+
return `, auto-merge refused by gh: ${report.autoMergeNote ?? "no reason given"}`;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* The advance, on this stack, start to finish. The shutdown flush's path.
|
|
712
|
+
*
|
|
713
|
+
* Kept synchronous deliberately: `finish()` runs inside the daemon's shutdown,
|
|
714
|
+
* where there is no loop left to return to and an advance that settled after
|
|
715
|
+
* the process exited would be an advance nobody recorded.
|
|
716
|
+
*/
|
|
717
|
+
export function runAdvanceSync(input, auth) {
|
|
718
|
+
return recordFinish(input, auth, runVerbHere(input, auth));
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* The advance, with the git work in a child, so this loop keeps answering.
|
|
722
|
+
*
|
|
723
|
+
* ## Why a child rather than an `await`
|
|
724
|
+
*
|
|
725
|
+
* The second half of the 2026-09-02 incident: `answerCallbackQuery: HTTP 400`,
|
|
726
|
+
* over and over, around the grants. Telegram drops a callback query that is not
|
|
727
|
+
* answered inside its window, and `approval up` runs the channel listener and
|
|
728
|
+
* the daemon in ONE process on ONE loop. `logAdvance` is `spawnSync` from end to
|
|
729
|
+
* end — `git fetch`, a scratch-index commit, `git push`, `gh pr create` — so
|
|
730
|
+
* every tap that arrived during an advance waited for the push to finish, and
|
|
731
|
+
* the human got a button that never toasted. A promise would not have helped:
|
|
732
|
+
* synchronous work does not yield no matter what it is wrapped in. Only another
|
|
733
|
+
* process does.
|
|
734
|
+
*
|
|
735
|
+
* ## What does NOT move
|
|
736
|
+
*
|
|
737
|
+
* The gate work. `core/child-env.ts` strips `APPROVAL_*` from a child's
|
|
738
|
+
* environment (APRV-205), and the `supervised-live` draw's secret is exactly
|
|
739
|
+
* such a variable, so a child that asked the gate would be a child that could
|
|
740
|
+
* not draw and would fail closed on every tick. Authorization happens in
|
|
741
|
+
* {@link authorizeAdvance}, in this process, before the child is spawned;
|
|
742
|
+
* {@link recordFinish} closes the execution in this process, after it settles.
|
|
743
|
+
* The child gets one job — the git side effect — and no authority whatsoever.
|
|
744
|
+
* It never touches the log: it cannot append, and everything it says comes back
|
|
745
|
+
* as one JSON line on its stdout.
|
|
746
|
+
*/
|
|
747
|
+
export function runAdvanceAsync(input, auth) {
|
|
748
|
+
return new Promise((resolve) => {
|
|
749
|
+
const spec = input.runner ?? {
|
|
750
|
+
command: process.execPath,
|
|
751
|
+
args: [fileURLToPath(new URL("./advance-child.js", import.meta.url))],
|
|
752
|
+
};
|
|
753
|
+
const request_ = JSON.stringify({
|
|
754
|
+
cwd: auth.root,
|
|
755
|
+
remote: input.cadence.remote,
|
|
756
|
+
base: input.cadence.base,
|
|
757
|
+
pr: input.cadence.pr,
|
|
758
|
+
autoMerge: input.cadence.autoMerge,
|
|
759
|
+
branch: auth.recordsBranch,
|
|
760
|
+
today: auth.today,
|
|
761
|
+
});
|
|
762
|
+
let child;
|
|
763
|
+
try {
|
|
764
|
+
child = spawn(spec.command, [...spec.args, request_], {
|
|
765
|
+
cwd: auth.root,
|
|
766
|
+
env: childEnvironment().env,
|
|
767
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
catch (cause) {
|
|
771
|
+
resolve(recordFinish(input, auth, {
|
|
772
|
+
ok: false,
|
|
773
|
+
code: "log-advance-child-unspawnable",
|
|
774
|
+
message: `the advance child could not be started: ${detail(cause)}`,
|
|
775
|
+
}));
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
let out = "";
|
|
779
|
+
let err = "";
|
|
780
|
+
child.stdout.setEncoding("utf8");
|
|
781
|
+
child.stderr.setEncoding("utf8");
|
|
782
|
+
child.stdout.on("data", (chunk) => (out += chunk));
|
|
783
|
+
child.stderr.on("data", (chunk) => (err += chunk));
|
|
784
|
+
const settle = (run) => {
|
|
785
|
+
resolve(recordFinish(input, auth, run));
|
|
786
|
+
};
|
|
787
|
+
child.on("error", (cause) => {
|
|
788
|
+
settle({
|
|
789
|
+
ok: false,
|
|
790
|
+
code: "log-advance-child-unspawnable",
|
|
791
|
+
message: `the advance child could not be started: ${detail(cause)}`,
|
|
792
|
+
});
|
|
793
|
+
});
|
|
794
|
+
child.on("close", (code) => {
|
|
795
|
+
const parsed = parseChildReport(out);
|
|
796
|
+
if (parsed !== null) {
|
|
797
|
+
settle(parsed);
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
settle({
|
|
801
|
+
ok: false,
|
|
802
|
+
code: "log-advance-child-unreadable",
|
|
803
|
+
message: `the advance child exited ${String(code)} and said nothing this runtime could read${err.trim() === "" ? "" : `: ${err.trim().slice(0, 400)}`}`,
|
|
804
|
+
});
|
|
805
|
+
});
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* The child's one line, or `null` when it is not the shape this expects.
|
|
810
|
+
*
|
|
811
|
+
* Validated rather than cast: the child is another process, its stdout is
|
|
812
|
+
* input, and a report this runtime cannot recognise is a failed advance with a
|
|
813
|
+
* machine-readable reason — never a success taken on faith.
|
|
814
|
+
*/
|
|
815
|
+
function parseChildReport(text) {
|
|
816
|
+
const line = text.trim().split("\n").pop();
|
|
817
|
+
if (line === undefined || line === "")
|
|
818
|
+
return null;
|
|
819
|
+
let value;
|
|
820
|
+
try {
|
|
821
|
+
value = JSON.parse(line);
|
|
822
|
+
}
|
|
823
|
+
catch {
|
|
824
|
+
return null;
|
|
825
|
+
}
|
|
826
|
+
if (typeof value !== "object" || value === null)
|
|
827
|
+
return null;
|
|
828
|
+
const shape = value;
|
|
829
|
+
if (shape["ok"] === true && typeof shape["report"] === "object" && shape["report"] !== null) {
|
|
830
|
+
return { ok: true, report: shape["report"] };
|
|
831
|
+
}
|
|
832
|
+
if (shape["ok"] === false && typeof shape["code"] === "string" && typeof shape["message"] === "string") {
|
|
833
|
+
return { ok: false, code: shape["code"], message: shape["message"] };
|
|
834
|
+
}
|
|
835
|
+
return null;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Register, request, start, advance, finish. One attempt, on this stack.
|
|
839
|
+
*
|
|
840
|
+
* The whole cycle for a caller that has no loop to protect: the shutdown flush,
|
|
841
|
+
* `--once`, and every test that asserts on what one tick did.
|
|
842
|
+
*/
|
|
843
|
+
export function attemptAdvance(input, records) {
|
|
844
|
+
const auth = authorizeAdvance(input, records);
|
|
845
|
+
if (!auth.authorized)
|
|
846
|
+
return auth.attempt;
|
|
847
|
+
return runAdvanceSync(input, auth);
|
|
848
|
+
}
|
|
849
|
+
//# sourceMappingURL=advance.js.map
|