@vela-science/canopus 0.6.4 → 0.7.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +26 -16
  3. package/SECURITY.md +3 -2
  4. package/capsules/erdos1056-k15/bin/linux-arm64/{10428601-10428800 → 10428801-10429000}/verifier +0 -0
  5. package/capsules/erdos1056-k15/bin/linux-x86_64/{10428601-10428800 → 10428801-10429000}/verifier +0 -0
  6. package/dist/src/activity/events.d.ts +4 -1
  7. package/dist/src/activity/events.js +8 -2
  8. package/dist/src/activity/store.d.ts +2 -2
  9. package/dist/src/activity/store.js +4 -3
  10. package/dist/src/cli.js +2 -37
  11. package/dist/src/product/run.js +0 -7
  12. package/dist/src/run.d.ts +0 -7
  13. package/dist/src/run.js +0 -15
  14. package/dist/src/vela/cli.d.ts +0 -9
  15. package/dist/src/vela/cli.js +0 -48
  16. package/missions/erdos1056-k15-next/mission.draft.json +6 -6
  17. package/package.json +3 -10
  18. package/profiles/{erdos1056-k15-10428601-10428800.json → erdos1056-k15-10428801-10429000.json} +9 -9
  19. package/BUILD_WEEK.md +0 -198
  20. package/advisories/erdos1056-claim-fidelity/output.schema.json +0 -90
  21. package/advisories/erdos1056-claim-fidelity/registration.json +0 -42
  22. package/advisories/erdos1056-claim-fidelity/results/assessment.json +0 -1
  23. package/advisories/erdos1056-claim-fidelity/results/verification.json +0 -1
  24. package/dist/src/capability/withdrawal.d.ts +0 -47
  25. package/dist/src/capability/withdrawal.js +0 -487
  26. package/dist/src/product/withdraw.d.ts +0 -8
  27. package/dist/src/product/withdraw.js +0 -255
  28. package/docs/RELEASES.md +0 -1326
  29. package/evidence/build-week/run_eb6bcd46-cffd-4ae8-b630-2681bd84da71.public.json +0 -1
  30. package/evidence/build-week/run_f68e4cfc-e5c7-4c73-86cb-d79807c47ec4.public.json +0 -1
  31. package/evidence/erdos/run_192b3bef-9d6e-49e5-b72d-7ae903b29d5e/pending-commands.json +0 -1
  32. package/evidence/erdos/run_192b3bef-9d6e-49e5-b72d-7ae903b29d5e/public-run.json +0 -1
  33. package/evidence/erdos/run_192b3bef-9d6e-49e5-b72d-7ae903b29d5e/root-manifest.json +0 -1
  34. package/evidence/erdos/run_192b3bef-9d6e-49e5-b72d-7ae903b29d5e/web-import.json +0 -1
  35. package/scripts/run-claim-fidelity-advisory.mjs +0 -235
@@ -1,255 +0,0 @@
1
- import os from "node:os";
2
- import path from "node:path";
3
- import { mkdir, mkdtemp, readdir, realpath, rm } from "node:fs/promises";
4
- import { consumeWithdrawalCapability, installWithdrawalCapabilitySecret, loadWithdrawalCapability, } from "../capability/withdrawal.js";
5
- import { parseRunRecord } from "../projection/run.js";
6
- import { canonicalJcs, contentDigest, protocolDigest, sha256Bytes, } from "../util/canonical.js";
7
- import { isolatedEnvironment, runCommand } from "../util/command.js";
8
- import { readBoundedRegularFile } from "../util/files.js";
9
- import { VelaClient } from "../vela/cli.js";
10
- async function gitText(runner, cwd, home, argv) {
11
- const result = await runner({
12
- argv: ["git", ...argv],
13
- cwd,
14
- env: isolatedEnvironment(home),
15
- timeoutMs: 120_000,
16
- maxOutputBytes: 8 * 1024 * 1024,
17
- });
18
- if (result.exitCode !== 0 || result.stderr.length !== 0) {
19
- throw new Error(`git ${argv.join(" ")} failed: stdout=${sha256Bytes(result.stdout)} stderr=${sha256Bytes(result.stderr)}`);
20
- }
21
- return result.stdout.toString("utf8").trim();
22
- }
23
- function safeActor(actor) {
24
- return [...actor].map((character) => /[A-Za-z0-9_-]/u.test(character) ? character : "-").join("");
25
- }
26
- async function scientificProjectionRoot(frontierRoot) {
27
- const project = JSON.parse((await readBoundedRegularFile(path.join(frontierRoot, "frontier.json"), 128 * 1024 * 1024))
28
- .toString("utf8"));
29
- const fields = [
30
- "actors",
31
- "anchor_links",
32
- "artifacts",
33
- "condition_records",
34
- "evidence_atoms",
35
- "findings",
36
- "frontier",
37
- "frontier_id",
38
- "released_diff_packs",
39
- "sources",
40
- "statement_attestations",
41
- "verifier_attachments",
42
- ];
43
- return contentDigest(Object.fromEntries(fields.map((field) => [field, project[field] ?? null])));
44
- }
45
- function jsonObject(value, at) {
46
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
47
- throw new Error(`${at} must be an object`);
48
- }
49
- return value;
50
- }
51
- function proposalStanding(value) {
52
- if (value !== "pending_review" &&
53
- value !== "accepted" &&
54
- value !== "applied" &&
55
- value !== "rejected" &&
56
- value !== "revision_requested" &&
57
- value !== "needs_revision" &&
58
- value !== "withdrawn") {
59
- throw new Error(`proposal has unrecognized standing ${JSON.stringify(value)}`);
60
- }
61
- return value;
62
- }
63
- async function frontierWithin(repo, frontier) {
64
- const resolved = await realpath(path.resolve(repo, frontier));
65
- if (resolved !== repo && !resolved.startsWith(`${repo}${path.sep}`)) {
66
- throw new Error("capability frontier resolves outside the selected source repository");
67
- }
68
- return resolved;
69
- }
70
- async function terminalProposalEvent(frontierRoot, proposalId) {
71
- const directory = path.join(frontierRoot, ".vela", "events");
72
- const entries = await readdir(directory, { withFileTypes: true });
73
- if (entries.length > 1_000_000)
74
- throw new Error("frontier event directory exceeds the supported audit bound");
75
- const matches = [];
76
- const decisionByKind = new Map([
77
- ["review.accepted", "accepted"],
78
- ["review.rejected", "rejected"],
79
- ["review.revision_requested", "revision_requested"],
80
- ["proposal.withdrawn", "withdrawn"],
81
- ]);
82
- for (const entry of entries) {
83
- if (!entry.isFile() || !/^vev_[0-9a-f]{16}\.json$/u.test(entry.name))
84
- continue;
85
- const event = jsonObject(JSON.parse((await readBoundedRegularFile(path.join(directory, entry.name), 8 * 1024 * 1024)).toString("utf8")), `event ${entry.name}`);
86
- const decision = typeof event.kind === "string" ? decisionByKind.get(event.kind) : undefined;
87
- if (decision === undefined)
88
- continue;
89
- const target = jsonObject(event.target, `event ${entry.name}.target`);
90
- if (target.type !== "proposal" || target.id !== proposalId)
91
- continue;
92
- const eventId = entry.name.slice(0, -".json".length);
93
- if (event.id !== eventId || typeof event.signature !== "string" || event.signature.length === 0) {
94
- throw new Error(`terminal proposal event ${entry.name} is not a signed canonical event file`);
95
- }
96
- const payload = jsonObject(event.payload, `event ${entry.name}.payload`);
97
- if (payload.proposal_id !== proposalId) {
98
- throw new Error(`terminal proposal event ${entry.name} payload targets another proposal`);
99
- }
100
- if (decision === "withdrawn") {
101
- if (payload.schema !== "vela.proposal-withdrawal.v1") {
102
- throw new Error(`withdrawal event ${entry.name} has the wrong payload schema`);
103
- }
104
- }
105
- else if (payload.verdict !== decision) {
106
- throw new Error(`review event ${entry.name} verdict disagrees with its kind`);
107
- }
108
- matches.push({ event_id: eventId, decision });
109
- }
110
- if (matches.length > 1)
111
- throw new Error("proposal has conflicting terminal events");
112
- return matches[0] ?? null;
113
- }
114
- function terminalDecisionMatchesStanding(decision, standing) {
115
- return decision === "accepted"
116
- ? standing === "accepted" || standing === "applied"
117
- : decision === "revision_requested"
118
- ? standing === "revision_requested" || standing === "needs_revision"
119
- : decision === standing;
120
- }
121
- export async function withdrawProduct(options) {
122
- if (options.reason.trim().length === 0)
123
- throw new Error("withdrawal reason must not be empty");
124
- const runner = options.runner ?? runCommand;
125
- const source = await realpath(options.frontier);
126
- const runFile = path.resolve(options.runFile);
127
- const record = parseRunRecord(JSON.parse((await readBoundedRegularFile(runFile, 8 * 1024 * 1024)).toString("utf8")));
128
- const capability = await loadWithdrawalCapability(record.landing.proposal_id, options.storeRoot);
129
- if (capability.manifest.state !== "available" || !capability.secret_available) {
130
- throw new Error("withdrawal capability is not available");
131
- }
132
- if (capability.manifest.receipt_root !== record.landing.receipt_root ||
133
- canonicalJcs(capability.manifest.final_roots) !== canonicalJcs(record.final_roots)) {
134
- throw new Error("withdrawal capability does not match the selected run");
135
- }
136
- const controlRoot = await mkdtemp(path.join(os.tmpdir(), "canopus-withdraw-"));
137
- const controlHome = path.join(controlRoot, "home");
138
- const clone = path.join(controlRoot, "candidate");
139
- const replay = path.join(controlRoot, "replay");
140
- await mkdir(path.join(controlHome, ".vela", "agents", safeActor(capability.manifest.actor)), {
141
- recursive: true,
142
- mode: 0o700,
143
- });
144
- try {
145
- const sourceHead = await gitText(runner, source, controlHome, ["rev-parse", "--verify", "HEAD^{commit}"]);
146
- const sourceStatus = await gitText(runner, source, controlHome, ["status", "--porcelain=v1", "--untracked-files=all"]);
147
- if (sourceStatus !== "")
148
- throw new Error("source frontier must be clean before withdrawal");
149
- const client = new VelaClient({
150
- binary: capability.manifest.vela.binary,
151
- expectedVersion: capability.manifest.vela.version,
152
- expectedSha256: capability.manifest.vela.sha256,
153
- home: controlHome,
154
- runner,
155
- });
156
- const strictBaseline = capability.manifest.strict_baseline;
157
- const sourceInspection = await client.inspect(source, capability.manifest.frontier, strictBaseline);
158
- const frontierRoot = await frontierWithin(source, capability.manifest.frontier);
159
- const proposalPath = path.join(frontierRoot, ".vela", "proposals", `${record.landing.proposal_id}.json`);
160
- const proposal = jsonObject(JSON.parse((await readBoundedRegularFile(proposalPath, 8 * 1024 * 1024)).toString("utf8")), "proposal");
161
- if (proposal.id !== record.landing.proposal_id)
162
- throw new Error("proposal file id disagrees with the run");
163
- const standing = proposalStanding(proposal.status);
164
- const terminalEvent = await terminalProposalEvent(frontierRoot, record.landing.proposal_id);
165
- const stableHead = await gitText(runner, source, controlHome, ["rev-parse", "--verify", "HEAD^{commit}"]);
166
- const stableStatus = await gitText(runner, source, controlHome, ["status", "--porcelain=v1", "--untracked-files=all"]);
167
- if (stableHead !== sourceHead || stableStatus !== "") {
168
- throw new Error("source frontier changed during proposal verification");
169
- }
170
- if (standing !== "pending_review") {
171
- if (terminalEvent === null || !terminalDecisionMatchesStanding(terminalEvent.decision, standing)) {
172
- throw new Error("terminal proposal standing is not backed by exactly one matching signed event");
173
- }
174
- await consumeWithdrawalCapability(record.landing.proposal_id, terminalEvent.decision === "withdrawn" ? "withdrawn" : "human_decision_observed", options.storeRoot);
175
- return {
176
- ok: true,
177
- command: "withdraw",
178
- proposal_id: record.landing.proposal_id,
179
- terminal_event_id: terminalEvent.event_id,
180
- state: terminalEvent.decision === "withdrawn" ? "already_withdrawn" : "human_decision_observed",
181
- frontier_mutated: false,
182
- capability_consumed: true,
183
- };
184
- }
185
- if (terminalEvent !== null)
186
- throw new Error("pending proposal already has a terminal event");
187
- if (protocolDigest(proposal) !== capability.manifest.proposal_root) {
188
- throw new Error("pending proposal bytes drifted from the retained withdrawal capability");
189
- }
190
- await gitText(runner, controlRoot, controlHome, ["clone", "--quiet", "--no-local", source, clone]);
191
- const before = await client.inspect(clone, capability.manifest.frontier, strictBaseline);
192
- if (canonicalJcs(before.roots) !== canonicalJcs(sourceInspection.roots)) {
193
- throw new Error("disposable withdrawal clone does not match the selected source head");
194
- }
195
- await installWithdrawalCapabilitySecret(record.landing.proposal_id, path.join(controlHome, ".vela", "agents", safeActor(capability.manifest.actor), "private.key"), options.storeRoot);
196
- const scientificRootBefore = await scientificProjectionRoot(path.resolve(clone, capability.manifest.frontier));
197
- const withdrawal = await client.withdraw(clone, capability.manifest.frontier, record.landing.proposal_id, capability.manifest.actor, options.reason);
198
- const after = await client.inspect(clone, capability.manifest.frontier, strictBaseline);
199
- const scientificRootAfter = await scientificProjectionRoot(path.resolve(clone, capability.manifest.frontier));
200
- if (after.roots.git_commit === before.roots.git_commit || after.roots.vela_event_log === before.roots.vela_event_log) {
201
- throw new Error("withdrawal did not append and publish one event");
202
- }
203
- if (withdrawal.publication_commit !== after.roots.git_commit ||
204
- withdrawal.state_root_before !== before.roots.vela_event_log ||
205
- withdrawal.state_root_after !== after.roots.vela_event_log) {
206
- throw new Error("withdrawal response does not bind the verified Git and event-log roots");
207
- }
208
- if (scientificRootAfter !== scientificRootBefore) {
209
- throw new Error("withdrawal changed the accepted scientific projection");
210
- }
211
- const withdrawnFrontier = await frontierWithin(await realpath(clone), capability.manifest.frontier);
212
- const withdrawnProposal = jsonObject(JSON.parse((await readBoundedRegularFile(path.join(withdrawnFrontier, ".vela", "proposals", `${record.landing.proposal_id}.json`), 8 * 1024 * 1024)).toString("utf8")), "withdrawn proposal");
213
- const withdrawnEvent = await terminalProposalEvent(withdrawnFrontier, record.landing.proposal_id);
214
- if (withdrawnProposal.status !== "withdrawn" ||
215
- withdrawnEvent?.decision !== "withdrawn" ||
216
- withdrawnEvent.event_id !== withdrawal.withdrawal_event_id) {
217
- throw new Error("withdrawal output is not backed by the exact materialized terminal event");
218
- }
219
- await gitText(runner, controlRoot, controlHome, ["clone", "--quiet", "--no-local", clone, replay]);
220
- const reproduced = await client.inspect(replay, capability.manifest.frontier, strictBaseline);
221
- if (canonicalJcs(reproduced.roots) !== canonicalJcs(after.roots)) {
222
- throw new Error("clean-clone withdrawal replay drifted");
223
- }
224
- const unchangedHead = await gitText(runner, source, controlHome, ["rev-parse", "--verify", "HEAD^{commit}"]);
225
- const unchangedStatus = await gitText(runner, source, controlHome, ["status", "--porcelain=v1", "--untracked-files=all"]);
226
- if (unchangedHead !== sourceHead || unchangedStatus !== "") {
227
- throw new Error("source frontier changed during withdrawal");
228
- }
229
- await gitText(runner, source, controlHome, ["fetch", "--quiet", "--no-tags", clone, "HEAD"]);
230
- await gitText(runner, source, controlHome, ["merge", "--ff-only", "--no-edit", "FETCH_HEAD"]);
231
- const installed = await client.inspect(source, capability.manifest.frontier, strictBaseline);
232
- if (canonicalJcs(installed.roots) !== canonicalJcs(after.roots)) {
233
- throw new Error("fast-forwarded source does not match the verified withdrawal clone");
234
- }
235
- const installedStatus = await gitText(runner, source, controlHome, ["status", "--porcelain=v1", "--untracked-files=all"]);
236
- if (installedStatus !== "")
237
- throw new Error("fast-forwarded source is not clean");
238
- await consumeWithdrawalCapability(record.landing.proposal_id, "withdrawn", options.storeRoot);
239
- return {
240
- ok: true,
241
- command: "withdraw",
242
- proposal_id: record.landing.proposal_id,
243
- withdrawal_event_id: withdrawal.withdrawal_event_id,
244
- roots: after.roots,
245
- scientific_state_root_before: scientificRootBefore,
246
- scientific_state_root_after: scientificRootAfter,
247
- accepted_state_changed: false,
248
- clean_clone_reproduced: true,
249
- capability_consumed: true,
250
- };
251
- }
252
- finally {
253
- await rm(controlRoot, { recursive: true, force: true });
254
- }
255
- }