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,492 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The audit lifecycle (SPEC.md §5.2, §9.1, §12): `audit.sampled` →
|
|
3
|
+
* `audit.reviewed` → (on a denial) `reconciliation.required` →
|
|
4
|
+
* `reconciliation.satisfied`.
|
|
5
|
+
*
|
|
6
|
+
* ## What a retrospective denial can and cannot do (amended SPEC.md §5.2, APRV-127)
|
|
7
|
+
*
|
|
8
|
+
* The action already happened. The runtime cannot undo it, and any design that
|
|
9
|
+
* pretended otherwise would be lying to the person who denied it. What it can do
|
|
10
|
+
* is **oblige and record**: a denial appends an obligation naming the action, its
|
|
11
|
+
* class, and the review that denied, and the obligation stays open, and loud,
|
|
12
|
+
* until a HUMAN says it is discharged. Which obligation depends on whether the
|
|
13
|
+
* action was declared reversible — see {@link obligationFor}, including why a
|
|
14
|
+
* self-reported `reversible` may choose the shape and can never remove one.
|
|
15
|
+
*
|
|
16
|
+
* This is also why the autonomy split has a floor. `supervised-retro` refuses an
|
|
17
|
+
* action declaring `reversible: false` (`core/policy-match.ts`'s irreversibility
|
|
18
|
+
* floor pushes it to `manual`), because retrospective review of something that
|
|
19
|
+
* cannot be undone is regret with a paper trail. The floor is a floor and not a
|
|
20
|
+
* proof: it acts on a claim the acting party makes about itself, so it catches
|
|
21
|
+
* the honest declaration and never the lie. The obligation machinery here is what
|
|
22
|
+
* remains when the floor did not apply and the review said no anyway.
|
|
23
|
+
*
|
|
24
|
+
* Two halves, deliberately asymmetric:
|
|
25
|
+
*
|
|
26
|
+
* - **Sampling is the runtime's.** It is mechanical, it is retrospective, and it
|
|
27
|
+
* escalates a supervised action that already executed into a human's review
|
|
28
|
+
* queue. {@link sampleSupervised} appends `audit.sampled` with a `system:`
|
|
29
|
+
* actor. Selection is `core/sampler.ts`'s keyed HMAC, so no agent can predict
|
|
30
|
+
* or steer it.
|
|
31
|
+
* - **Review is a human's.** {@link reviewSample} appends `audit.reviewed` and
|
|
32
|
+
* refuses any actor that is not `human:<id>`, by the same rule that governs
|
|
33
|
+
* `grant`, `reject`, `revoke`, `policy attest`, and `execution resolve`. A
|
|
34
|
+
* runtime that could mark its own samples reviewed would be a supervision
|
|
35
|
+
* backlog that empties itself, which is precisely the failure the sampled
|
|
36
|
+
* backlog exists to make visible.
|
|
37
|
+
*
|
|
38
|
+
* ## Eligibility is derived, never self-reported (global invariant 4)
|
|
39
|
+
*
|
|
40
|
+
* A candidate is an `execution.started` record whose action's class, as recorded
|
|
41
|
+
* in the `task.registered` declaration the log already holds, resolves to
|
|
42
|
+
* `supervised` under the live policy. Two things follow, both load-bearing:
|
|
43
|
+
*
|
|
44
|
+
* - the class comes from the registration record rather than from the
|
|
45
|
+
* `execution.started` payload, and the autonomy comes from re-running
|
|
46
|
+
* `core/policy-match.ts` rather than from any field claiming an autonomy. No
|
|
47
|
+
* payload key an authoring party writes can move an action out of the
|
|
48
|
+
* candidate set;
|
|
49
|
+
* - eligibility is recomputed from the log every sweep, so it does not depend on
|
|
50
|
+
* any remembered flag, and a candidate cannot exclude itself by writing
|
|
51
|
+
* anything into its own event.
|
|
52
|
+
*
|
|
53
|
+
* The manual path is excluded because it never resolves `supervised`: a manual
|
|
54
|
+
* action's start is authorized by a token and its class resolves `manual`, so it
|
|
55
|
+
* is not a candidate and is not double-counted.
|
|
56
|
+
*
|
|
57
|
+
* ## Exactly once, without remembering anything
|
|
58
|
+
*
|
|
59
|
+
* Every sweep re-derives the whole candidate set and subtracts the subjects the
|
|
60
|
+
* log already carries an `audit.sampled` for, keyed on the subject record's
|
|
61
|
+
* `hash` (unique per record by construction, and stable across re-reads). A
|
|
62
|
+
* daemon restart, a second daemon, and a manual sweep all converge on the same
|
|
63
|
+
* set, and none of them can double-sample. Every append passes `expectedHead`,
|
|
64
|
+
* so a check made against one log cannot land on another (SPEC.md §11.1
|
|
65
|
+
* invariant 5).
|
|
66
|
+
*
|
|
67
|
+
* ## Time
|
|
68
|
+
*
|
|
69
|
+
* `audit.*` is gate-typed (SPEC.md §8), so no public function here takes a `ts`:
|
|
70
|
+
* the timestamp is read from the injected clock at the write boundary, and the
|
|
71
|
+
* party being audited does not author the clock it is judged by.
|
|
72
|
+
*/
|
|
73
|
+
import { type ClockOptions } from "./clock.js";
|
|
74
|
+
import { type AppendError, type EventRecord } from "./log.js";
|
|
75
|
+
import { type PolicyLoadResult } from "./policy-load.js";
|
|
76
|
+
import { type Sampler } from "./sampler.js";
|
|
77
|
+
import type { ValidateOptions } from "./validate.js";
|
|
78
|
+
/**
|
|
79
|
+
* SPEC.md §8: the sampler is the runtime, so its actor is `system:`. Distinct
|
|
80
|
+
* from `system:gate` (expiries) and `system:daemon` (envelope drift) so a reader
|
|
81
|
+
* can tell which part of the runtime spoke without reading the payload.
|
|
82
|
+
*/
|
|
83
|
+
export declare const AUDIT_ACTOR = "system:audit";
|
|
84
|
+
/**
|
|
85
|
+
* The closed set of audit refusal codes. Frozen public API in the same sense the
|
|
86
|
+
* gate's and the executor's are: a supervisor branches on these strings, so
|
|
87
|
+
* adding one is a spec change and renaming one is a breaking change.
|
|
88
|
+
*/
|
|
89
|
+
export declare const AUDIT_REFUSAL_CODES: readonly [
|
|
90
|
+
/** Review was attempted by an actor that is not `human:<id>`. */
|
|
91
|
+
"actor-not-human",
|
|
92
|
+
/** No `audit.sampled` record matches the subject named. */
|
|
93
|
+
"not-sampled",
|
|
94
|
+
/** That sample already has a later `audit.reviewed`. */
|
|
95
|
+
"already-reviewed",
|
|
96
|
+
/** An action key with more than one unreviewed sample; name the seq instead. */
|
|
97
|
+
"ambiguous-subject",
|
|
98
|
+
/** No `reconciliation.required` record at the seq named (APRV-127). */
|
|
99
|
+
"not-obliged",
|
|
100
|
+
/** That obligation already has a `reconciliation.satisfied` (APRV-127). */
|
|
101
|
+
"already-satisfied",
|
|
102
|
+
/**
|
|
103
|
+
* A verb that requires a reason was given none. Two shapes, one code: a
|
|
104
|
+
* reconciliation satisfied with a blank note (APRV-127), and a review whose
|
|
105
|
+
* `reaction` is `loved` or `disliked` with a blank note (APRV-239). Both are
|
|
106
|
+
* an assertion nobody can check, and both are evaluated after the actor check
|
|
107
|
+
* and before the log is read.
|
|
108
|
+
*/
|
|
109
|
+
"note-required",
|
|
110
|
+
/**
|
|
111
|
+
* A review that says the action should not have happened and that the human
|
|
112
|
+
* liked or loved it (APRV-239). Evaluated beside `note-required`, before the
|
|
113
|
+
* log is read; nothing is appended.
|
|
114
|
+
*
|
|
115
|
+
* The two fields point opposite ways and only one of them is enforcement, so
|
|
116
|
+
* the safe reading is not "believe the verdict and drop the grade": a record
|
|
117
|
+
* carrying both would be read by a person later as evidence of whichever half
|
|
118
|
+
* suited them, and by an agent as a signal that a denial is survivable if the
|
|
119
|
+
* operator is pleased. The reviewer is asked to say which they meant.
|
|
120
|
+
*/
|
|
121
|
+
"reaction-conflicts-verdict",
|
|
122
|
+
/**
|
|
123
|
+
* A `gated-revert` obligation whose satisfaction names no completed revert
|
|
124
|
+
* (APRV-127). The obligation is to undo the action THROUGH THE GATE, and the
|
|
125
|
+
* evidence of that is an `execution.completed` in this same log.
|
|
126
|
+
*/
|
|
127
|
+
"revert-required",
|
|
128
|
+
/**
|
|
129
|
+
* The denial was recorded and its obligation was not (APRV-127). The log is
|
|
130
|
+
* NOT inconsistent — `audit.reviewed` stands and says `denied` — but the
|
|
131
|
+
* obligation it should have created is missing and must be created by
|
|
132
|
+
* reviewing again once the head settles.
|
|
133
|
+
*/
|
|
134
|
+
"obligation-not-appended",
|
|
135
|
+
/** The log could not be read, or holds a line that is not a record. */
|
|
136
|
+
"log-unreadable",
|
|
137
|
+
/** The log's final line is unterminated (a crashed write). */
|
|
138
|
+
"log-torn-tail",
|
|
139
|
+
/** The chain does not verify; nothing is derived from an untrustworthy log. */
|
|
140
|
+
"log-corrupt",
|
|
141
|
+
/** The append itself failed; `append` carries the underlying error. */
|
|
142
|
+
"append-failed"];
|
|
143
|
+
export type AuditRefusalCode = (typeof AUDIT_REFUSAL_CODES)[number];
|
|
144
|
+
export interface AuditRefusal {
|
|
145
|
+
ok: false;
|
|
146
|
+
code: AuditRefusalCode;
|
|
147
|
+
message: string;
|
|
148
|
+
/** The seq of the record that produced the refusal, when there is one. */
|
|
149
|
+
seq?: number;
|
|
150
|
+
/** The underlying append error, when `code` is `append-failed`. */
|
|
151
|
+
append?: AppendError;
|
|
152
|
+
}
|
|
153
|
+
/** Options shared by the audit verbs. No `ts`: `audit.*` is gate-typed. */
|
|
154
|
+
export interface AuditOptions extends ClockOptions, ValidateOptions {
|
|
155
|
+
/** Policy location, with `loadPolicy`'s semantics. */
|
|
156
|
+
policy?: {
|
|
157
|
+
dir?: string;
|
|
158
|
+
file?: string;
|
|
159
|
+
};
|
|
160
|
+
/** Environment the sampling secret is read from. Injected by tests. */
|
|
161
|
+
env?: NodeJS.ProcessEnv;
|
|
162
|
+
}
|
|
163
|
+
/** One supervised execution eligible for retrospective review. */
|
|
164
|
+
export interface AuditCandidate {
|
|
165
|
+
/** `seq` of the `execution.started` record. */
|
|
166
|
+
seq: number;
|
|
167
|
+
/** `hash` of that record: the HMAC input and the dedupe key. */
|
|
168
|
+
hash: string;
|
|
169
|
+
ts: string;
|
|
170
|
+
actionKey: string;
|
|
171
|
+
task: string | null;
|
|
172
|
+
/** The class the registration declared, re-resolved to `supervised`. */
|
|
173
|
+
class: string;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Every `execution.started` whose action resolves `supervised` under `load`, in
|
|
177
|
+
* log order.
|
|
178
|
+
*
|
|
179
|
+
* Pure: no I/O, no clock, no environment. Records with no action key, and keys
|
|
180
|
+
* no `task.registered` record declares, are skipped rather than guessed at — an
|
|
181
|
+
* undeclared key has no class, and inventing one would put a fact in the sample
|
|
182
|
+
* that nobody wrote.
|
|
183
|
+
*/
|
|
184
|
+
export declare function supervisedExecutions(records: readonly EventRecord[], load: PolicyLoadResult): AuditCandidate[];
|
|
185
|
+
/** One `audit.sampled` record, with the review that closes it (or none). */
|
|
186
|
+
export interface SampledSubject {
|
|
187
|
+
/** `seq` of the `audit.sampled` record itself. */
|
|
188
|
+
seq: number;
|
|
189
|
+
ts: string;
|
|
190
|
+
actionKey: string | null;
|
|
191
|
+
task: string | null;
|
|
192
|
+
/** `hash` of the subject record the sample named, when it named one. */
|
|
193
|
+
subjectHash: string | null;
|
|
194
|
+
/** `seq` of the subject record the sample named, when it named one. */
|
|
195
|
+
subjectSeq: number | null;
|
|
196
|
+
/** `seq` of the later `audit.reviewed`, or `null` when still open. */
|
|
197
|
+
reviewedSeq: number | null;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Every `audit.sampled` in the log, each tagged with the `audit.reviewed` that
|
|
201
|
+
* closes it.
|
|
202
|
+
*
|
|
203
|
+
* A review closes a sample only when it comes **after** it in the chain and
|
|
204
|
+
* names the same action key. An earlier review is a review of an earlier sample;
|
|
205
|
+
* treating it as covering this one would silently empty the backlog, which is
|
|
206
|
+
* exactly the failure a sampled-audit backlog exists to prevent. This mirrors
|
|
207
|
+
* `channels/render-queue.ts`'s matching rule, so the CLI and the queue
|
|
208
|
+
* projection never disagree about what is outstanding.
|
|
209
|
+
*/
|
|
210
|
+
export declare function sampledSubjects(records: readonly EventRecord[]): SampledSubject[];
|
|
211
|
+
/** Samples with no later review, oldest first. The human's audit backlog. */
|
|
212
|
+
export declare function openSamples(records: readonly EventRecord[]): SampledSubject[];
|
|
213
|
+
/** The candidates a sweep would sample now: eligible, selected, not yet sampled. */
|
|
214
|
+
export declare function pendingSamples(records: readonly EventRecord[], load: PolicyLoadResult, sampler: Sampler): AuditCandidate[];
|
|
215
|
+
/** One `audit.sampled` this sweep appended. */
|
|
216
|
+
export interface SampleAppended {
|
|
217
|
+
record: EventRecord;
|
|
218
|
+
candidate: AuditCandidate;
|
|
219
|
+
}
|
|
220
|
+
export interface SampleSweepResult {
|
|
221
|
+
ok: true;
|
|
222
|
+
/** The sampler in force. Carries the reason when sampling is off. */
|
|
223
|
+
sampler: Sampler;
|
|
224
|
+
appended: SampleAppended[];
|
|
225
|
+
/** Appends that were refused. Reported, never retried in place. */
|
|
226
|
+
refusals: AuditRefusal[];
|
|
227
|
+
}
|
|
228
|
+
export type SampleResult = SampleSweepResult | AuditRefusal;
|
|
229
|
+
/**
|
|
230
|
+
* Sample every supervised execution the log does not yet carry an
|
|
231
|
+
* `audit.sampled` for, and append one event per selection.
|
|
232
|
+
*
|
|
233
|
+
* Re-reads the verified log before every append so the head each
|
|
234
|
+
* compare-and-append is made against is the head the decision was made from. A
|
|
235
|
+
* `head-moved` refusal is collected and reported rather than retried: only the
|
|
236
|
+
* next sweep, which re-derives the whole question from the log as it now is,
|
|
237
|
+
* knows whether the candidate is still a candidate.
|
|
238
|
+
*
|
|
239
|
+
* Returns `ok` with an empty `appended` list when sampling is disabled; the
|
|
240
|
+
* reason travels on `sampler`. A disabled sampler is not a refusal, because
|
|
241
|
+
* nothing was asked for and nothing failed. See `core/sampler.ts` on why a
|
|
242
|
+
* missing secret disables sampling rather than escalating everything.
|
|
243
|
+
*/
|
|
244
|
+
export declare function sampleSupervised(logPath: string, cwd: string, options?: AuditOptions): SampleResult;
|
|
245
|
+
/** How a caller named the sample to review. */
|
|
246
|
+
export type SubjectRef = {
|
|
247
|
+
kind: "seq";
|
|
248
|
+
seq: number;
|
|
249
|
+
} | {
|
|
250
|
+
kind: "action-key";
|
|
251
|
+
actionKey: string;
|
|
252
|
+
};
|
|
253
|
+
/** Parse the CLI's one positional: a bare integer is a seq, anything else a key. */
|
|
254
|
+
export declare function parseSubjectRef(text: string): SubjectRef;
|
|
255
|
+
export interface ReviewResult {
|
|
256
|
+
ok: true;
|
|
257
|
+
record: EventRecord;
|
|
258
|
+
subject: SampledSubject;
|
|
259
|
+
/**
|
|
260
|
+
* The `reconciliation.required` this review created, on a denial only
|
|
261
|
+
* (APRV-127). `null` for an `ok` verdict, which obliges nothing.
|
|
262
|
+
*/
|
|
263
|
+
obligation: EventRecord | null;
|
|
264
|
+
}
|
|
265
|
+
/** What a reviewer concluded (amended SPEC.md §5.2, APRV-127). */
|
|
266
|
+
export type ReviewVerdict = "ok" | "denied";
|
|
267
|
+
/**
|
|
268
|
+
* The graded reaction a review or a grant MAY carry (amended SPEC.md §5.2,
|
|
269
|
+
* APRV-237/APRV-239), in the order the schema's enum lists them: worst to best.
|
|
270
|
+
*
|
|
271
|
+
* **This is not enforcement, and it lives here rather than in an enforcement
|
|
272
|
+
* module for that reason.** `verdict` is the field the runtime acts on;
|
|
273
|
+
* `reaction` is what the human thought, travelling human-to-agent, and SPEC.md
|
|
274
|
+
* §11.1 invariant 10 says no routing, class matching, sampling, budget, token,
|
|
275
|
+
* gate-window or execution decision may read it. `tests/values-inert.test.ts`
|
|
276
|
+
* enforces that as a static guard over the enforcement modules, which is why the
|
|
277
|
+
* tuple is exported from `core/audit.ts` (the projection's home) and imported by
|
|
278
|
+
* the surfaces that show it, and by nothing that decides.
|
|
279
|
+
*
|
|
280
|
+
* The vocabulary is closed on purpose. Four words are a grade a person can give
|
|
281
|
+
* in one tap and an agent can read back without interpretation; an open field
|
|
282
|
+
* would accumulate synonyms across surfaces until "meh" and "indifferent" were
|
|
283
|
+
* two different signals. The absence of the field is absence: it is never read
|
|
284
|
+
* as `indifferent`, which is a thing a person had to actually say.
|
|
285
|
+
*/
|
|
286
|
+
export declare const REACTIONS: readonly ["disliked", "indifferent", "liked", "loved"];
|
|
287
|
+
export type Reaction = (typeof REACTIONS)[number];
|
|
288
|
+
/** Whether a string is one of the four graded reactions. Used at the CLI boundary. */
|
|
289
|
+
export declare function isReaction(value: string): value is Reaction;
|
|
290
|
+
/** The shape a denial's obligation takes. */
|
|
291
|
+
export type Obligation = "gated-revert" | "policy-finding";
|
|
292
|
+
export interface ReviewOptions extends AuditOptions {
|
|
293
|
+
/**
|
|
294
|
+
* The verdict. Defaults to `"ok"`: a review whose caller says nothing about
|
|
295
|
+
* what it concluded records the observation it always did, and the ABSENCE of
|
|
296
|
+
* a verdict is never read as a denial.
|
|
297
|
+
*/
|
|
298
|
+
verdict?: ReviewVerdict;
|
|
299
|
+
/**
|
|
300
|
+
* The graded reaction, recorded beside the verdict when the reviewer gave one
|
|
301
|
+
* (APRV-239). Omitted means omitted: nothing is written, and no reader may
|
|
302
|
+
* substitute `indifferent` for a person who said nothing.
|
|
303
|
+
*/
|
|
304
|
+
reaction?: Reaction;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* The obligation a denial creates, chosen by the action's DECLARED
|
|
308
|
+
* reversibility (amended SPEC.md §5.2/§7, APRV-127).
|
|
309
|
+
*
|
|
310
|
+
* - `reversible: true` → `gated-revert`. The action can be undone, so the
|
|
311
|
+
* obligation is to undo it *through the gate*: the revert is itself a
|
|
312
|
+
* side-effecting action, and routing it through the gate is what closes the
|
|
313
|
+
* loop inside the log rather than inside a promise.
|
|
314
|
+
* - `reversible: false` → `policy-finding`. There is nothing to revert. What is
|
|
315
|
+
* left is the finding that the class should not have permitted this without a
|
|
316
|
+
* human, and the sanctioned response is tightening the class; the obligation
|
|
317
|
+
* is the review of that tightening.
|
|
318
|
+
* - **declared nothing** → `policy-finding`, the same as `false`. This is the
|
|
319
|
+
* fail-closed direction, and the reason is worth stating: obliging a revert of
|
|
320
|
+
* an action nobody said could be reverted would record an obligation that may
|
|
321
|
+
* be impossible to discharge, and an impossible obligation is one that gets
|
|
322
|
+
* closed dishonestly. A policy finding is always dischargeable, and it is the
|
|
323
|
+
* heavier of the two: it puts the CLASS on the table rather than one action.
|
|
324
|
+
*
|
|
325
|
+
* ## Self-reported, and only ever in the safe direction
|
|
326
|
+
*
|
|
327
|
+
* `reversible` is written by the party whose action is under review, so global
|
|
328
|
+
* invariant 4 applies: it may never reduce scrutiny. Here it does not. It
|
|
329
|
+
* selects the SHAPE of an obligation that exists either way; it cannot remove
|
|
330
|
+
* one, delay one, or decide whether the denial happened. The one thing a false
|
|
331
|
+
* `reversible: true` buys is a revert obligation instead of a policy finding —
|
|
332
|
+
* and the revert obligation is the one whose satisfaction this runtime checks
|
|
333
|
+
* against the log (`revert-required`), so the lie makes the claimant's own exit
|
|
334
|
+
* harder rather than easier.
|
|
335
|
+
*/
|
|
336
|
+
export declare function obligationFor(reversible: boolean | null): Obligation;
|
|
337
|
+
/**
|
|
338
|
+
* Append `audit.reviewed` for one open sample.
|
|
339
|
+
*
|
|
340
|
+
* HUMAN-ONLY, by the same rule as `grant`/`reject`/`revoke`: the whole content
|
|
341
|
+
* of the event is that a person looked. An agent- or system-authored review
|
|
342
|
+
* would be the party under oversight closing its own audit item, and a backlog
|
|
343
|
+
* that can be emptied by the thing it supervises measures nothing.
|
|
344
|
+
*
|
|
345
|
+
* No attestation is required, for the reason `execution resolve` states: review
|
|
346
|
+
* records an observation and exercises no policy authority. It authorizes
|
|
347
|
+
* nothing, spends no budget, and mints no token.
|
|
348
|
+
*
|
|
349
|
+
* `--note` is optional and recorded verbatim when present. It is not mandatory
|
|
350
|
+
* the way `execution resolve`'s is, because that verb writes an *outcome* the
|
|
351
|
+
* runtime does not know while this one writes only "seen".
|
|
352
|
+
*/
|
|
353
|
+
export declare function reviewSample(logPath: string, ref: SubjectRef, actor: string, note: string | null, options?: ReviewOptions): ReviewResult | AuditRefusal;
|
|
354
|
+
/** One `reconciliation.required`, with the satisfaction that closes it. */
|
|
355
|
+
export interface ReconciliationObligation {
|
|
356
|
+
/** `seq` of the `reconciliation.required` record. */
|
|
357
|
+
seq: number;
|
|
358
|
+
ts: string;
|
|
359
|
+
actionKey: string;
|
|
360
|
+
task: string | null;
|
|
361
|
+
class: string;
|
|
362
|
+
/** `seq` of the `audit.reviewed` that denied. */
|
|
363
|
+
reviewSeq: number;
|
|
364
|
+
obligation: Obligation;
|
|
365
|
+
reversible: boolean | null;
|
|
366
|
+
/** `seq` of the `reconciliation.satisfied`, or `null` while still open. */
|
|
367
|
+
satisfiedSeq: number | null;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Every reconciliation obligation the log carries, each tagged with the
|
|
371
|
+
* satisfaction that closes it.
|
|
372
|
+
*
|
|
373
|
+
* A satisfaction closes an obligation only when it comes **after** it in the
|
|
374
|
+
* chain and names its seq — the same "later, and names it" rule
|
|
375
|
+
* {@link sampledSubjects} applies to reviews, and for the same reason: a
|
|
376
|
+
* backlog that an earlier record could close is a backlog that empties itself.
|
|
377
|
+
*
|
|
378
|
+
* A malformed `reconciliation.required` (no action key, no class, no usable
|
|
379
|
+
* obligation shape) is SKIPPED rather than guessed at. Such a record cannot
|
|
380
|
+
* reach the log through this runtime — the event schema requires all three — so
|
|
381
|
+
* one that is there arrived some other way, and inventing the missing field
|
|
382
|
+
* would put a fact in the backlog that nobody wrote.
|
|
383
|
+
*/
|
|
384
|
+
export declare function reconciliationObligations(records: readonly EventRecord[]): ReconciliationObligation[];
|
|
385
|
+
/** Obligations with no later satisfaction, oldest first. The loud backlog. */
|
|
386
|
+
export declare function openObligations(records: readonly EventRecord[]): ReconciliationObligation[];
|
|
387
|
+
export interface SatisfyResult {
|
|
388
|
+
ok: true;
|
|
389
|
+
record: EventRecord;
|
|
390
|
+
obligation: ReconciliationObligation;
|
|
391
|
+
}
|
|
392
|
+
/** What a human says they did to discharge an obligation. */
|
|
393
|
+
export interface SatisfyInput {
|
|
394
|
+
/** What was done. REQUIRED — see the event schema on why. */
|
|
395
|
+
note: string;
|
|
396
|
+
/**
|
|
397
|
+
* For a `gated-revert` obligation, the action key of the revert. The log must
|
|
398
|
+
* carry an `execution.completed` for it.
|
|
399
|
+
*/
|
|
400
|
+
revertActionKey?: string;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Close one reconciliation obligation.
|
|
404
|
+
*
|
|
405
|
+
* **HUMAN-ONLY**, by the same rule that governs `grant`, `reject`, `revoke` and
|
|
406
|
+
* `audit.reviewed`, and enforced twice: here in code and again by the event
|
|
407
|
+
* schema. The entire content of the record is that a person judged the
|
|
408
|
+
* obligation discharged. A runtime that could satisfy its own obligations would
|
|
409
|
+
* be a reconciliation backlog that empties itself, which is precisely the
|
|
410
|
+
* silence an unreconciled denial exists to break.
|
|
411
|
+
*
|
|
412
|
+
* Two checks beyond the actor, and both are about evidence rather than trust:
|
|
413
|
+
*
|
|
414
|
+
* - **A note is required.** `audit.reviewed` may record only "seen"; this record
|
|
415
|
+
* asserts that something was DONE, and an assertion nobody described is one no
|
|
416
|
+
* auditor can check.
|
|
417
|
+
* - **A `gated-revert` obligation requires a completed revert IN THIS LOG.** The
|
|
418
|
+
* obligation was "undo it through the gate", so the discharge is a gated
|
|
419
|
+
* action that ran, and the runtime looks for its `execution.completed` rather
|
|
420
|
+
* than accepting a sentence saying it happened. That is what closes the loop
|
|
421
|
+
* in the chain. A `policy-finding` obligation has no such artifact — the
|
|
422
|
+
* sanctioned response is a policy amendment, which is a separate human
|
|
423
|
+
* ceremony with its own `policy.updated` record — so the note is the discharge
|
|
424
|
+
* there, and the note is required.
|
|
425
|
+
*
|
|
426
|
+
* No attestation is required, for the reason `audit review` and `execution
|
|
427
|
+
* resolve` state: this record exercises no policy authority, authorizes nothing,
|
|
428
|
+
* spends no budget, and mints no token.
|
|
429
|
+
*/
|
|
430
|
+
export declare function satisfyObligation(logPath: string, obligationSeq: number, actor: string, input: SatisfyInput, options?: AuditOptions): SatisfyResult | AuditRefusal;
|
|
431
|
+
/** Which human gesture an entry came from. */
|
|
432
|
+
export type FeedbackSource = "review" | "decision";
|
|
433
|
+
/**
|
|
434
|
+
* One thing a human said about an action, as `approval feedback` prints it.
|
|
435
|
+
*
|
|
436
|
+
* Everything here is derived from records the caller already verified. Nothing
|
|
437
|
+
* is looked up in a file, no policy is resolved, and no clock is read: two
|
|
438
|
+
* callers handed the same records get the same list in the same order.
|
|
439
|
+
*/
|
|
440
|
+
export interface FeedbackEntry {
|
|
441
|
+
/** `seq` of the record carrying the reaction or note. */
|
|
442
|
+
seq: number;
|
|
443
|
+
ts: string;
|
|
444
|
+
source: FeedbackSource;
|
|
445
|
+
/** `audit.reviewed` or `approval.granted`, spelled as the log spells it. */
|
|
446
|
+
event: string;
|
|
447
|
+
/** The human who said it: the actor on the record itself. */
|
|
448
|
+
actor: string;
|
|
449
|
+
/** The graded reaction, or `null` when the record carries none. */
|
|
450
|
+
reaction: Reaction | null;
|
|
451
|
+
/** The human's words, or `null` when there were none. */
|
|
452
|
+
note: string | null;
|
|
453
|
+
/** The enforcement field, on a review only. `null` on a grant. */
|
|
454
|
+
verdict: ReviewVerdict | null;
|
|
455
|
+
actionKey: string | null;
|
|
456
|
+
task: string | null;
|
|
457
|
+
/** The class from the `task.registered` declaration, never from a payload claim. */
|
|
458
|
+
class: string | null;
|
|
459
|
+
/**
|
|
460
|
+
* The agent whose work this is about, NOT the human who reacted.
|
|
461
|
+
*
|
|
462
|
+
* Sourced from the `task.registered` record that declared the action key, and
|
|
463
|
+
* failing that from the `execution.started` that ran it. Never from a payload
|
|
464
|
+
* field: an actor is a property of who appended a record, and a self-reported
|
|
465
|
+
* one would let the party under oversight choose whose feedback this reads as
|
|
466
|
+
* (SPEC.md §11.1 invariant 4).
|
|
467
|
+
*/
|
|
468
|
+
agentActor: string | null;
|
|
469
|
+
/** `seq` of the `audit.sampled` a review closed, or `null` on a grant. */
|
|
470
|
+
sampleSeq: number | null;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Every reaction and every note a human wrote about an action, oldest first.
|
|
474
|
+
*
|
|
475
|
+
* The HUMAN-TO-AGENT direction of the log (amended SPEC.md §5.2). Two sources,
|
|
476
|
+
* because a human says what they thought in two places: at the gate, answering a
|
|
477
|
+
* request (`approval.granted`), and afterwards, reviewing a sampled action
|
|
478
|
+
* (`audit.reviewed`). Rejections and revocations carry no reaction at all, so
|
|
479
|
+
* they are not a source: their reason IS their note, and the record already says
|
|
480
|
+
* what happened.
|
|
481
|
+
*
|
|
482
|
+
* **An entry with neither a reaction nor a note is omitted.** A grant with no
|
|
483
|
+
* words is the ordinary case, most grants are, and listing thousands of them as
|
|
484
|
+
* blank rows would bury the handful where somebody actually said something.
|
|
485
|
+
* Absence of feedback is not feedback.
|
|
486
|
+
*
|
|
487
|
+
* Reads only the records it is given, and callers pass VERIFIED records: this is
|
|
488
|
+
* a projection in the sense the rest of this module uses the word, it writes
|
|
489
|
+
* nothing, decides nothing, and no enforcement path reads it (SPEC.md §11.1
|
|
490
|
+
* invariant 10).
|
|
491
|
+
*/
|
|
492
|
+
export declare function humanFeedback(records: readonly EventRecord[]): FeedbackEntry[];
|