approval-md 0.1.0 → 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/README.md +584 -553
- package/SPEC.md +42 -13
- package/dist/src/adapters/agentmail.d.ts +426 -0
- package/dist/src/adapters/agentmail.js +2 -2
- package/dist/src/adapters/conformance.d.ts +149 -0
- package/dist/src/adapters/contract.d.ts +628 -0
- package/dist/src/adapters/contract.js +110 -16
- package/dist/src/adapters/contract.js.map +1 -1
- package/dist/src/adapters/email.d.ts +324 -0
- package/dist/src/adapters/env-passphrase.d.ts +93 -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 +2 -1
- package/dist/src/adapters/registry.js.map +1 -1
- package/dist/src/adapters/smtp.d.ts +213 -0
- package/dist/src/adapters/vault-provider.d.ts +114 -0
- package/dist/src/adapters/vault-provider.js +3 -3
- 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/cli.d.ts +193 -0
- package/dist/src/channels/conformance.d.ts +92 -0
- package/dist/src/channels/contract.d.ts +623 -0
- package/dist/src/channels/payload-view.d.ts +35 -0
- package/dist/src/channels/render-queue.d.ts +149 -0
- package/dist/src/channels/tagging.d.ts +196 -0
- package/dist/src/channels/telegram.d.ts +1832 -0
- package/dist/src/channels/web.d.ts +341 -0
- package/dist/src/cli/adapter.d.ts +90 -0
- package/dist/src/cli/adapter.js +25 -15
- package/dist/src/cli/adapter.js.map +1 -1
- package/dist/src/cli/amend.d.ts +59 -0
- package/dist/src/cli/args.d.ts +43 -0
- package/dist/src/cli/attest.d.ts +41 -0
- package/dist/src/cli/audit-card.d.ts +62 -0
- package/dist/src/cli/audit.d.ts +59 -0
- package/dist/src/cli/channel-telegram.d.ts +806 -0
- package/dist/src/cli/channel-web.d.ts +131 -0
- package/dist/src/cli/channel.d.ts +71 -0
- package/dist/src/cli/checkpoint-tap.d.ts +169 -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/daemon.d.ts +120 -0
- package/dist/src/cli/doctor.d.ts +129 -0
- package/dist/src/cli/doctor.js +119 -5
- package/dist/src/cli/doctor.js.map +1 -1
- package/dist/src/cli/env.d.ts +65 -0
- package/dist/src/cli/execute.d.ts +202 -0
- package/dist/src/cli/exit-codes.d.ts +73 -0
- package/dist/src/cli/feedback.d.ts +60 -0
- package/dist/src/cli/gate-window.d.ts +40 -0
- package/dist/src/cli/gate.d.ts +68 -0
- package/dist/src/cli/git-scope.d.ts +190 -0
- package/dist/src/cli/gloss-attach.d.ts +85 -0
- package/dist/src/cli/gloss-codex-child.d.ts +9 -0
- package/dist/src/cli/gloss-codex.d.ts +24 -0
- package/dist/src/cli/gloss-options.d.ts +42 -0
- package/dist/src/cli/gloss.d.ts +265 -0
- package/dist/src/cli/help.d.ts +103 -0
- package/dist/src/cli/help.js +173 -51
- package/dist/src/cli/help.js.map +1 -1
- 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 +186 -80
- package/dist/src/cli/hook.js.map +1 -1
- package/dist/src/cli/import.d.ts +35 -0
- package/dist/src/cli/init.d.ts +84 -0
- package/dist/src/cli/init.js +2 -2
- package/dist/src/cli/init.js.map +1 -1
- package/dist/src/cli/instructions.d.ts +23 -0
- package/dist/src/cli/journal.d.ts +41 -0
- package/dist/src/cli/log-advance.d.ts +287 -0
- package/dist/src/cli/log-advance.js +102 -11
- package/dist/src/cli/log-advance.js.map +1 -1
- package/dist/src/cli/log-anchor.d.ts +176 -0
- package/dist/src/cli/log-checkpoint.d.ts +22 -0
- package/dist/src/cli/log-sync.d.ts +243 -0
- package/dist/src/cli/log-verbs.d.ts +16 -0
- package/dist/src/cli/log-verbs.js +7 -1
- package/dist/src/cli/log-verbs.js.map +1 -1
- package/dist/src/cli/long-help.d.ts +70 -0
- package/dist/src/cli/main.d.ts +77 -0
- package/dist/src/cli/main.js +155 -5
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/mcp.d.ts +52 -0
- package/dist/src/cli/paths.d.ts +56 -0
- package/dist/src/cli/payload.d.ts +58 -0
- package/dist/src/cli/policy.d.ts +43 -0
- package/dist/src/cli/preflight.d.ts +363 -0
- package/dist/src/cli/preflight.js +294 -7
- package/dist/src/cli/preflight.js.map +1 -1
- package/dist/src/cli/progress.d.ts +78 -0
- package/dist/src/cli/prompt.d.ts +209 -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/render.d.ts +22 -0
- package/dist/src/cli/sandbox.d.ts +51 -0
- package/dist/src/cli/scaffold.d.ts +79 -0
- package/dist/src/cli/setup-adapter.d.ts +137 -0
- package/dist/src/cli/setup-adapter.js +38 -4
- package/dist/src/cli/setup-adapter.js.map +1 -1
- package/dist/src/cli/setup-channel.d.ts +117 -0
- package/dist/src/cli/setup-checkpoint.d.ts +57 -0
- package/dist/src/cli/setup-common.d.ts +275 -0
- package/dist/src/cli/setup-flow.d.ts +287 -0
- package/dist/src/cli/setup-service.d.ts +96 -0
- package/dist/src/cli/setup.d.ts +202 -0
- package/dist/src/cli/style.d.ts +320 -0
- package/dist/src/cli/token.d.ts +39 -0
- package/dist/src/cli/up.d.ts +155 -0
- package/dist/src/cli/up.js +4 -2
- package/dist/src/cli/up.js.map +1 -1
- package/dist/src/cli/usage.d.ts +37 -0
- package/dist/src/cli/values.d.ts +40 -0
- package/dist/src/cli/vault.d.ts +59 -0
- package/dist/src/cli/vault.js +2 -2
- package/dist/src/cli/vault.js.map +1 -1
- package/dist/src/cli/verb-registry.d.ts +76 -0
- package/dist/src/cli/verb-registry.js +176 -8
- package/dist/src/cli/verb-registry.js.map +1 -1
- package/dist/src/cli/wordmark.d.ts +31 -0
- package/dist/src/cli/wordmark.js +2 -2
- 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/agents-md.d.ts +276 -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 +13 -1
- package/dist/src/core/attest.js.map +1 -1
- package/dist/src/core/audit.d.ts +492 -0
- package/dist/src/core/budgets.d.ts +238 -0
- package/dist/src/core/checkpoint.d.ts +500 -0
- package/dist/src/core/child-env.d.ts +88 -0
- package/dist/src/core/clock.d.ts +52 -0
- package/dist/src/core/command-class.d.ts +543 -0
- package/dist/src/core/command-class.js +43 -8
- package/dist/src/core/command-class.js.map +1 -1
- package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
- package/dist/src/core/coverage-sources/gh.d.ts +48 -0
- package/dist/src/core/coverage-sources/git.d.ts +101 -0
- package/dist/src/core/coverage.d.ts +217 -0
- package/dist/src/core/credential-spec.d.ts +72 -0
- package/dist/src/core/dark-session.d.ts +331 -0
- package/dist/src/core/decision-refusal.d.ts +185 -0
- package/dist/src/core/env-file.d.ts +450 -0
- package/dist/src/core/execute.d.ts +858 -0
- package/dist/src/core/execute.js +44 -6
- package/dist/src/core/execute.js.map +1 -1
- package/dist/src/core/frontmatter.d.ts +78 -0
- package/dist/src/core/gate-window.d.ts +312 -0
- package/dist/src/core/gate.d.ts +1364 -0
- package/dist/src/core/gate.js +68 -13
- package/dist/src/core/gate.js.map +1 -1
- package/dist/src/core/git-run.d.ts +73 -0
- package/dist/src/core/harness-version.d.ts +157 -0
- package/dist/src/core/harness-version.js +2 -1
- package/dist/src/core/harness-version.js.map +1 -1
- package/dist/src/core/harness-wait.d.ts +55 -0
- package/dist/src/core/head-retry.d.ts +107 -0
- package/dist/src/core/instance.d.ts +253 -0
- package/dist/src/core/intake-limits.d.ts +247 -0
- package/dist/src/core/jcs.d.ts +52 -0
- package/dist/src/core/journal.d.ts +144 -0
- package/dist/src/core/live-draw.d.ts +436 -0
- package/dist/src/core/log-reconcile.d.ts +89 -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/loop.d.ts +274 -0
- package/dist/src/core/loop.js +11 -0
- package/dist/src/core/loop.js.map +1 -1
- package/dist/src/core/md-fence.d.ts +41 -0
- package/dist/src/core/money.d.ts +147 -0
- package/dist/src/core/payload-census.d.ts +74 -0
- package/dist/src/core/payload-store.d.ts +175 -0
- package/dist/src/core/payload.d.ts +71 -0
- package/dist/src/core/policy-diff.d.ts +292 -0
- package/dist/src/core/policy-diff.js +27 -4
- package/dist/src/core/policy-diff.js.map +1 -1
- package/dist/src/core/policy-expectations.d.ts +199 -0
- package/dist/src/core/policy-explain.d.ts +150 -0
- package/dist/src/core/policy-explain.js +31 -3
- package/dist/src/core/policy-explain.js.map +1 -1
- package/dist/src/core/policy-load.d.ts +527 -0
- package/dist/src/core/policy-load.js +15 -3
- package/dist/src/core/policy-load.js.map +1 -1
- package/dist/src/core/policy-match.d.ts +281 -0
- package/dist/src/core/policy-match.js +20 -9
- package/dist/src/core/policy-match.js.map +1 -1
- package/dist/src/core/policy-proposal.d.ts +265 -0
- package/dist/src/core/prompt-layout.d.ts +221 -0
- package/dist/src/core/protected-path-guard.d.ts +453 -0
- package/dist/src/core/protected-path-guard.js +514 -35
- package/dist/src/core/protected-path-guard.js.map +1 -1
- package/dist/src/core/registration.d.ts +25 -0
- package/dist/src/core/reindex.d.ts +99 -0
- package/dist/src/core/sampler.d.ts +313 -0
- package/dist/src/core/sandbox.d.ts +290 -0
- package/dist/src/core/seal.d.ts +165 -0
- package/dist/src/core/state.d.ts +505 -0
- package/dist/src/core/task-file.d.ts +185 -0
- package/dist/src/core/telegram-config.d.ts +93 -0
- package/dist/src/core/token.d.ts +409 -0
- package/dist/src/core/token.js +21 -38
- package/dist/src/core/token.js.map +1 -1
- package/dist/src/core/validate.d.ts +138 -0
- package/dist/src/core/values.d.ts +137 -0
- package/dist/src/core/vault.d.ts +291 -0
- package/dist/src/core/verified-snapshot.d.ts +204 -0
- package/dist/src/core/verify.d.ts +336 -0
- package/dist/src/core/version.d.ts +8 -0
- package/dist/src/core/wysiwys.d.ts +370 -0
- package/dist/src/daemon/advance-child.d.ts +39 -0
- package/dist/src/daemon/advance.d.ts +466 -0
- package/dist/src/daemon/audit.d.ts +87 -0
- package/dist/src/daemon/daemon.d.ts +1180 -0
- package/dist/src/daemon/dark-session.d.ts +64 -0
- package/dist/src/daemon/draw-child.d.ts +36 -0
- package/dist/src/daemon/draw.d.ts +154 -0
- package/dist/src/daemon/git-evidence.d.ts +173 -0
- package/dist/src/daemon/git-evidence.js +1 -1
- package/dist/src/daemon/projection.d.ts +180 -0
- package/dist/src/daemon/prune.d.ts +207 -0
- package/dist/src/mcp/http.d.ts +113 -0
- package/dist/src/mcp/server.d.ts +265 -0
- package/dist/src/mcp/server.js +9 -1
- package/dist/src/mcp/server.js.map +1 -1
- package/docs/adapter-api.md +106 -0
- package/docs/cli-reference.md +389 -36
- package/docs/codex-enforced-session.md +30 -0
- package/package.json +12 -2
- package/schema/codex-instance.schema.json +82 -0
- package/schema/event.schema.json +2 -1
- package/schema/fixtures/codex-instance/invalid/unpinned-codex-version.json +40 -0
- package/schema/fixtures/codex-instance/valid/canonical.json +40 -0
- package/schema/policy.schema.json +21 -1
- package/templates/codex/README.md +9 -0
|
@@ -0,0 +1,466 @@
|
|
|
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 { type Clock } from "../core/clock.js";
|
|
77
|
+
import { type DanglingAdvance } from "../core/advance-cycle.js";
|
|
78
|
+
import { type FailureReason } from "../core/execute.js";
|
|
79
|
+
import type { EventRecord } from "../core/log.js";
|
|
80
|
+
import { type AutoMergeState } from "../cli/log-advance.js";
|
|
81
|
+
/**
|
|
82
|
+
* How often the daemon advances, absent the record-count trigger: 15 minutes.
|
|
83
|
+
*
|
|
84
|
+
* Chosen against what a records pull request costs and what staleness costs. A
|
|
85
|
+
* shorter interval publishes a pull request whose diff is a handful of lines
|
|
86
|
+
* and burns a CI run for each; a longer one leaves the guards that read the
|
|
87
|
+
* committed log (the CI protected-path cross-check especially) looking at a log
|
|
88
|
+
* that is hours behind the decisions it is supposed to evidence.
|
|
89
|
+
*/
|
|
90
|
+
export declare const DEFAULT_ADVANCE_INTERVAL_MS = 900000;
|
|
91
|
+
/**
|
|
92
|
+
* How many unpublished records force an advance before the interval elapses: 20.
|
|
93
|
+
*
|
|
94
|
+
* The busy-hour case. Twenty records is a few minutes of an active session, and
|
|
95
|
+
* a records branch that far behind is a records branch a person starts doing
|
|
96
|
+
* arithmetic against.
|
|
97
|
+
*/
|
|
98
|
+
export declare const DEFAULT_ADVANCE_AFTER_RECORDS = 20;
|
|
99
|
+
/** The cadence, as the daemon is configured with it. */
|
|
100
|
+
export interface AdvanceCadence {
|
|
101
|
+
/** Minimum time between attempts, successful or not. */
|
|
102
|
+
intervalMs: number;
|
|
103
|
+
/** Attempt as soon as this many substantive records are unpublished. */
|
|
104
|
+
afterRecords: number;
|
|
105
|
+
remote: string;
|
|
106
|
+
/** The remote branch the first advance of a day is parented on. */
|
|
107
|
+
base: string | null;
|
|
108
|
+
/** Open or update the day's pull request. */
|
|
109
|
+
pr: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Arm auto-merge on that pull request (APRV-284). Read only when `pr` is set.
|
|
112
|
+
*/
|
|
113
|
+
autoMerge: boolean;
|
|
114
|
+
}
|
|
115
|
+
/** The default cadence, spelled once. */
|
|
116
|
+
export declare function defaultCadence(): AdvanceCadence;
|
|
117
|
+
/**
|
|
118
|
+
* How an attempt ended. Machine-readable and closed (SPEC.md §11.1 invariant 6).
|
|
119
|
+
*
|
|
120
|
+
* - `advanced` — records were committed and pushed.
|
|
121
|
+
* - `nothing-owed` — the records branch already carries every record.
|
|
122
|
+
* - `gated` — the gate sent this advance to a human (the live draw selected it,
|
|
123
|
+
* or the class resolves `manual`). Nothing was committed.
|
|
124
|
+
* - `refused` — the gate refused it outright (unattested policy, an escalated
|
|
125
|
+
* task, a `human-only` class, a budget). Nothing was committed.
|
|
126
|
+
* - `failed` — the advance itself did not complete (a rejected push, a diverged
|
|
127
|
+
* remote, an unreadable index). The failure is recorded as an
|
|
128
|
+
* `execution.failed`.
|
|
129
|
+
*/
|
|
130
|
+
export type AdvanceOutcome = "advanced" | "nothing-owed" | "gated" | "refused" | "failed";
|
|
131
|
+
/** One attempt, as the daemon reports it and as a status reader recovers it. */
|
|
132
|
+
export interface AdvanceAttempt {
|
|
133
|
+
outcome: AdvanceOutcome;
|
|
134
|
+
/** The runtime's own clock reading, taken once at the top of the attempt. */
|
|
135
|
+
ts: string;
|
|
136
|
+
/** Records not yet on a records branch, counted honestly (bookkeeping included). */
|
|
137
|
+
recordsPending: number;
|
|
138
|
+
recordsBranch: string | null;
|
|
139
|
+
commit: string | null;
|
|
140
|
+
prUrl: string | null;
|
|
141
|
+
/** True when this attempt OPENED the day's pull request rather than updating it. */
|
|
142
|
+
prCreated: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* What became of the auto-merge arm (APRV-284), or `null` when no pull
|
|
145
|
+
* request step ran. `cli/log-advance.ts`'s `AutoMergeState` documents each.
|
|
146
|
+
*/
|
|
147
|
+
autoMerge: AutoMergeState | null;
|
|
148
|
+
/** Why the arm was withheld or refused; `null` when it was not. */
|
|
149
|
+
autoMergeNote: string | null;
|
|
150
|
+
range: {
|
|
151
|
+
from: number;
|
|
152
|
+
to: number;
|
|
153
|
+
} | null;
|
|
154
|
+
/** The refusal code, for every outcome that carries one. */
|
|
155
|
+
code: string | null;
|
|
156
|
+
message: string;
|
|
157
|
+
/**
|
|
158
|
+
* True when the day's records branch was REBUILT on the base rather than
|
|
159
|
+
* stacked on its own tip (APRV-234), and the ref it was rebuilt on.
|
|
160
|
+
*/
|
|
161
|
+
rebuilt: boolean;
|
|
162
|
+
rebuiltOn: string | null;
|
|
163
|
+
/**
|
|
164
|
+
* The outcome this attempt observed and could NOT record (APRV-233).
|
|
165
|
+
*
|
|
166
|
+
* `null` in the ordinary case, where `execution.completed` or
|
|
167
|
+
* `execution.failed` landed. Non-null when the bounded head-moved retry was
|
|
168
|
+
* spent and the execution is therefore left open: the advance's effect has
|
|
169
|
+
* happened, this process knows how it ended, and the log does not yet. The
|
|
170
|
+
* daemon carries it to the next tick and settles it there
|
|
171
|
+
* ({@link settleAdvanceFinish}) before it evaluates any trigger, which is
|
|
172
|
+
* what stops a lost outcome record from reading as "no advance yet".
|
|
173
|
+
*/
|
|
174
|
+
pendingFinish: PendingAdvanceFinish | null;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* An advance outcome that happened and is not yet in the log (APRV-233).
|
|
178
|
+
*
|
|
179
|
+
* Every field is an OBSERVATION this runtime made, carried forward verbatim: a
|
|
180
|
+
* later tick appends exactly the record the failing one would have appended.
|
|
181
|
+
* Nothing here is re-derived and nothing is guessed — a dangling execution
|
|
182
|
+
* whose outcome this process does not hold stays dangling for a human, exactly
|
|
183
|
+
* as `core/execute.ts` says it must.
|
|
184
|
+
*/
|
|
185
|
+
export interface PendingAdvanceFinish {
|
|
186
|
+
actionKey: string;
|
|
187
|
+
exitCode: number;
|
|
188
|
+
reason?: FailureReason;
|
|
189
|
+
note?: FailureReason;
|
|
190
|
+
}
|
|
191
|
+
/** What {@link attemptAdvance} needs. Everything is injected; nothing is ambient. */
|
|
192
|
+
export interface AdvanceInput {
|
|
193
|
+
logPath: string;
|
|
194
|
+
/** The working directory the advance runs in: the primary checkout. */
|
|
195
|
+
cwd: string;
|
|
196
|
+
policy: {
|
|
197
|
+
dir?: string;
|
|
198
|
+
file?: string;
|
|
199
|
+
};
|
|
200
|
+
schemaDir?: string;
|
|
201
|
+
clock?: Clock;
|
|
202
|
+
cadence: AdvanceCadence;
|
|
203
|
+
/** The day the records branch is named for. Injected by tests. */
|
|
204
|
+
today?: string;
|
|
205
|
+
/**
|
|
206
|
+
* The child that runs the git side effect for {@link runAdvanceAsync}, when
|
|
207
|
+
* something other than this module's own runner should run it (APRV-211).
|
|
208
|
+
*
|
|
209
|
+
* A test seam and nothing else: production spawns `daemon/advance-child.js`
|
|
210
|
+
* under `process.execPath`. It exists because the property AC7 pins — a tap
|
|
211
|
+
* answered while an advance is in flight — needs an advance that stays in
|
|
212
|
+
* flight for longer than a real one does, and the alternative (making the
|
|
213
|
+
* daemon's own git work slow to order) would put test-only branches on the
|
|
214
|
+
* path that publishes the log.
|
|
215
|
+
*/
|
|
216
|
+
runner?: {
|
|
217
|
+
command: string;
|
|
218
|
+
args: readonly string[];
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* How many times the outcome record is re-derived against a moved head
|
|
222
|
+
* (APRV-233). Clamped to 1..`core/head-retry.ts`'s `HEAD_MOVED_ATTEMPTS` by
|
|
223
|
+
* that module's own `attemptsOf`, and downward only.
|
|
224
|
+
*
|
|
225
|
+
* The seam a test uses to pin BOTH shapes with one harness, exactly as
|
|
226
|
+
* `GateOptions.retryOnHeadMoved` does for the harness writers: `1` is the
|
|
227
|
+
* pre-APRV-233 writer, whose lost race left the execution dangling. A caller
|
|
228
|
+
* may ask for less tolerance of a moved head, never for more; ambiguity — a
|
|
229
|
+
* zero, a fraction, a negative, a larger number — resolves to the runtime's
|
|
230
|
+
* own value rather than the caller's.
|
|
231
|
+
*/
|
|
232
|
+
retryOnHeadMoved?: number;
|
|
233
|
+
/**
|
|
234
|
+
* The other half of that seam (APRV-261), forwarded to the finish path as
|
|
235
|
+
* `FinishOptions.afterRead`: a callback fired between one attempt's read and
|
|
236
|
+
* its append, which is where a test puts the record that moves the head.
|
|
237
|
+
*
|
|
238
|
+
* `retryOnHeadMoved` pins how hard the writer tries; this pins that it has to
|
|
239
|
+
* try at all. Together they let one harness drive both shapes with no
|
|
240
|
+
* sleeping anywhere: the pre-APRV-233 writer at `1`, and the retried writer
|
|
241
|
+
* at the default. Production sets neither.
|
|
242
|
+
*/
|
|
243
|
+
afterFinishRead?: () => void;
|
|
244
|
+
}
|
|
245
|
+
/** The argv the payload binds to: the verb this attempt is authorization for. */
|
|
246
|
+
export declare function advanceArgv(cadence: AdvanceCadence): string[];
|
|
247
|
+
/**
|
|
248
|
+
* The half of an attempt that talks to the gate, with nothing committed yet.
|
|
249
|
+
*
|
|
250
|
+
* Split out in APRV-211 so the two halves can run in different places: the
|
|
251
|
+
* authorization stays in the DAEMON's own process, where the launch environment
|
|
252
|
+
* carries the sampling secret the `supervised-live` draw needs and where the
|
|
253
|
+
* append lock is held for milliseconds, while the git side effect may move to a
|
|
254
|
+
* child (`runAdvanceAsync`) whose environment is scrubbed of exactly that
|
|
255
|
+
* secret (`core/child-env.ts`). Authorization is fast and must be trusted;
|
|
256
|
+
* `git fetch` + `git push` + `gh pr create` is slow and needs no trust at all.
|
|
257
|
+
*/
|
|
258
|
+
export type AdvanceAuthorization = {
|
|
259
|
+
authorized: false;
|
|
260
|
+
attempt: AdvanceAttempt;
|
|
261
|
+
} | {
|
|
262
|
+
authorized: true;
|
|
263
|
+
/** The clock reading the attempt was opened with. */
|
|
264
|
+
ts: string;
|
|
265
|
+
today: string;
|
|
266
|
+
root: string;
|
|
267
|
+
recordsBranch: string;
|
|
268
|
+
/** The key the execution was started under: the one the human answered. */
|
|
269
|
+
actionKey: string;
|
|
270
|
+
recordsPending: number;
|
|
271
|
+
/**
|
|
272
|
+
* True when this authorization rode a decision an EARLIER tick asked for
|
|
273
|
+
* rather than one this tick opened. Reported so an operator reading the
|
|
274
|
+
* event stream can tell "the human just said yes" from "the human said
|
|
275
|
+
* yes at 11:04 and this is the tick that spent it".
|
|
276
|
+
*/
|
|
277
|
+
adopted: boolean;
|
|
278
|
+
};
|
|
279
|
+
/**
|
|
280
|
+
* Close an execution whose outcome an EARLIER tick observed and could not
|
|
281
|
+
* record (APRV-233).
|
|
282
|
+
*
|
|
283
|
+
* The second half of the fix, and the one that matters after a restart of the
|
|
284
|
+
* loop rather than of the process: the outcome is carried in memory as a
|
|
285
|
+
* {@link PendingAdvanceFinish} and re-attempted here, on the fresh head,
|
|
286
|
+
* before the next tick evaluates any trigger. It appends the same record the
|
|
287
|
+
* failed attempt would have appended, through the same bounded retry.
|
|
288
|
+
*
|
|
289
|
+
* `already-finished` is a SUCCESS here: something (a concurrent settle, a
|
|
290
|
+
* human's `approval execution resolve`) closed the cycle in the meantime, the
|
|
291
|
+
* log holds an outcome, and there is nothing left to carry.
|
|
292
|
+
*/
|
|
293
|
+
export declare function settleAdvanceFinish(input: AdvanceInput, pending: PendingAdvanceFinish): {
|
|
294
|
+
ok: boolean;
|
|
295
|
+
code: string | null;
|
|
296
|
+
message: string;
|
|
297
|
+
};
|
|
298
|
+
/**
|
|
299
|
+
* Every open advance cycle, with what this checkout's refs prove (APRV-264).
|
|
300
|
+
*
|
|
301
|
+
* One `publishedState` for the whole list rather than one per key: it is the
|
|
302
|
+
* git read on the tick's hot path, and the answer it gives is the same for
|
|
303
|
+
* every key — the highest published seq, and the ref that carries it.
|
|
304
|
+
*
|
|
305
|
+
* Read from the log rather than from this process's memory, so a daemon that
|
|
306
|
+
* restarted still sees that an advance HAPPENED. It is the fact the cadence was
|
|
307
|
+
* missing on 2026-09-02: with no outcome record, nothing but an in-process
|
|
308
|
+
* clock said the branch had just been pushed.
|
|
309
|
+
*/
|
|
310
|
+
export declare function proveDanglingAdvancesFor(input: AdvanceInput, records: readonly EventRecord[]): DanglingAdvance[];
|
|
311
|
+
/** One key the sweep closed, or could not. */
|
|
312
|
+
export interface SweptAdvance {
|
|
313
|
+
actionKey: string;
|
|
314
|
+
/** The ref that proved it, on a settled entry. */
|
|
315
|
+
provenBy: string | null;
|
|
316
|
+
message: string;
|
|
317
|
+
}
|
|
318
|
+
/** What one sweep did, and what it left for a person (APRV-264). */
|
|
319
|
+
export interface AdvanceSweep {
|
|
320
|
+
/** Cycles this sweep closed, in log order, each with the ref that proved it. */
|
|
321
|
+
settled: SweptAdvance[];
|
|
322
|
+
/**
|
|
323
|
+
* Cycles nothing in this checkout can prove, plus the ones whose append was
|
|
324
|
+
* refused. Both are a person's now, and both block a new advance.
|
|
325
|
+
*/
|
|
326
|
+
outstanding: SweptAdvance[];
|
|
327
|
+
}
|
|
328
|
+
/** Is this sweep's work done — nothing open, nothing owed to a person? */
|
|
329
|
+
export declare function sweepIsClear(sweep: AdvanceSweep): boolean;
|
|
330
|
+
/**
|
|
331
|
+
* Close EVERY dangling advance cycle whose outcome the git refs still show
|
|
332
|
+
* (APRV-233, widened by APRV-264).
|
|
333
|
+
*
|
|
334
|
+
* ## The loop this breaks
|
|
335
|
+
*
|
|
336
|
+
* From Carter's terminal, 2026-09-02, with the APRV-211 build running: tick two
|
|
337
|
+
* advanced and pushed; its `execution.completed` lost the head race (expected
|
|
338
|
+
* 14874, found 14875) and `daemon-log-advance-1-14871` was left open. Tick
|
|
339
|
+
* three's authorization then reached `startExecution` on that same key and was
|
|
340
|
+
* refused `already-executed: an idempotency key is single-use and nothing here
|
|
341
|
+
* reconciles or reruns it` — which is the gate saying, correctly, that
|
|
342
|
+
* somebody has to close the books. Nobody did, so the span moved and the daemon
|
|
343
|
+
* pushed again, and again, every ninety seconds under a fifteen-minute
|
|
344
|
+
* interval.
|
|
345
|
+
*
|
|
346
|
+
* ## Why it sweeps rather than reconciles one
|
|
347
|
+
*
|
|
348
|
+
* APRV-233's version closed the LAST open cycle and returned. On 2026-09-05
|
|
349
|
+
* that was not enough: `approval status` listed FIVE, left by the 2026-09-02
|
|
350
|
+
* loop and by the restarts after it, the daemon refused one advance per tick
|
|
351
|
+
* naming one key each, and Carter closed all five by hand with five
|
|
352
|
+
* near-identical commands. One key per tick is a repair rate of one every
|
|
353
|
+
* thirty seconds behind a refusal that stops the cadence entirely, so the sweep
|
|
354
|
+
* takes every key it can prove in one pass and reports the rest together.
|
|
355
|
+
*
|
|
356
|
+
* ## What is and is not a guess here
|
|
357
|
+
*
|
|
358
|
+
* {@link settleAdvanceFinish} covers the case this process still holds the
|
|
359
|
+
* outcome for. This covers the other one — cycles left open by an earlier
|
|
360
|
+
* process, or by ticks whose memory of them is gone — and it closes each only
|
|
361
|
+
* on EVIDENCE: the records the cycle was authorized to publish are demonstrably
|
|
362
|
+
* on a records branch, read from git's own object store through the same
|
|
363
|
+
* `publishedState` the cadence and the doctor row read. When the evidence is
|
|
364
|
+
* there the outcome is recorded as the completion it was, with a note saying
|
|
365
|
+
* which ref proved it and that the RUNTIME observed it. When it is NOT there,
|
|
366
|
+
* nothing is written: the execution stays open for a person, the daemon warns
|
|
367
|
+
* once, and no new advance is started over work nobody has accounted for. A
|
|
368
|
+
* false `execution.failed` for an advance that actually published would be
|
|
369
|
+
* worse than the dangling record.
|
|
370
|
+
*
|
|
371
|
+
* The actor is {@link ADVANCE_ACTOR} and the record carries no
|
|
372
|
+
* `attested_by_human`: human attestation is for what a person went and looked
|
|
373
|
+
* at, and this is the runtime reading its own refs. The note says so in as many
|
|
374
|
+
* words, so no reader has to infer it from the actor field alone.
|
|
375
|
+
*
|
|
376
|
+
* This is a deliberate, narrow carve-out to `core/execute.ts`'s rule that
|
|
377
|
+
* nothing closes a dangling execution automatically. Narrow because it closes
|
|
378
|
+
* only executions THIS runtime started, for its own `log.advance` cycles, whose
|
|
379
|
+
* entire effect is a git ref this runtime can look at and did.
|
|
380
|
+
*/
|
|
381
|
+
export declare function sweepDanglingAdvances(input: AdvanceInput, records: readonly EventRecord[]): AdvanceSweep;
|
|
382
|
+
/**
|
|
383
|
+
* The refusal that stands while any advance cycle is open, naming all of them.
|
|
384
|
+
*
|
|
385
|
+
* One sentence for every outstanding key rather than one refusal per key per
|
|
386
|
+
* tick: the operator's next act is the same command whichever key they read
|
|
387
|
+
* first, so the command is on the line and every key it will close is under it.
|
|
388
|
+
*/
|
|
389
|
+
export declare function unreconciledRefusal(keys: readonly string[]): string;
|
|
390
|
+
/**
|
|
391
|
+
* Register, request, start. The authorization, and not one byte of side effect.
|
|
392
|
+
*
|
|
393
|
+
* The order is the whole point and none of it is optional: nothing is committed
|
|
394
|
+
* before the gate has answered, and nothing the gate answered for goes
|
|
395
|
+
* unrecorded. `execution.started` is appended BEFORE the advance runs, so the
|
|
396
|
+
* commit the advance builds carries the record of its own authorization; the
|
|
397
|
+
* `execution.completed` that follows lands after the commit and is published by
|
|
398
|
+
* the next advance, which is why the trigger discounts it.
|
|
399
|
+
*
|
|
400
|
+
* ## What APRV-211 changed: the tick asks the log what it already asked
|
|
401
|
+
*
|
|
402
|
+
* Observed live on 2026-09-02: three ticks over ONE owed advance put three
|
|
403
|
+
* questions on Carter's phone, because the idempotency key embedded the log
|
|
404
|
+
* HEAD and the head moved with each gated attempt's own two records. Two things
|
|
405
|
+
* fix it and both are here.
|
|
406
|
+
*
|
|
407
|
+
* 1. The key and the payload span end at `substantiveSeq` — the last unpublished
|
|
408
|
+
* record that is not an advance cycle's own bookkeeping — so a tick that
|
|
409
|
+
* appended only its own question computes the SAME key and the SAME hash.
|
|
410
|
+
* 2. Before it registers anything, the tick reads {@link openAdvanceRequest}.
|
|
411
|
+
* A question still open is ADOPTED (nothing is appended, the outcome is
|
|
412
|
+
* `gated`, and the human keeps the one question they already have); a grant
|
|
413
|
+
* on it is SPENT (straight to `startExecution` on that key, with the hash
|
|
414
|
+
* that key declared, so the single-use rule the log already enforces makes
|
|
415
|
+
* one decision authorise exactly one advance); a terminal answer on the
|
|
416
|
+
* CURRENT owed span is HONOURED (`advance-decided`, and nothing is asked
|
|
417
|
+
* again until the span itself changes).
|
|
418
|
+
*/
|
|
419
|
+
export declare function authorizeAdvance(input: AdvanceInput, records: readonly EventRecord[]): AdvanceAuthorization;
|
|
420
|
+
/**
|
|
421
|
+
* The advance, on this stack, start to finish. The shutdown flush's path.
|
|
422
|
+
*
|
|
423
|
+
* Kept synchronous deliberately: `finish()` runs inside the daemon's shutdown,
|
|
424
|
+
* where there is no loop left to return to and an advance that settled after
|
|
425
|
+
* the process exited would be an advance nobody recorded.
|
|
426
|
+
*/
|
|
427
|
+
export declare function runAdvanceSync(input: AdvanceInput, auth: Extract<AdvanceAuthorization, {
|
|
428
|
+
authorized: true;
|
|
429
|
+
}>): AdvanceAttempt;
|
|
430
|
+
/**
|
|
431
|
+
* The advance, with the git work in a child, so this loop keeps answering.
|
|
432
|
+
*
|
|
433
|
+
* ## Why a child rather than an `await`
|
|
434
|
+
*
|
|
435
|
+
* The second half of the 2026-09-02 incident: `answerCallbackQuery: HTTP 400`,
|
|
436
|
+
* over and over, around the grants. Telegram drops a callback query that is not
|
|
437
|
+
* answered inside its window, and `approval up` runs the channel listener and
|
|
438
|
+
* the daemon in ONE process on ONE loop. `logAdvance` is `spawnSync` from end to
|
|
439
|
+
* end — `git fetch`, a scratch-index commit, `git push`, `gh pr create` — so
|
|
440
|
+
* every tap that arrived during an advance waited for the push to finish, and
|
|
441
|
+
* the human got a button that never toasted. A promise would not have helped:
|
|
442
|
+
* synchronous work does not yield no matter what it is wrapped in. Only another
|
|
443
|
+
* process does.
|
|
444
|
+
*
|
|
445
|
+
* ## What does NOT move
|
|
446
|
+
*
|
|
447
|
+
* The gate work. `core/child-env.ts` strips `APPROVAL_*` from a child's
|
|
448
|
+
* environment (APRV-205), and the `supervised-live` draw's secret is exactly
|
|
449
|
+
* such a variable, so a child that asked the gate would be a child that could
|
|
450
|
+
* not draw and would fail closed on every tick. Authorization happens in
|
|
451
|
+
* {@link authorizeAdvance}, in this process, before the child is spawned;
|
|
452
|
+
* {@link recordFinish} closes the execution in this process, after it settles.
|
|
453
|
+
* The child gets one job — the git side effect — and no authority whatsoever.
|
|
454
|
+
* It never touches the log: it cannot append, and everything it says comes back
|
|
455
|
+
* as one JSON line on its stdout.
|
|
456
|
+
*/
|
|
457
|
+
export declare function runAdvanceAsync(input: AdvanceInput, auth: Extract<AdvanceAuthorization, {
|
|
458
|
+
authorized: true;
|
|
459
|
+
}>): Promise<AdvanceAttempt>;
|
|
460
|
+
/**
|
|
461
|
+
* Register, request, start, advance, finish. One attempt, on this stack.
|
|
462
|
+
*
|
|
463
|
+
* The whole cycle for a caller that has no loop to protect: the shutdown flush,
|
|
464
|
+
* `--once`, and every test that asserts on what one tick did.
|
|
465
|
+
*/
|
|
466
|
+
export declare function attemptAdvance(input: AdvanceInput, records: readonly EventRecord[]): AdvanceAttempt;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The daemon's audit-sampling sweep (SPEC.md §10.2: "samples supervised actions
|
|
3
|
+
* for audit").
|
|
4
|
+
*
|
|
5
|
+
* `daemon/daemon.ts` calls {@link sweepAuditSampling} once per tick and nothing
|
|
6
|
+
* else. The scheduling question — when to look — is the daemon's; every other
|
|
7
|
+
* question is answered in `core/audit.ts` and `core/sampler.ts`, which is the
|
|
8
|
+
* same division the drift scan and the TTL sweep already follow: the daemon
|
|
9
|
+
* decides nothing of its own.
|
|
10
|
+
*
|
|
11
|
+
* ## Why the sweep is idempotent without remembering anything
|
|
12
|
+
*
|
|
13
|
+
* `core/audit.ts` re-derives the candidate set from the verified log every call
|
|
14
|
+
* and subtracts the subjects already sampled. A tick that samples nothing new is
|
|
15
|
+
* the normal case; a restarted daemon, a second daemon, and an operator running
|
|
16
|
+
* a sweep by hand all converge on exactly the same set. This module therefore
|
|
17
|
+
* holds no state at all — no seen-set, no cursor, no cache.
|
|
18
|
+
*
|
|
19
|
+
* ## Reporting, and why it goes through `warning`
|
|
20
|
+
*
|
|
21
|
+
* The sweep reports failures through the daemon's existing `warning` channel.
|
|
22
|
+
* Successful samples were originally left implicit, visible only as `QUEUE.md`'s
|
|
23
|
+
* sampled-audit backlog and the `rendered` event's `audit_backlog` growing later
|
|
24
|
+
* in the same tick, which is what let APRV-40 leave `DaemonEvent` untouched.
|
|
25
|
+
* APRV-57 grew that union additively, so a sample now also reports through the
|
|
26
|
+
* optional {@link AuditSweepOptions.sampled} sink, which the daemon renders as
|
|
27
|
+
* its `sampled` line. Both channels remain the caller's: this module names no
|
|
28
|
+
* event and writes to no stream.
|
|
29
|
+
*
|
|
30
|
+
* ## A disabled sampler is not a failure
|
|
31
|
+
*
|
|
32
|
+
* When no sampling secret is configured, `core/sampler.ts` returns a disabled
|
|
33
|
+
* sampler with a machine-readable reason. That is a standing configuration fact,
|
|
34
|
+
* not an error, so it is NOT routed through the failure channel: a warning that
|
|
35
|
+
* fires every tick for something the operator chose is noise that trains them to
|
|
36
|
+
* scroll past the channel that also carries real refusals. It is reported
|
|
37
|
+
* instead through the optional {@link AuditSweepOptions.notice} sink (once per
|
|
38
|
+
* reason per process), and standingly by `approval status`, which is where an
|
|
39
|
+
* operator goes to ask what is switched on. See `core/sampler.ts` for why an
|
|
40
|
+
* unconfigured sampler disables sampling rather than escalating everything.
|
|
41
|
+
*/
|
|
42
|
+
import { type SampleAppended } from "../core/audit.js";
|
|
43
|
+
import type { Clock } from "../core/clock.js";
|
|
44
|
+
import type { SamplerDisabledReason } from "../core/sampler.js";
|
|
45
|
+
/** What the daemon hands the sweep. Everything is injected; nothing is ambient. */
|
|
46
|
+
export interface AuditSweepOptions {
|
|
47
|
+
logPath: string;
|
|
48
|
+
/** Policy location, with `loadPolicy`'s semantics. */
|
|
49
|
+
policy: {
|
|
50
|
+
dir?: string;
|
|
51
|
+
file?: string;
|
|
52
|
+
};
|
|
53
|
+
/** Reported relative to this, and the fallback policy directory. */
|
|
54
|
+
cwd: string;
|
|
55
|
+
schemaDir?: string;
|
|
56
|
+
/** The write-boundary clock (amended SPEC.md §8). */
|
|
57
|
+
clock?: Clock;
|
|
58
|
+
/** Environment the sampling secret is read from. Injected by tests. */
|
|
59
|
+
env?: NodeJS.ProcessEnv;
|
|
60
|
+
/**
|
|
61
|
+
* One FAILURE line: an append the sampler could not make, or a log it could
|
|
62
|
+
* not read. Mapped by the caller onto its own warning vocabulary.
|
|
63
|
+
*/
|
|
64
|
+
warn(message: string): void;
|
|
65
|
+
/**
|
|
66
|
+
* One CONFIGURATION line: sampling is switched off and here is why. Optional,
|
|
67
|
+
* and the daemon does not pass it — see the module header. `approval status`
|
|
68
|
+
* reports the same fact standingly, from the same resolver.
|
|
69
|
+
*/
|
|
70
|
+
notice?(message: string): void;
|
|
71
|
+
/**
|
|
72
|
+
* One SUCCESS line per `audit.sampled` appended (APRV-57). Optional, and
|
|
73
|
+
* injected exactly as {@link AuditSweepOptions.warn} is, so the sweep still
|
|
74
|
+
* reports in the caller's vocabulary and owns no output of its own.
|
|
75
|
+
*/
|
|
76
|
+
sampled?(sample: SampleAppended): void;
|
|
77
|
+
}
|
|
78
|
+
export interface AuditSweepSummary {
|
|
79
|
+
/** How many `audit.sampled` events this sweep appended. */
|
|
80
|
+
sampled: number;
|
|
81
|
+
/** `null` when sampling ran; the reason when it did not. */
|
|
82
|
+
disabled: SamplerDisabledReason | null;
|
|
83
|
+
}
|
|
84
|
+
/** Reset the once-per-process notice. Exported for tests, used nowhere else. */
|
|
85
|
+
export declare function resetAuditSweepNotices(): void;
|
|
86
|
+
/** One sampling sweep. Appends through `core/audit.ts` and decides nothing. */
|
|
87
|
+
export declare function sweepAuditSampling(options: AuditSweepOptions): AuditSweepSummary;
|