approval-md 0.2.0 → 0.3.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 +63 -24
- package/SPEC.md +57 -11
- package/dist/src/channels/contract.d.ts +34 -1
- package/dist/src/channels/contract.js +200 -7
- package/dist/src/channels/contract.js.map +1 -1
- package/dist/src/channels/telegram.d.ts +123 -11
- package/dist/src/channels/telegram.js +218 -23
- package/dist/src/channels/telegram.js.map +1 -1
- package/dist/src/channels/web.d.ts +9 -0
- package/dist/src/channels/web.js +17 -0
- package/dist/src/channels/web.js.map +1 -1
- package/dist/src/cli/amend.js +214 -30
- package/dist/src/cli/amend.js.map +1 -1
- package/dist/src/cli/attest.d.ts +9 -0
- package/dist/src/cli/attest.js +134 -7
- package/dist/src/cli/attest.js.map +1 -1
- package/dist/src/cli/channel-telegram.d.ts +99 -26
- package/dist/src/cli/channel-telegram.js +311 -13
- package/dist/src/cli/channel-telegram.js.map +1 -1
- package/dist/src/cli/channel.d.ts +9 -0
- package/dist/src/cli/channel.js +9 -0
- package/dist/src/cli/channel.js.map +1 -1
- package/dist/src/cli/codex-bridge.d.ts +819 -0
- package/dist/src/cli/codex-bridge.js +1607 -0
- package/dist/src/cli/codex-bridge.js.map +1 -0
- package/dist/src/cli/codex.d.ts +1 -1
- package/dist/src/cli/codex.js +304 -7
- package/dist/src/cli/codex.js.map +1 -1
- package/dist/src/cli/daemon.js +4 -1
- package/dist/src/cli/daemon.js.map +1 -1
- package/dist/src/cli/doctor.js +467 -12
- package/dist/src/cli/doctor.js.map +1 -1
- package/dist/src/cli/execute.js +25 -2
- package/dist/src/cli/execute.js.map +1 -1
- package/dist/src/cli/help.d.ts +6 -2
- package/dist/src/cli/help.js +165 -60
- package/dist/src/cli/help.js.map +1 -1
- package/dist/src/cli/hook-codex.d.ts +49 -1
- package/dist/src/cli/hook-codex.js +60 -1
- package/dist/src/cli/hook-codex.js.map +1 -1
- package/dist/src/cli/hook.d.ts +459 -3
- package/dist/src/cli/hook.js +1062 -114
- package/dist/src/cli/hook.js.map +1 -1
- package/dist/src/cli/import.js +1 -1
- package/dist/src/cli/import.js.map +1 -1
- package/dist/src/cli/main.js +5 -3
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/policy-apply.d.ts +195 -0
- package/dist/src/cli/policy-apply.js +573 -0
- package/dist/src/cli/policy-apply.js.map +1 -0
- package/dist/src/cli/policy.js +14 -1
- package/dist/src/cli/policy.js.map +1 -1
- package/dist/src/cli/preflight.d.ts +151 -13
- package/dist/src/cli/preflight.js +398 -41
- package/dist/src/cli/preflight.js.map +1 -1
- package/dist/src/cli/sandbox.js +17 -1
- package/dist/src/cli/sandbox.js.map +1 -1
- package/dist/src/cli/scaffold.d.ts +1 -1
- package/dist/src/cli/scaffold.js +1 -1
- package/dist/src/cli/setup-channel.d.ts +9 -0
- package/dist/src/cli/setup-channel.js +28 -1
- package/dist/src/cli/setup-channel.js.map +1 -1
- package/dist/src/cli/setup-common.d.ts +3 -1
- package/dist/src/cli/setup-common.js +3 -2
- package/dist/src/cli/setup-common.js.map +1 -1
- package/dist/src/cli/setup.d.ts +2 -0
- package/dist/src/cli/setup.js +94 -2
- package/dist/src/cli/setup.js.map +1 -1
- package/dist/src/cli/up.js +115 -51
- package/dist/src/cli/up.js.map +1 -1
- package/dist/src/cli/values.js +3 -4
- package/dist/src/cli/values.js.map +1 -1
- package/dist/src/cli/verb-registry.js +174 -9
- package/dist/src/cli/verb-registry.js.map +1 -1
- package/dist/src/cli/wordmark.d.ts +2 -2
- package/dist/src/cli/wordmark.js +2 -2
- package/dist/src/codex/broker.d.ts +229 -0
- package/dist/src/codex/broker.js +548 -0
- package/dist/src/codex/broker.js.map +1 -0
- package/dist/src/codex/runner.d.ts +178 -0
- package/dist/src/codex/runner.js +231 -0
- package/dist/src/codex/runner.js.map +1 -0
- package/dist/src/codex/serve.d.ts +56 -0
- package/dist/src/codex/serve.js +98 -0
- package/dist/src/codex/serve.js.map +1 -0
- package/dist/src/codex/workspace-commit.d.ts +219 -0
- package/dist/src/codex/workspace-commit.js +549 -0
- package/dist/src/codex/workspace-commit.js.map +1 -0
- package/dist/src/core/advance-cycle.d.ts +51 -0
- package/dist/src/core/advance-cycle.js +66 -2
- package/dist/src/core/advance-cycle.js.map +1 -1
- package/dist/src/core/agents-md.d.ts +20 -18
- package/dist/src/core/agents-md.js +33 -31
- package/dist/src/core/agents-md.js.map +1 -1
- package/dist/src/core/attest.d.ts +215 -0
- package/dist/src/core/attest.js +317 -7
- package/dist/src/core/attest.js.map +1 -1
- package/dist/src/core/audit.d.ts +18 -0
- package/dist/src/core/audit.js +13 -0
- package/dist/src/core/audit.js.map +1 -1
- package/dist/src/core/channel-owner.d.ts +213 -0
- package/dist/src/core/channel-owner.js +358 -0
- package/dist/src/core/channel-owner.js.map +1 -0
- package/dist/src/core/command-class.d.ts +154 -0
- package/dist/src/core/command-class.js +673 -20
- package/dist/src/core/command-class.js.map +1 -1
- package/dist/src/core/commit-guard.d.ts +272 -0
- package/dist/src/core/commit-guard.js +424 -0
- package/dist/src/core/commit-guard.js.map +1 -0
- package/dist/src/core/daemon-actor.d.ts +45 -0
- package/dist/src/core/daemon-actor.js +54 -0
- package/dist/src/core/daemon-actor.js.map +1 -0
- package/dist/src/core/dark-session.d.ts +109 -8
- package/dist/src/core/dark-session.js +266 -82
- package/dist/src/core/dark-session.js.map +1 -1
- package/dist/src/core/decision-refusal.d.ts +23 -2
- package/dist/src/core/decision-refusal.js +24 -2
- package/dist/src/core/decision-refusal.js.map +1 -1
- package/dist/src/core/env-file.d.ts +5 -0
- package/dist/src/core/env-file.js +60 -1
- package/dist/src/core/env-file.js.map +1 -1
- package/dist/src/core/execute.d.ts +15 -2
- package/dist/src/core/execute.js +15 -2
- package/dist/src/core/execute.js.map +1 -1
- package/dist/src/core/gate.d.ts +86 -1
- package/dist/src/core/gate.js +81 -1
- package/dist/src/core/gate.js.map +1 -1
- package/dist/src/core/gesture-refusal.d.ts +166 -0
- package/dist/src/core/gesture-refusal.js +188 -0
- package/dist/src/core/gesture-refusal.js.map +1 -0
- package/dist/src/core/harness-version.d.ts +1 -1
- package/dist/src/core/harness-version.js +3 -1
- package/dist/src/core/harness-version.js.map +1 -1
- package/dist/src/core/instance.d.ts +59 -2
- package/dist/src/core/instance.js +113 -0
- package/dist/src/core/instance.js.map +1 -1
- package/dist/src/core/log.d.ts +39 -1
- package/dist/src/core/log.js.map +1 -1
- package/dist/src/core/policy-explain.d.ts +10 -0
- package/dist/src/core/policy-explain.js +32 -0
- package/dist/src/core/policy-explain.js.map +1 -1
- package/dist/src/core/policy-load.d.ts +41 -1
- package/dist/src/core/policy-load.js +21 -3
- package/dist/src/core/policy-load.js.map +1 -1
- package/dist/src/core/policy-match.d.ts +43 -0
- package/dist/src/core/policy-match.js +52 -0
- package/dist/src/core/policy-match.js.map +1 -1
- package/dist/src/core/policy-proposal.d.ts +52 -0
- package/dist/src/core/policy-proposal.js +102 -2
- package/dist/src/core/policy-proposal.js.map +1 -1
- package/dist/src/core/protected-path-guard.d.ts +117 -4
- package/dist/src/core/protected-path-guard.js +362 -48
- package/dist/src/core/protected-path-guard.js.map +1 -1
- package/dist/src/core/question-preempted.d.ts +141 -0
- package/dist/src/core/question-preempted.js +152 -0
- package/dist/src/core/question-preempted.js.map +1 -0
- package/dist/src/core/read-scope.d.ts +172 -0
- package/dist/src/core/read-scope.js +252 -0
- package/dist/src/core/read-scope.js.map +1 -0
- package/dist/src/core/sandbox.d.ts +81 -0
- package/dist/src/core/sandbox.js +190 -1
- package/dist/src/core/sandbox.js.map +1 -1
- package/dist/src/core/sender-identity.d.ts +476 -0
- package/dist/src/core/sender-identity.js +572 -0
- package/dist/src/core/sender-identity.js.map +1 -0
- package/dist/src/core/shlex.d.ts +102 -0
- package/dist/src/core/shlex.js +159 -0
- package/dist/src/core/shlex.js.map +1 -0
- package/dist/src/core/values.d.ts +18 -8
- package/dist/src/core/values.js +36 -1
- package/dist/src/core/values.js.map +1 -1
- package/dist/src/daemon/advance.d.ts +10 -0
- package/dist/src/daemon/advance.js +25 -4
- package/dist/src/daemon/advance.js.map +1 -1
- package/dist/src/daemon/daemon.js +9 -0
- package/dist/src/daemon/daemon.js.map +1 -1
- package/dist/src/daemon/git-evidence.d.ts +2 -2
- package/dist/src/daemon/git-evidence.js +1 -1
- package/dist/src/mcp/server.js +8 -0
- package/dist/src/mcp/server.js.map +1 -1
- package/docs/cli-reference.md +932 -32
- package/docs/codex-enforced-session.md +75 -2
- package/docs/codex-workspace-broker.md +118 -0
- package/package.json +3 -1
- package/schema/event.schema.json +538 -9
- package/schema/fixtures/event/invalid/approval-granted-sender-hashed-false.json +20 -0
- package/schema/fixtures/event/invalid/approval-granted-sender-hashed-raw-id.json +20 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-human-actor.json +16 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-no-actor-no-sender.json +15 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-unknown-gesture.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-agent-actor.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-no-question-id.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-unknown-source.json +15 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-absolute-path.json +14 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-agent-actor.json +14 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-missing-path.json +13 -0
- package/schema/fixtures/event/valid/approval-granted-sender-hashed.json +20 -0
- package/schema/fixtures/event/valid/audit-gesture-refused-review-note.json +21 -0
- package/schema/fixtures/event/valid/audit-gesture-refused-sender-key-unavailable.json +19 -0
- package/schema/fixtures/event/valid/audit-gesture-refused.json +19 -0
- package/schema/fixtures/event/valid/audit-question-preempted-no-verdict.json +16 -0
- package/schema/fixtures/event/valid/audit-question-preempted.json +20 -0
- package/schema/fixtures/event/valid/gate-path-signed-off.json +14 -0
- package/schema/fixtures/event/valid/harness-kind-claude-code.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-codex.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-cursor.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-grok.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-muse.json +23 -0
- package/schema/fixtures/policy/invalid/senders-half-keyed.json +20 -0
- package/schema/fixtures/policy/valid/canonical.json +1 -1
- package/schema/fixtures/policy/valid/senders-keyed.json +24 -0
- package/schema/fixtures/policy-md/valid/canonical.md +1 -1
- package/schema/fixtures/policy-md/valid/with-values.md +5 -7
- package/schema/fixtures/values/invalid/class-shaped.json +1 -1
- package/schema/fixtures/values/invalid/duplicate-entry.json +1 -1
- package/schema/fixtures/values/invalid/non-string-item.json +1 -1
- package/schema/fixtures/values/invalid/over-cap.json +1 -1
- package/schema/fixtures/values/invalid/unknown-key.json +1 -1
- package/schema/fixtures/values/invalid/version-float.json +1 -0
- package/schema/fixtures/values/invalid/version-integer.json +1 -0
- package/schema/fixtures/values/invalid/version-wrong-string.json +1 -0
- package/schema/fixtures/values/valid/empty-lists.json +2 -3
- package/schema/fixtures/values/valid/full.json +5 -7
- package/schema/fixtures/values/valid/minimal.json +1 -1
- package/schema/fixtures/values-md/invalid/schema-invalid.md +5 -3
- package/schema/fixtures/values-md/invalid/two-blocks.md +3 -3
- package/schema/fixtures/values-md/invalid/unterminated.md +2 -2
- package/schema/fixtures/values-md/invalid/version-1.md +69 -0
- package/schema/fixtures/values-md/invalid/version-unquoted.md +64 -0
- package/schema/fixtures/values-md/invalid/yaml-error.md +2 -2
- package/schema/fixtures/values-md/valid/absent.md +1 -1
- package/schema/fixtures/values-md/valid/with-values.md +5 -7
- package/schema/policy.schema.json +54 -2
- package/schema/values.schema.json +7 -11
- package/schema/fixtures/values/invalid/version-string.json +0 -1
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The durable half of the Codex workspace broker (APRV-325.2).
|
|
3
|
+
*
|
|
4
|
+
* `workspace-plan.ts` decides what a change IS; this module is the only place
|
|
5
|
+
* that makes one happen. It exists because POSIX gives no atomic multi-file
|
|
6
|
+
* rename: four `rename(2)` calls are four separate instants, and a crash
|
|
7
|
+
* between the second and the third leaves a workspace that is neither the state
|
|
8
|
+
* the human approved nor the state they started from.
|
|
9
|
+
*
|
|
10
|
+
* ## The three states, and the one this module refuses to invent
|
|
11
|
+
*
|
|
12
|
+
* A committed transaction is `after`. An untouched one is `before`. Anything
|
|
13
|
+
* else is `mixed`, and mixed is reported as mixed. The classification is made
|
|
14
|
+
* by READING the filesystem back against the journal's recorded digests, never
|
|
15
|
+
* from what the applying code believes it did: a `rename` that returned zero
|
|
16
|
+
* and a `rename` whose effect a crash lost look identical from inside the
|
|
17
|
+
* process that called it, and only the bytes on disk can tell them apart. That
|
|
18
|
+
* is why {@link inspectWorkspaceState} runs after a successful apply as well as
|
|
19
|
+
* after a failed one, and why a mixed result reaches the log as
|
|
20
|
+
* `execution.indeterminate` with reason `workspace-commit-unknown` rather than
|
|
21
|
+
* as a failure (SPEC.md §8's closed reason set, extended by this task).
|
|
22
|
+
*
|
|
23
|
+
* ## Ordering, and why the journal is written before anything moves
|
|
24
|
+
*
|
|
25
|
+
* 1. Stage every new byte and every preimage into a directory on the SAME
|
|
26
|
+
* filesystem as the workspace, and `fsync` each file and then the directory.
|
|
27
|
+
* Nothing in the workspace has changed yet, so a crash here is `before`.
|
|
28
|
+
* 2. Write the journal — the plan's payload hash, every endpoint, and the
|
|
29
|
+
* before/after digest of each — and `fsync` it and its directory. This is
|
|
30
|
+
* the instant after which a crash is RECOVERABLE rather than merely
|
|
31
|
+
* undecidable: a journal on disk names what the workspace was and what it
|
|
32
|
+
* was becoming, so a later reader can prove which of the two it is in.
|
|
33
|
+
* 3. Apply, in a fixed order, then `fsync` every touched parent directory.
|
|
34
|
+
* 4. Read the endpoints back. Only an all-`after` reading removes the journal
|
|
35
|
+
* as a success; an all-`before` reading removes it as an untaken
|
|
36
|
+
* transaction; a mixed reading LEAVES IT for a person.
|
|
37
|
+
*
|
|
38
|
+
* ## Custody is claimed only as far as the platform proves it
|
|
39
|
+
*
|
|
40
|
+
* {@link acquireWorkspaceCustody} takes an `O_CREAT | O_EXCL` lock, which
|
|
41
|
+
* excludes other cooperating brokers and nothing else, and then asks POSIX
|
|
42
|
+
* ownership and mode whether any other principal can write the endpoints it is
|
|
43
|
+
* about to touch. It reports `os-exclusive` only when both hold, `advisory`
|
|
44
|
+
* otherwise, and it always reports `acl-unproven`, because ownership and mode
|
|
45
|
+
* do not speak about ACLs (the same honesty `codex/trust.ts` already keeps). A
|
|
46
|
+
* caller that needs the strong answer asks for it and is refused when the host
|
|
47
|
+
* cannot give it; nothing here silently downgrades.
|
|
48
|
+
*/
|
|
49
|
+
import { createHash } from "node:crypto";
|
|
50
|
+
import { closeSync, constants, fstatSync, fsyncSync, mkdirSync, openSync, readFileSync, readSync, renameSync, rmSync, statSync, unlinkSync, writeSync, } from "node:fs";
|
|
51
|
+
import { dirname, join, resolve } from "node:path";
|
|
52
|
+
import { canonicalize } from "../core/jcs.js";
|
|
53
|
+
export const WORKSPACE_COMMIT_VERSION = "approval.codex.workspace-commit.v1";
|
|
54
|
+
/**
|
|
55
|
+
* The one directory name inside a workspace root that a proposal may never
|
|
56
|
+
* name (the broker refuses an endpoint under it before the planner ever reads
|
|
57
|
+
* a preimage). Staging has to share a filesystem with the workspace for
|
|
58
|
+
* `rename` to be atomic, so it lives inside it; reserving the name is what
|
|
59
|
+
* stops a proposal from staging over its own transaction.
|
|
60
|
+
*/
|
|
61
|
+
export const WORKSPACE_TXN_DIR = ".approval-codex-txn";
|
|
62
|
+
/** The lockfile, beside the staging directory and under the same reservation. */
|
|
63
|
+
export const WORKSPACE_LOCK_FILE = ".approval-codex-lock";
|
|
64
|
+
const JOURNAL_FILE = "journal.json";
|
|
65
|
+
function fsyncDir(path) {
|
|
66
|
+
let fd = null;
|
|
67
|
+
try {
|
|
68
|
+
fd = openSync(path, constants.O_RDONLY);
|
|
69
|
+
fsyncSync(fd);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// A filesystem that refuses to fsync a directory handle (some network and
|
|
73
|
+
// virtual filesystems do) is reported by the readback, not guessed at here.
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
if (fd !== null)
|
|
77
|
+
closeSync(fd);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function writeDurable(path, bytes) {
|
|
81
|
+
const fd = openSync(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o600);
|
|
82
|
+
try {
|
|
83
|
+
let written = 0;
|
|
84
|
+
while (written < bytes.byteLength) {
|
|
85
|
+
written += writeSync(fd, bytes, written, bytes.byteLength - written);
|
|
86
|
+
}
|
|
87
|
+
fsyncSync(fd);
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
closeSync(fd);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Is `path` writable by a principal other than this process's effective user,
|
|
95
|
+
* as far as POSIX ownership and mode can say?
|
|
96
|
+
*
|
|
97
|
+
* `null` means the question could not be asked (the path could not be stated),
|
|
98
|
+
* which is not the same as "no" and is never read as one.
|
|
99
|
+
*/
|
|
100
|
+
function foreignWritable(path) {
|
|
101
|
+
try {
|
|
102
|
+
const stats = statSync(path);
|
|
103
|
+
if ((stats.mode & 0o022) !== 0)
|
|
104
|
+
return true;
|
|
105
|
+
return stats.uid !== process.getuid?.();
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Take the workspace lock and report how much exclusion it bought.
|
|
113
|
+
*
|
|
114
|
+
* The lock is `O_CREAT | O_EXCL` on a reserved name, which is the strongest
|
|
115
|
+
* primitive available to a process with no privilege: it excludes every other
|
|
116
|
+
* broker that respects it and no one else. Whether anything else CAN write is a
|
|
117
|
+
* separate question, asked of POSIX afterwards, and answered conservatively.
|
|
118
|
+
*/
|
|
119
|
+
export function acquireWorkspaceCustody(root, touchedDirectories) {
|
|
120
|
+
const lockPath = join(root, WORKSPACE_LOCK_FILE);
|
|
121
|
+
const txnDir = join(root, WORKSPACE_TXN_DIR);
|
|
122
|
+
let lockFd;
|
|
123
|
+
try {
|
|
124
|
+
lockFd = openSync(lockPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o600);
|
|
125
|
+
}
|
|
126
|
+
catch (cause) {
|
|
127
|
+
const code = cause.code;
|
|
128
|
+
if (code === "EEXIST") {
|
|
129
|
+
return {
|
|
130
|
+
ok: false,
|
|
131
|
+
code: "custody-contended",
|
|
132
|
+
message: `another workspace transaction holds ${lockPath}; no operation was attempted`,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
ok: false,
|
|
137
|
+
code: "custody-unavailable",
|
|
138
|
+
message: `workspace lock ${lockPath} could not be created: ${String(code ?? cause)}`,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
closeSync(lockFd);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// The lock is the file's existence, not the descriptor.
|
|
146
|
+
}
|
|
147
|
+
const findings = ["acl-unproven"];
|
|
148
|
+
let exclusive = true;
|
|
149
|
+
for (const directory of [root, ...touchedDirectories]) {
|
|
150
|
+
const foreign = foreignWritable(directory);
|
|
151
|
+
if (foreign === null) {
|
|
152
|
+
findings.push(`custody-unreadable:${directory}`);
|
|
153
|
+
exclusive = false;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (foreign) {
|
|
157
|
+
findings.push(`foreign-writable:${directory}`);
|
|
158
|
+
exclusive = false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
mkdirSync(txnDir, { mode: 0o700 });
|
|
163
|
+
}
|
|
164
|
+
catch (cause) {
|
|
165
|
+
try {
|
|
166
|
+
unlinkSync(lockPath);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// Best effort: the failure below is the one that matters.
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
ok: false,
|
|
173
|
+
code: "custody-unavailable",
|
|
174
|
+
message: `staging directory ${txnDir} could not be created: ${String(cause.code ?? cause)}`,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
fsyncDir(root);
|
|
178
|
+
return {
|
|
179
|
+
ok: true,
|
|
180
|
+
custody: {
|
|
181
|
+
report: { kind: exclusive ? "os-exclusive" : "advisory", findings },
|
|
182
|
+
root,
|
|
183
|
+
txnDir,
|
|
184
|
+
release: () => {
|
|
185
|
+
try {
|
|
186
|
+
rmSync(txnDir, { recursive: true, force: true });
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// Leaving staging behind is visible to recovery and to a person.
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
unlinkSync(lockPath);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// Same.
|
|
196
|
+
}
|
|
197
|
+
fsyncDir(root);
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function sha256(bytes) {
|
|
203
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* The endpoints one planned operation owns, in the shape recovery reads.
|
|
207
|
+
*
|
|
208
|
+
* A move owns two, and the second one's `before` is deliberately `null`: the
|
|
209
|
+
* planner refuses a move whose destination exists, so "absent" is the state the
|
|
210
|
+
* transaction started from and the state a rollback must restore.
|
|
211
|
+
*/
|
|
212
|
+
export function journalEndpointsOf(operation) {
|
|
213
|
+
switch (operation.kind) {
|
|
214
|
+
case "create":
|
|
215
|
+
return [{ path: operation.path, before_sha256: null, after_sha256: operation.after.sha256 }];
|
|
216
|
+
case "replace":
|
|
217
|
+
return [{
|
|
218
|
+
path: operation.path,
|
|
219
|
+
before_sha256: operation.before.sha256,
|
|
220
|
+
after_sha256: operation.after.sha256,
|
|
221
|
+
}];
|
|
222
|
+
case "delete":
|
|
223
|
+
return [{ path: operation.path, before_sha256: operation.before.sha256, after_sha256: null }];
|
|
224
|
+
case "move":
|
|
225
|
+
return [
|
|
226
|
+
{ path: operation.from, before_sha256: operation.before.sha256, after_sha256: null },
|
|
227
|
+
{ path: operation.to, before_sha256: null, after_sha256: operation.before.sha256 },
|
|
228
|
+
];
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function newBytesOf(operation) {
|
|
232
|
+
if (operation.kind === "create" || operation.kind === "replace") {
|
|
233
|
+
return Buffer.from(operation.after.base64, "base64");
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
function preimageBytesOf(operation) {
|
|
238
|
+
if (operation.kind === "create")
|
|
239
|
+
return null;
|
|
240
|
+
return Buffer.from(operation.before.base64, "base64");
|
|
241
|
+
}
|
|
242
|
+
/** Every parent directory a transaction will write into, absolute and deduplicated. */
|
|
243
|
+
export function touchedDirectories(plan) {
|
|
244
|
+
const seen = new Set();
|
|
245
|
+
for (const operation of plan.payload.operations) {
|
|
246
|
+
for (const endpoint of journalEndpointsOf(operation)) {
|
|
247
|
+
seen.add(dirname(resolve(plan.payload.root, endpoint.path)));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return [...seen].sort();
|
|
251
|
+
}
|
|
252
|
+
const MAX_READBACK_BYTES = 1_048_576;
|
|
253
|
+
/**
|
|
254
|
+
* The digest of a regular single-link file, `null` when it is absent, and
|
|
255
|
+
* `undefined` when the question could not be answered.
|
|
256
|
+
*
|
|
257
|
+
* `O_NOFOLLOW` and the `nlink` test are the planner's rules, repeated here
|
|
258
|
+
* because a readback that followed a symlink planted between the apply and the
|
|
259
|
+
* inspection would report someone else's bytes as this transaction's outcome.
|
|
260
|
+
*/
|
|
261
|
+
function readbackDigest(path) {
|
|
262
|
+
let fd = null;
|
|
263
|
+
try {
|
|
264
|
+
fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
265
|
+
}
|
|
266
|
+
catch (cause) {
|
|
267
|
+
const code = cause.code;
|
|
268
|
+
if (code === "ENOENT")
|
|
269
|
+
return null;
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
try {
|
|
273
|
+
const stats = fstatSync(fd, { bigint: true });
|
|
274
|
+
if (!stats.isFile() || stats.nlink !== 1n)
|
|
275
|
+
return undefined;
|
|
276
|
+
if (stats.size > BigInt(MAX_READBACK_BYTES))
|
|
277
|
+
return undefined;
|
|
278
|
+
const buffer = Buffer.alloc(Number(stats.size));
|
|
279
|
+
let length = 0;
|
|
280
|
+
while (length < buffer.byteLength) {
|
|
281
|
+
const count = readSync(fd, buffer, length, buffer.byteLength - length, length);
|
|
282
|
+
if (count === 0)
|
|
283
|
+
break;
|
|
284
|
+
length += count;
|
|
285
|
+
}
|
|
286
|
+
if (length !== buffer.byteLength)
|
|
287
|
+
return undefined;
|
|
288
|
+
return sha256(buffer);
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
finally {
|
|
294
|
+
closeSync(fd);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function endpointState(root, endpoint) {
|
|
298
|
+
const digest = readbackDigest(resolve(root, endpoint.path));
|
|
299
|
+
if (digest === undefined)
|
|
300
|
+
return "unreadable";
|
|
301
|
+
const isAfter = digest === endpoint.after_sha256;
|
|
302
|
+
const isBefore = digest === endpoint.before_sha256;
|
|
303
|
+
// `before` wins a tie, and a tie is real: a replace whose new bytes equal its
|
|
304
|
+
// old bytes is both, and calling it `before` can only under-claim.
|
|
305
|
+
if (isBefore)
|
|
306
|
+
return "before";
|
|
307
|
+
if (isAfter)
|
|
308
|
+
return "after";
|
|
309
|
+
return "other";
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Classify the workspace against a journal by reading it.
|
|
313
|
+
*
|
|
314
|
+
* `mixed` is the honest answer for anything that is not uniformly one state,
|
|
315
|
+
* INCLUDING a single unreadable endpoint: "I could not look" and "it is half
|
|
316
|
+
* done" are both "nobody knows", and collapsing the first into `before` would
|
|
317
|
+
* be the runtime deciding an effect did not happen.
|
|
318
|
+
*/
|
|
319
|
+
export function inspectWorkspaceState(journal) {
|
|
320
|
+
const endpoints = [];
|
|
321
|
+
for (const operation of journal.operations) {
|
|
322
|
+
for (const endpoint of operation.endpoints) {
|
|
323
|
+
endpoints.push({ path: endpoint.path, state: endpointState(journal.root, endpoint) });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const every = (state) => endpoints.every((entry) => entry.state === state);
|
|
327
|
+
if (endpoints.length > 0 && every("after"))
|
|
328
|
+
return { state: "after", endpoints };
|
|
329
|
+
if (endpoints.length === 0 || every("before"))
|
|
330
|
+
return { state: "before", endpoints };
|
|
331
|
+
return { state: "mixed", endpoints };
|
|
332
|
+
}
|
|
333
|
+
function journalPath(txnDir) {
|
|
334
|
+
return join(txnDir, JOURNAL_FILE);
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Stage, journal, apply, read back.
|
|
338
|
+
*
|
|
339
|
+
* The caller holds custody (and therefore the staging directory) and keeps it
|
|
340
|
+
* until this returns. A `stage-failed` result is the one result that guarantees
|
|
341
|
+
* the workspace is untouched by construction rather than by inspection: nothing
|
|
342
|
+
* visible has been attempted when it fires.
|
|
343
|
+
*/
|
|
344
|
+
export function commitWorkspacePlan(plan, custody, options = {}) {
|
|
345
|
+
const { root, txnDir } = custody;
|
|
346
|
+
const operations = [];
|
|
347
|
+
// --- Phase 1: stage. Nothing in the workspace changes. ------------------
|
|
348
|
+
try {
|
|
349
|
+
for (let index = 0; index < plan.payload.operations.length; index += 1) {
|
|
350
|
+
const operation = plan.payload.operations[index];
|
|
351
|
+
const newBytes = newBytesOf(operation);
|
|
352
|
+
const preimage = preimageBytesOf(operation);
|
|
353
|
+
const staged = newBytes === null ? null : `new-${String(index)}`;
|
|
354
|
+
const saved = preimage === null ? null : `old-${String(index)}`;
|
|
355
|
+
if (staged !== null && newBytes !== null)
|
|
356
|
+
writeDurable(join(txnDir, staged), newBytes);
|
|
357
|
+
if (saved !== null && preimage !== null)
|
|
358
|
+
writeDurable(join(txnDir, saved), preimage);
|
|
359
|
+
operations.push({
|
|
360
|
+
index,
|
|
361
|
+
kind: operation.kind,
|
|
362
|
+
endpoints: journalEndpointsOf(operation),
|
|
363
|
+
staged,
|
|
364
|
+
preimage: saved,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
fsyncDir(txnDir);
|
|
368
|
+
}
|
|
369
|
+
catch (cause) {
|
|
370
|
+
return {
|
|
371
|
+
ok: false,
|
|
372
|
+
code: "stage-failed",
|
|
373
|
+
message: `workspace staging failed before any mutation: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
const journal = {
|
|
377
|
+
version: WORKSPACE_COMMIT_VERSION,
|
|
378
|
+
root,
|
|
379
|
+
payload_hash: plan.payload_hash,
|
|
380
|
+
custody: custody.report.kind,
|
|
381
|
+
operations,
|
|
382
|
+
};
|
|
383
|
+
// --- Phase 2: the journal, durable, before anything moves. --------------
|
|
384
|
+
try {
|
|
385
|
+
writeDurable(journalPath(txnDir), Buffer.from(canonicalize(journal), "utf8"));
|
|
386
|
+
fsyncDir(txnDir);
|
|
387
|
+
}
|
|
388
|
+
catch (cause) {
|
|
389
|
+
return {
|
|
390
|
+
ok: false,
|
|
391
|
+
code: "stage-failed",
|
|
392
|
+
message: `workspace journal could not be made durable before any mutation: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
// --- Phase 3: apply. From here the answer comes from reading. -----------
|
|
396
|
+
let failure;
|
|
397
|
+
try {
|
|
398
|
+
options.onStep?.(0);
|
|
399
|
+
for (const entry of operations) {
|
|
400
|
+
const operation = plan.payload.operations[entry.index];
|
|
401
|
+
applyOne(root, txnDir, operation, entry);
|
|
402
|
+
options.onStep?.(entry.index + 1);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
catch (cause) {
|
|
406
|
+
failure = cause instanceof Error ? cause.message : String(cause);
|
|
407
|
+
}
|
|
408
|
+
for (const directory of touchedDirectories(plan))
|
|
409
|
+
fsyncDir(directory);
|
|
410
|
+
// --- Phase 4: a failed apply tries to undo itself, and is then read. ----
|
|
411
|
+
if (failure !== undefined) {
|
|
412
|
+
try {
|
|
413
|
+
rollback(root, txnDir, operations);
|
|
414
|
+
}
|
|
415
|
+
catch (cause) {
|
|
416
|
+
failure = `${failure}; rollback also failed: ${cause instanceof Error ? cause.message : String(cause)}`;
|
|
417
|
+
}
|
|
418
|
+
for (const directory of touchedDirectories(plan))
|
|
419
|
+
fsyncDir(directory);
|
|
420
|
+
}
|
|
421
|
+
const inspection = inspectWorkspaceState(journal);
|
|
422
|
+
const retained = inspection.state === "mixed";
|
|
423
|
+
if (!retained) {
|
|
424
|
+
try {
|
|
425
|
+
unlinkSync(journalPath(txnDir));
|
|
426
|
+
fsyncDir(txnDir);
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
// Recovery reads a journal beside a settled workspace as settled.
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return {
|
|
433
|
+
ok: true,
|
|
434
|
+
outcome: {
|
|
435
|
+
state: inspection.state,
|
|
436
|
+
inspection,
|
|
437
|
+
journal,
|
|
438
|
+
journalRetained: retained,
|
|
439
|
+
...(failure === undefined ? {} : { failure }),
|
|
440
|
+
},
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
function applyOne(root, txnDir, operation, entry) {
|
|
444
|
+
switch (operation.kind) {
|
|
445
|
+
case "create":
|
|
446
|
+
case "replace":
|
|
447
|
+
renameSync(join(txnDir, entry.staged), resolve(root, operation.path));
|
|
448
|
+
return;
|
|
449
|
+
case "delete":
|
|
450
|
+
unlinkSync(resolve(root, operation.path));
|
|
451
|
+
return;
|
|
452
|
+
case "move":
|
|
453
|
+
renameSync(resolve(root, operation.from), resolve(root, operation.to));
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Put every endpoint back the way the journal says it was, from the staged
|
|
459
|
+
* preimages.
|
|
460
|
+
*
|
|
461
|
+
* Best effort by construction: it is attempted, and then the workspace is READ.
|
|
462
|
+
* A rollback that half worked produces `mixed` from the inspection, which is
|
|
463
|
+
* the same answer a rollback that was never attempted would produce, and that
|
|
464
|
+
* is deliberate — the classification must not depend on what this function
|
|
465
|
+
* thinks it achieved.
|
|
466
|
+
*/
|
|
467
|
+
function rollback(root, txnDir, operations) {
|
|
468
|
+
const problems = [];
|
|
469
|
+
for (let index = operations.length - 1; index >= 0; index -= 1) {
|
|
470
|
+
const entry = operations[index];
|
|
471
|
+
try {
|
|
472
|
+
for (let slot = 0; slot < entry.endpoints.length; slot += 1) {
|
|
473
|
+
const endpoint = entry.endpoints[slot];
|
|
474
|
+
const target = resolve(root, endpoint.path);
|
|
475
|
+
if (endpoint.before_sha256 === null) {
|
|
476
|
+
try {
|
|
477
|
+
unlinkSync(target);
|
|
478
|
+
}
|
|
479
|
+
catch (cause) {
|
|
480
|
+
if (cause.code !== "ENOENT")
|
|
481
|
+
throw cause;
|
|
482
|
+
}
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
if (readbackDigest(target) === endpoint.before_sha256)
|
|
486
|
+
continue;
|
|
487
|
+
if (entry.preimage === null)
|
|
488
|
+
continue;
|
|
489
|
+
const restored = join(txnDir, `restore-${String(index)}-${String(slot)}`);
|
|
490
|
+
writeDurable(restored, readFileSync(join(txnDir, entry.preimage)));
|
|
491
|
+
renameSync(restored, target);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
catch (cause) {
|
|
495
|
+
problems.push(cause instanceof Error ? cause.message : String(cause));
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
if (problems.length > 0)
|
|
499
|
+
throw new Error(problems.join("; "));
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Read a workspace's retained journal and say which of the three states it is
|
|
503
|
+
* in. It CHANGES NOTHING in the workspace.
|
|
504
|
+
*
|
|
505
|
+
* That is the whole contract, and the restraint is the point: a recovery that
|
|
506
|
+
* rolled a mixed workspace forward would be guessing which half of it the
|
|
507
|
+
* human approved, and a recovery that rolled one back would be deleting the
|
|
508
|
+
* half that already committed. Both are the runtime inventing a fact. A proven
|
|
509
|
+
* `before` or `after` needs no repair by definition, so there is nothing left
|
|
510
|
+
* for this function to do but say so and let the operator clear the staging
|
|
511
|
+
* directory.
|
|
512
|
+
*/
|
|
513
|
+
export function recoverWorkspaceCommit(root) {
|
|
514
|
+
const path = journalPath(join(root, WORKSPACE_TXN_DIR));
|
|
515
|
+
let raw;
|
|
516
|
+
try {
|
|
517
|
+
raw = readFileSync(path, "utf8");
|
|
518
|
+
}
|
|
519
|
+
catch (cause) {
|
|
520
|
+
if (cause.code === "ENOENT") {
|
|
521
|
+
return { ok: true, state: "none", message: `no workspace transaction journal at ${path}` };
|
|
522
|
+
}
|
|
523
|
+
return {
|
|
524
|
+
ok: false,
|
|
525
|
+
code: "journal-unreadable",
|
|
526
|
+
message: `workspace transaction journal ${path} could not be read: ${String(cause.code ?? cause)}`,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
let journal;
|
|
530
|
+
try {
|
|
531
|
+
const parsed = JSON.parse(raw);
|
|
532
|
+
if (typeof parsed !== "object" || parsed === null ||
|
|
533
|
+
parsed.version !== WORKSPACE_COMMIT_VERSION ||
|
|
534
|
+
!Array.isArray(parsed.operations)) {
|
|
535
|
+
return { ok: false, code: "journal-unreadable", message: `${path} is not a v1 workspace transaction journal` };
|
|
536
|
+
}
|
|
537
|
+
journal = parsed;
|
|
538
|
+
}
|
|
539
|
+
catch (cause) {
|
|
540
|
+
return {
|
|
541
|
+
ok: false,
|
|
542
|
+
code: "journal-unreadable",
|
|
543
|
+
message: `${path} is not parseable JSON: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
const inspection = inspectWorkspaceState(journal);
|
|
547
|
+
return { ok: true, state: inspection.state, inspection, journal };
|
|
548
|
+
}
|
|
549
|
+
//# sourceMappingURL=workspace-commit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-commit.js","sourceRoot":"","sources":["../../../src/codex/workspace-commit.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* (`tests/layering.test.ts`). One home, so the three cannot disagree about
|
|
10
10
|
* which records are an advance's own.
|
|
11
11
|
*/
|
|
12
|
+
import { ADVANCE_DAEMON_CLASS } from "./command-class.js";
|
|
12
13
|
import type { EventRecord } from "./log.js";
|
|
14
|
+
import type { PolicyLoadResult } from "./policy-load.js";
|
|
13
15
|
import { type RequestState } from "./state.js";
|
|
14
16
|
/**
|
|
15
17
|
* Who proposes an advance.
|
|
@@ -23,6 +25,55 @@ import { type RequestState } from "./state.js";
|
|
|
23
25
|
export declare const ADVANCE_ACTOR = "agent:daemon";
|
|
24
26
|
/** The class an advance is gated as. Declared, resolved, and never assumed. */
|
|
25
27
|
export declare const ADVANCE_CLASS = "log.advance";
|
|
28
|
+
export { ADVANCE_DAEMON_CLASS };
|
|
29
|
+
/** Every class an advance cycle may be gated as, newest first. */
|
|
30
|
+
export declare const ADVANCE_CLASSES: readonly string[];
|
|
31
|
+
/** The refusal a non-daemon actor gets where the route would be autonomous. */
|
|
32
|
+
export declare const ADVANCE_ACTOR_REFUSAL = "advance-actor-not-daemon";
|
|
33
|
+
/** Which class this cycle asks under, or the refusal that stops it. */
|
|
34
|
+
export type AdvanceRoute = {
|
|
35
|
+
ok: true;
|
|
36
|
+
cls: string;
|
|
37
|
+
daemon: boolean;
|
|
38
|
+
} | {
|
|
39
|
+
ok: false;
|
|
40
|
+
code: typeof ADVANCE_ACTOR_REFUSAL;
|
|
41
|
+
message: string;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* The class this advance asks under, decided by WHO is running it (APRV-382).
|
|
45
|
+
*
|
|
46
|
+
* Carter's 2026-09-19 question, answered as policy plus one check. An advance
|
|
47
|
+
* appends nothing and decides nothing: it publishes records the log already
|
|
48
|
+
* holds to a records branch, and the CI guard reads grants that exist whether
|
|
49
|
+
* or not an advance ran. So the DAEMON may make it unattended. Every other
|
|
50
|
+
* actor — a session in a worktree, a human terminal — stays where the class was
|
|
51
|
+
* declared at seq 513, because a lane that could publish the log could choose
|
|
52
|
+
* which records the world sees first.
|
|
53
|
+
*
|
|
54
|
+
* The policy grammar has no actor condition, so the rule is two classes and
|
|
55
|
+
* this function is the actor half of it. Three outcomes, and each one is the
|
|
56
|
+
* stricter reading of its case:
|
|
57
|
+
*
|
|
58
|
+
* 1. **The daemon, with a rule on {@link ADVANCE_DAEMON_CLASS}.** The daemon's
|
|
59
|
+
* own class, whatever autonomy the operator gave it.
|
|
60
|
+
* 2. **The daemon, with no such rule.** {@link ADVANCE_CLASS}, exactly as every
|
|
61
|
+
* build before this one. A policy that has not declared the daemon class has
|
|
62
|
+
* not decided anything about it, and resolving to the fail-closed default
|
|
63
|
+
* there would put a phone tap on every cadence advance between the day this
|
|
64
|
+
* code ships and the day the page is applied. Falling back to the class the
|
|
65
|
+
* operator DID write is the behaviour they attested to.
|
|
66
|
+
* 3. **Any other actor.** {@link ADVANCE_CLASS}, unless that class resolves
|
|
67
|
+
* `autonomous`, which is refused {@link ADVANCE_ACTOR_REFUSAL}. The
|
|
68
|
+
* autonomous route is the daemon's, so an actor that is not the daemon
|
|
69
|
+
* taking it is a policy that was loosened past what this repository decided,
|
|
70
|
+
* and the refusal says so rather than publishing the log unattended.
|
|
71
|
+
*
|
|
72
|
+
* PURE: the policy load is an argument and the actor answer is a boolean the
|
|
73
|
+
* caller read from `core/daemon-actor.ts`, so the whole rule is testable in
|
|
74
|
+
* both directions with no daemon, no git, and no clock.
|
|
75
|
+
*/
|
|
76
|
+
export declare function advanceRoute(daemon: boolean, load: PolicyLoadResult): AdvanceRoute;
|
|
26
77
|
/** The task id every advance cycle registers under, plus its head seq. */
|
|
27
78
|
export declare const ADVANCE_TASK_PREFIX = "daemon-advance";
|
|
28
79
|
/** The idempotency key prefix. One key per (published head → working head) span. */
|