knodin 0.7.6 → 0.8.3

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 (134) hide show
  1. package/README.md +41 -12
  2. package/benchmarks/competitors/SYNTHESIS.md +66 -0
  3. package/dist/bin/cli.js +2181 -108
  4. package/dist/bin/launcher.js +25 -3
  5. package/dist/src/agent-integration.js +304 -0
  6. package/dist/src/artifact-refresh.js +82 -0
  7. package/dist/src/cli-args.js +292 -0
  8. package/dist/src/cli-model.js +384 -0
  9. package/dist/src/codeflow-replay.js +81 -0
  10. package/dist/src/compact-structural.js +96 -0
  11. package/dist/src/compare.js +39 -0
  12. package/dist/src/competitive-cold-mcp.js +40 -0
  13. package/dist/src/competitive-constraints.js +21 -0
  14. package/dist/src/competitive-manifest.js +411 -0
  15. package/dist/src/competitive-measurement.js +183 -0
  16. package/dist/src/competitive-runner.js +487 -0
  17. package/dist/src/competitive-sandbox.js +108 -0
  18. package/dist/src/context-export.js +423 -0
  19. package/dist/src/context.js +102 -0
  20. package/dist/src/deterministic-random.js +34 -0
  21. package/dist/src/diagnostics-write-helper.js +473 -0
  22. package/dist/src/diagnostics.js +1476 -0
  23. package/dist/src/docs-sections.js +142 -0
  24. package/dist/src/doctor.js +382 -0
  25. package/dist/src/engine/ann-hnsw.js +261 -0
  26. package/dist/src/engine/embeddings.js +193 -0
  27. package/dist/src/engine/file-walker.js +49 -0
  28. package/dist/src/engine/git-history.js +289 -0
  29. package/dist/src/engine/index.js +15094 -0
  30. package/dist/src/engine/perf.js +115 -0
  31. package/dist/src/engine/prune.js +112 -0
  32. package/dist/src/engine/sarif-import.js +341 -0
  33. package/dist/src/engine/scip-import.js +423 -0
  34. package/dist/src/engine/source-policy.js +85 -0
  35. package/dist/src/engine/sqlite.js +71 -0
  36. package/dist/src/engine/state-paths.js +175 -0
  37. package/dist/src/engine/symbol-delete.js +58 -0
  38. package/dist/src/execution-profile.js +208 -0
  39. package/dist/src/failure-diagnosis.js +655 -0
  40. package/dist/src/fleet.js +7 -0
  41. package/dist/src/git-executable.js +31 -0
  42. package/dist/src/graph-layout.js +173 -0
  43. package/dist/src/graph-query-health.js +115 -0
  44. package/dist/src/hook-manager-integration.js +156 -0
  45. package/dist/src/index-activity.js +126 -0
  46. package/dist/src/init-progress-worker.js +70 -2
  47. package/dist/src/init-progress.js +155 -0
  48. package/dist/src/init.js +1295 -0
  49. package/dist/src/lifecycle-health.js +282 -0
  50. package/dist/src/lsp-readonly.js +217 -0
  51. package/dist/src/mcp-graph-worker.js +69 -0
  52. package/dist/src/mcp-reliability.js +154 -0
  53. package/dist/src/mcp-worker-supervisor.js +350 -0
  54. package/dist/src/mirror.js +290 -0
  55. package/dist/src/node-runtime.js +157 -0
  56. package/dist/src/output-compression.js +630 -0
  57. package/dist/src/output-telemetry.js +368 -0
  58. package/dist/src/pr-triage.js +638 -0
  59. package/dist/src/progress-worker-runtime.js +46 -0
  60. package/dist/src/progressive-evidence.js +477 -0
  61. package/dist/src/pure-compression-cli.js +102 -0
  62. package/dist/src/relationship-adapters.js +377 -0
  63. package/dist/src/release-attestation.js +533 -0
  64. package/dist/src/release-preflight.js +513 -0
  65. package/dist/src/repair-lease.js +85 -0
  66. package/dist/src/repair-progress-worker.js +83 -2
  67. package/dist/src/repair-progress.js +262 -0
  68. package/dist/src/repository-init-process.js +177 -0
  69. package/dist/src/repository-management.js +1261 -0
  70. package/dist/src/resource-reachability.js +456 -0
  71. package/dist/src/response-budget.js +200 -0
  72. package/dist/src/server.js +217 -0
  73. package/dist/src/structural-fast-path.js +344 -0
  74. package/dist/src/structural-snapshot.js +37 -0
  75. package/dist/src/system-config.js +638 -0
  76. package/dist/src/terminal-help.js +83 -0
  77. package/dist/src/tools/knodin-tools.js +1645 -0
  78. package/dist/src/update-ceremony.js +162 -0
  79. package/dist/src/update-policy.js +944 -0
  80. package/dist/src/update-trust.js +504 -0
  81. package/dist/src/version.js +13 -0
  82. package/dist/src/visualization.js +515 -0
  83. package/dist/src/wait-for-fresh.js +98 -0
  84. package/dist/src/worktree-lifecycle.js +234 -0
  85. package/docs/BEHAVIORAL-CONTRACT.md +114 -0
  86. package/docs/CLI.md +30 -1
  87. package/docs/COMPARISON.md +413 -0
  88. package/docs/COMPETITIVE-LANDSCAPE-2026-08.md +267 -0
  89. package/docs/CONTAINED-EXECUTION.md +77 -0
  90. package/docs/DEMO.md +49 -0
  91. package/docs/DIAGNOSTICS.md +80 -0
  92. package/docs/GIT-HISTORY-REVIEW.md +39 -0
  93. package/docs/HANDOFF.md +180 -0
  94. package/docs/INSTALLATION.md +21 -18
  95. package/docs/MCP.md +64 -8
  96. package/docs/PROGRESSIVE-EVIDENCE.md +37 -0
  97. package/docs/PT-ACCESS-RECOMMENDATION.md +89 -0
  98. package/docs/RELEASE-0.3-EVIDENCE.md +73 -0
  99. package/docs/REPOSITORIES-AND-WORKTREES.md +18 -6
  100. package/docs/SCIP-IMPORT.md +62 -0
  101. package/docs/SIGNED-UPDATES.md +151 -0
  102. package/docs/TELEMETRY.md +46 -0
  103. package/docs/TOKEN-OPTIMIZER-SCORECARD.md +79 -0
  104. package/docs/assets/knodin-favicon.svg +4 -0
  105. package/docs/releases/0.3.0.md +46 -0
  106. package/docs/releases/0.4.0.md +68 -0
  107. package/docs/releases/0.4.1.md +28 -0
  108. package/docs/releases/0.4.2.md +27 -0
  109. package/docs/releases/0.4.3.md +23 -0
  110. package/docs/releases/0.5.0.md +29 -0
  111. package/docs/releases/0.5.1.md +17 -0
  112. package/docs/releases/0.6.0.md +18 -0
  113. package/docs/releases/0.7.0.md +24 -0
  114. package/docs/releases/0.7.1.md +21 -0
  115. package/docs/releases/0.7.2.md +21 -0
  116. package/docs/releases/0.7.3.md +23 -0
  117. package/docs/releases/0.7.4.md +17 -0
  118. package/docs/releases/0.7.5.md +20 -0
  119. package/docs/releases/0.8.0.md +74 -0
  120. package/docs/releases/0.8.2.md +34 -0
  121. package/docs/releases/0.8.3.md +47 -0
  122. package/package.json +139 -4
  123. package/roadmap/competitive-roadmap.md +3896 -0
  124. package/schemas/release-attestation-v1.schema.json +210 -0
  125. package/schemas/support-bundle-v2.schema.json +212 -0
  126. package/dist/chunks/chunk-DMQAGX77.js +0 -654
  127. package/dist/chunks/chunk-F4Z3Z766.js +0 -4
  128. package/dist/chunks/chunk-SIJAQVSX.js +0 -3
  129. package/dist/chunks/chunk-X6M4HUUE.js +0 -2
  130. package/dist/chunks/chunk-YPRMY2LP.js +0 -8
  131. package/dist/chunks/pure-compression-cli-4TA2TQD5.js +0 -5
  132. package/dist/chunks/server-7EDF4CBY.js +0 -14
  133. package/dist/chunks/structural-fast-path-KD5KQSPX.js +0 -4
  134. package/docs/releases/0.7.6.md +0 -25
@@ -0,0 +1,513 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { createHash, createPublicKey } from "node:crypto";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { compareBytes } from "./compare.js";
6
+ import { validateRootCeremonyManifest } from "./update-ceremony.js";
7
+ import { canonicalizeUpdateMetadata, verifyUpdateRootChain, } from "./update-trust.js";
8
+ export const RELEASE_PREFLIGHT_DEPENDENCIES = [
9
+ "C60",
10
+ "C62",
11
+ "C63",
12
+ "C64",
13
+ "C65",
14
+ "C66",
15
+ "C75",
16
+ ];
17
+ export const RELEASE_PREFLIGHT_REPOSITORY_COMMANDS = [
18
+ "npm ci --ignore-scripts",
19
+ "npm run lint",
20
+ "npm run typecheck",
21
+ "npm run build",
22
+ "npm run test",
23
+ "npm run test:pack-install",
24
+ "npm run check:roadmap",
25
+ "npm run verify:c95",
26
+ ];
27
+ export const RELEASE_PREFLIGHT_REPLAY_COMMANDS = {
28
+ competitive: [
29
+ "npm run audit:token-optimizer",
30
+ 'REPLAY_OUTPUT="$(mktemp -t knodin-c67-token-optimizer.XXXXXX.json)" && npm run bench:token-optimizer -- "$REPLAY_OUTPUT"',
31
+ ],
32
+ trust: [
33
+ "npx vitest run src/__tests__/unit/update-trust.spec.ts src/__tests__/unit/update-ceremony.spec.ts",
34
+ ],
35
+ defense: [
36
+ "npx vitest run src/__tests__/unit/update-policy.spec.ts src/__tests__/unit/update-policy-cli.spec.ts",
37
+ ],
38
+ attestation: [
39
+ "npx vitest run src/__tests__/unit/release-attestation.spec.ts src/__tests__/unit/release-attestation-cli.spec.ts src/__tests__/unit/release-workflow.spec.ts src/__tests__/unit/release-candidate-workflow.spec.ts",
40
+ ],
41
+ };
42
+ const COMMAND_CATEGORIES = [
43
+ "repository",
44
+ "competitive",
45
+ "trust",
46
+ "defense",
47
+ "attestation",
48
+ ];
49
+ const TARGET_PLATFORMS = ["macOS", "Linux", "Windows"];
50
+ const MAX_MANIFEST_BYTES = 1 * 1_024 * 1_024;
51
+ const MAX_EVIDENCE_BYTES = 16 * 1_024 * 1_024;
52
+ const MAX_TOTAL_EVIDENCE_BYTES = 128 * 1_024 * 1_024;
53
+ const COMMIT = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/;
54
+ const SHA256 = /^[a-f0-9]{64}$/;
55
+ const SEMVER = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|[A-Za-z-][0-9A-Za-z-]*))*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
56
+ const REPOSITORY_PATH = /^[A-Za-z0-9._+/-]+$/;
57
+ const IMMUTABLE_EVIDENCE_PATH = /^(?:docs\/evidence|benchmarks\/evaluations)\//;
58
+ const RUNTIME_NAME = /^[a-z][a-z0-9._-]{0,31}$/;
59
+ const RUNTIME_VERSION = /^[0-9A-Za-z][0-9A-Za-z._+-]{0,63}$/;
60
+ const TERMINAL_STATUS = /^(?:implemented(?: \([^()\n]+\)| — [^\n]+)?|evaluated — [^\n]+|certified(?: with [^\n]+| — [^\n]+)?|closed(?:; [^\n]+| — [^\n]+)?|done(?: — [^\n]+)?)$/;
61
+ function fail(message) {
62
+ throw new Error(`release preflight failed: ${message}`);
63
+ }
64
+ function record(value, label) {
65
+ if (value === null || typeof value !== "object" || Array.isArray(value))
66
+ fail(`${label} must be an object`);
67
+ return value;
68
+ }
69
+ function exactKeys(value, expected, label) {
70
+ const keys = Object.keys(value).sort(compareBytes);
71
+ const wanted = [...expected].sort(compareBytes);
72
+ if (keys.length !== wanted.length || keys.some((key, index) => key !== wanted[index]))
73
+ fail(`${label} must contain exactly: ${wanted.join(", ")}`);
74
+ }
75
+ function text(value, label, maximum) {
76
+ if (typeof value !== "string" || value.length === 0 || value.length > maximum)
77
+ fail(`${label} must be a non-empty bounded string`);
78
+ return value;
79
+ }
80
+ function safeRelativePath(value, label) {
81
+ const candidate = text(value, label, 512);
82
+ if (path.isAbsolute(candidate) ||
83
+ candidate.includes("\\") ||
84
+ candidate.includes("\0") ||
85
+ !REPOSITORY_PATH.test(candidate) ||
86
+ candidate.split("/").some((part) => part === "" || part === "." || part === ".."))
87
+ fail(`${label} must be a normalized repository-relative path`);
88
+ return candidate;
89
+ }
90
+ function boundedStringArray(value, label, options) {
91
+ if (!Array.isArray(value) || value.length === 0 || value.length > options.maximumItems)
92
+ fail(`${label} must be a non-empty bounded array`);
93
+ const result = value.map((item, index) => text(item, `${label}[${index}]`, options.maximumLength));
94
+ if (new Set(result).size !== result.length)
95
+ fail(`${label} must not contain duplicates`);
96
+ return result;
97
+ }
98
+ function parsePlan(value) {
99
+ const plan = record(value, "release plan");
100
+ exactKeys(plan, [
101
+ "schemaVersion",
102
+ "version",
103
+ "tag",
104
+ "sourceCommit",
105
+ "workflow",
106
+ "runtimes",
107
+ "certifiedTargetPlatforms",
108
+ "trustRootSha256",
109
+ "trustRootPath",
110
+ "trustCeremonyManifestPath",
111
+ "trustReviewReceiptPath",
112
+ "verificationCommands",
113
+ "dependencyEvidence",
114
+ "evidenceDirectory",
115
+ ], "release plan");
116
+ if (plan.schemaVersion !== 1)
117
+ fail("schemaVersion must be 1");
118
+ const version = text(plan.version, "version", 128);
119
+ if (!SEMVER.test(version))
120
+ fail("version must be semantic");
121
+ const tag = text(plan.tag, "tag", 129);
122
+ if (tag !== `v${version}`)
123
+ fail("tag must equal v followed by package version");
124
+ const sourceCommit = text(plan.sourceCommit, "source commit", 64);
125
+ if (!COMMIT.test(sourceCommit))
126
+ fail("source commit must be a lowercase full commit digest");
127
+ const workflow = record(plan.workflow, "workflow");
128
+ exactKeys(workflow, ["path", "commit"], "workflow");
129
+ const workflowPath = safeRelativePath(workflow.path, "workflow path");
130
+ if (workflowPath !== ".github/workflows/release-candidate.yml")
131
+ fail("workflow path must select the C64 nonpublishing candidate workflow");
132
+ const workflowCommit = text(workflow.commit, "workflow commit", 64);
133
+ if (!COMMIT.test(workflowCommit))
134
+ fail("workflow commit must be a lowercase full commit digest");
135
+ if (workflowCommit !== sourceCommit)
136
+ fail("workflow commit must equal source commit");
137
+ if (!Array.isArray(plan.runtimes) || plan.runtimes.length < 2 || plan.runtimes.length > 16)
138
+ fail("runtimes must contain a bounded runtime set including node and npm");
139
+ const runtimes = plan.runtimes.map((candidate, index) => {
140
+ const runtime = record(candidate, `runtimes[${index}]`);
141
+ exactKeys(runtime, ["name", "version"], `runtimes[${index}]`);
142
+ const name = text(runtime.name, `runtimes[${index}].name`, 32);
143
+ const runtimeVersion = text(runtime.version, `runtimes[${index}].version`, 64);
144
+ if (!RUNTIME_NAME.test(name) || !RUNTIME_VERSION.test(runtimeVersion))
145
+ fail(`runtimes[${index}] has an invalid name or version`);
146
+ return { name, version: runtimeVersion };
147
+ });
148
+ const runtimeNames = runtimes.map(({ name }) => name);
149
+ if (new Set(runtimeNames).size !== runtimeNames.length ||
150
+ !runtimeNames.includes("node") ||
151
+ !runtimeNames.includes("npm"))
152
+ fail("runtimes must uniquely include node and npm");
153
+ const certifiedTargetPlatforms = boundedStringArray(plan.certifiedTargetPlatforms, "certified target platforms", { maximumItems: 3, maximumLength: 16 });
154
+ if (certifiedTargetPlatforms.length !== TARGET_PLATFORMS.length ||
155
+ TARGET_PLATFORMS.some((platform) => !certifiedTargetPlatforms.includes(platform)))
156
+ fail("certified target platforms must contain exactly macOS, Linux, and Windows");
157
+ const trustRootSha256 = text(plan.trustRootSha256, "trust-root digest", 64);
158
+ if (!SHA256.test(trustRootSha256))
159
+ fail("trust-root digest must be a lowercase SHA-256 digest");
160
+ const trustRootPath = safeRelativePath(plan.trustRootPath, "trust-root path");
161
+ const trustCeremonyManifestPath = safeRelativePath(plan.trustCeremonyManifestPath, "trust ceremony manifest path");
162
+ const trustReviewReceiptPath = safeRelativePath(plan.trustReviewReceiptPath, "trust review receipt path");
163
+ const commandsRecord = record(plan.verificationCommands, "verification commands");
164
+ exactKeys(commandsRecord, COMMAND_CATEGORIES, "verification commands");
165
+ const verificationCommands = {};
166
+ for (const category of COMMAND_CATEGORIES) {
167
+ verificationCommands[category] = boundedStringArray(commandsRecord[category], `${category} verification commands`, { maximumItems: 32, maximumLength: 512 });
168
+ for (const command of verificationCommands[category]) {
169
+ if (/[^\x20-\x7e]/.test(command))
170
+ fail(`${category} verification commands must be exact single-line printable commands`);
171
+ }
172
+ }
173
+ if (verificationCommands.repository.length !== RELEASE_PREFLIGHT_REPOSITORY_COMMANDS.length ||
174
+ RELEASE_PREFLIGHT_REPOSITORY_COMMANDS.some((command, index) => verificationCommands.repository[index] !== command))
175
+ fail("repository verification commands must equal the canonical ordered gate set");
176
+ for (const category of ["competitive", "trust", "defense", "attestation"]) {
177
+ const expected = RELEASE_PREFLIGHT_REPLAY_COMMANDS[category];
178
+ if (verificationCommands[category].length !== expected.length ||
179
+ expected.some((command, index) => verificationCommands[category][index] !== command))
180
+ fail(`${category} verification commands must equal the canonical ordered replay set`);
181
+ }
182
+ const evidenceRecord = record(plan.dependencyEvidence, "dependency evidence");
183
+ exactKeys(evidenceRecord, RELEASE_PREFLIGHT_DEPENDENCIES, "dependency evidence");
184
+ const dependencyEvidence = {};
185
+ for (const dependency of RELEASE_PREFLIGHT_DEPENDENCIES) {
186
+ dependencyEvidence[dependency] = boundedStringArray(evidenceRecord[dependency], `${dependency} evidence paths`, { maximumItems: 32, maximumLength: 512 }).map((candidate, index) => safeRelativePath(candidate, `${dependency} evidence paths[${index}]`));
187
+ }
188
+ return {
189
+ schemaVersion: 1,
190
+ version,
191
+ tag,
192
+ sourceCommit,
193
+ workflow: { path: workflowPath, commit: workflowCommit },
194
+ runtimes,
195
+ certifiedTargetPlatforms,
196
+ trustRootSha256,
197
+ trustRootPath,
198
+ trustCeremonyManifestPath,
199
+ trustReviewReceiptPath,
200
+ verificationCommands,
201
+ dependencyEvidence,
202
+ evidenceDirectory: safeRelativePath(plan.evidenceDirectory, "evidence directory"),
203
+ };
204
+ }
205
+ function readBoundedRegularFile(repositoryRoot, file, label, maximum) {
206
+ const lexical = path.resolve(file);
207
+ let stat;
208
+ try {
209
+ stat = fs.lstatSync(lexical);
210
+ }
211
+ catch {
212
+ return fail(`${label} does not exist`);
213
+ }
214
+ if (!stat.isFile() || stat.isSymbolicLink())
215
+ fail(`${label} must be a regular non-symlink file`);
216
+ let canonical;
217
+ try {
218
+ canonical = fs.realpathSync(lexical);
219
+ }
220
+ catch {
221
+ return fail(`${label} cannot be resolved`);
222
+ }
223
+ if (canonical === repositoryRoot ||
224
+ !canonical.startsWith(`${repositoryRoot}${path.sep}`) ||
225
+ (label !== "release-plan manifest" && canonical !== lexical))
226
+ fail(`${label} must not traverse a symlink or leave the repository`);
227
+ const noFollow = "O_NOFOLLOW" in fs.constants ? fs.constants.O_NOFOLLOW : 0;
228
+ let descriptor;
229
+ try {
230
+ descriptor = fs.openSync(lexical, fs.constants.O_RDONLY | noFollow);
231
+ const opened = fs.fstatSync(descriptor);
232
+ if (!opened.isFile() || opened.dev !== stat.dev || opened.ino !== stat.ino)
233
+ fail(`${label} changed while it was being validated`);
234
+ if (opened.size === 0 || opened.size > maximum)
235
+ fail(`${label} exceeds its non-empty byte budget`);
236
+ const bounded = Buffer.allocUnsafe(maximum + 1);
237
+ let offset = 0;
238
+ while (offset < bounded.byteLength) {
239
+ const count = fs.readSync(descriptor, bounded, offset, bounded.byteLength - offset, null);
240
+ if (count === 0)
241
+ break;
242
+ offset += count;
243
+ }
244
+ if (offset > maximum)
245
+ fail(`${label} exceeds its non-empty byte budget`);
246
+ const after = fs.fstatSync(descriptor);
247
+ if (after.dev !== opened.dev || after.ino !== opened.ino || after.size !== offset)
248
+ fail(`${label} changed while it was being read`);
249
+ const bytes = Buffer.from(bounded.subarray(0, offset));
250
+ return {
251
+ bytes,
252
+ relativePath: path.relative(repositoryRoot, canonical).split(path.sep).join("/"),
253
+ size: opened.size,
254
+ };
255
+ }
256
+ finally {
257
+ if (descriptor !== undefined)
258
+ fs.closeSync(descriptor);
259
+ }
260
+ }
261
+ function parseJson(bytes, label) {
262
+ try {
263
+ return JSON.parse(bytes.toString("utf8"));
264
+ }
265
+ catch {
266
+ return fail(`${label} must be valid JSON`);
267
+ }
268
+ }
269
+ function defaultGit(repositoryRoot) {
270
+ const run = (args, allowFailure = false) => {
271
+ const result = spawnSync("git", args, {
272
+ cwd: repositoryRoot,
273
+ encoding: "utf8",
274
+ timeout: 10_000,
275
+ maxBuffer: 1 * 1_024 * 1_024,
276
+ env: {
277
+ PATH: process.env.PATH,
278
+ SYSTEMROOT: process.env.SYSTEMROOT,
279
+ WINDIR: process.env.WINDIR,
280
+ },
281
+ });
282
+ if (result.error)
283
+ throw result.error;
284
+ if (!allowFailure && result.status !== 0)
285
+ fail(`git ${args[0] ?? "command"} failed without mutating the checkout`);
286
+ return { status: result.status, stdout: result.stdout.trim() };
287
+ };
288
+ const runBytes = (args) => {
289
+ const result = spawnSync("git", args, {
290
+ cwd: repositoryRoot,
291
+ timeout: 10_000,
292
+ maxBuffer: MAX_EVIDENCE_BYTES + 1,
293
+ env: {
294
+ PATH: process.env.PATH,
295
+ SYSTEMROOT: process.env.SYSTEMROOT,
296
+ WINDIR: process.env.WINDIR,
297
+ },
298
+ });
299
+ if (result.error)
300
+ throw result.error;
301
+ if (result.status !== 0 || !Buffer.isBuffer(result.stdout))
302
+ fail(`git ${args[0] ?? "command"} could not read a source-bound blob`);
303
+ if (result.stdout.byteLength === 0 || result.stdout.byteLength > MAX_EVIDENCE_BYTES)
304
+ fail("source-bound Git blob exceeds its non-empty byte budget");
305
+ return result.stdout;
306
+ };
307
+ return {
308
+ headCommit: () => run(["rev-parse", "HEAD"]).stdout,
309
+ statusPorcelain: () => run(["status", "--porcelain=v1", "--untracked-files=all"]).stdout,
310
+ tagObjectType: (tag) => run(["cat-file", "-t", `refs/tags/${tag}`]).stdout,
311
+ tagCommit: (tag) => run(["rev-parse", `${tag}^{commit}`]).stdout,
312
+ isTracked: (relativePath) => run(["ls-files", "--error-unmatch", "--", relativePath], true).status === 0,
313
+ fileAtCommit: (commit, relativePath) => runBytes(["show", `${commit}:${relativePath}`]),
314
+ };
315
+ }
316
+ function requireTaggedBytes(git, commit, relativePath, bytes, label) {
317
+ const tagged = git.fileAtCommit(commit, relativePath);
318
+ if (!tagged.equals(bytes))
319
+ fail(`${label} bytes do not equal the exact source-commit blob`);
320
+ }
321
+ function verifyRoadmap(roadmap) {
322
+ const activeLedger = /### Track A — Active local-work ledger\n[\s\S]*?\n\| ID \| State \| Previous state \| Depends on \| Exit evidence \|\n\|---\|---\|---\|---\|---\|\n((?:\| C[^\n]+\n)*)\n### Track B — Parked external-evidence ledger/.exec(roadmap);
323
+ const parkedLedger = /### Track B — Parked external-evidence ledger\n[\s\S]*?\n\| ID \| State \| Previous state \| Depends on \| Exit evidence \|\n\|---\|---\|---\|---\|---\|\n((?:\| C[^\n]+\n)*)\n### Goal-loop ordering/.exec(roadmap);
324
+ if (!activeLedger || !parkedLedger)
325
+ fail("two-track roadmap ledger is missing or malformed");
326
+ const activeIds = new Set([...activeLedger[1].matchAll(/^\| (C\d+) \|/gm)].map((match) => match[1]));
327
+ const parkedIds = new Set([...parkedLedger[1].matchAll(/^\| (C\d+) \|/gm)].map((match) => match[1]));
328
+ if (parkedIds.size > 0)
329
+ fail(`Track B remains parked (${parkedIds.size} external item${parkedIds.size === 1 ? "" : "s"}: ${[...parkedIds].join(", ")})`);
330
+ const evidencePaths = new Map();
331
+ for (const dependency of RELEASE_PREFLIGHT_DEPENDENCIES) {
332
+ if (activeIds.has(dependency))
333
+ fail(`${dependency} remains in Track A`);
334
+ const header = dependency === "C75" ? "C69–C75" : dependency;
335
+ const section = new RegExp(String.raw `(^#{2,3} ${header} —[^\n]*\n[\s\S]*?)(?=^#{2,3} C(?:\d+|\d+–C\d+) —|(?![\s\S]))`, "m").exec(roadmap);
336
+ if (!section)
337
+ fail(`${dependency} has no authoritative item status`);
338
+ const statusMatch = /^- Status: ([^\n]+)$/m.exec(section[1]);
339
+ if (!statusMatch)
340
+ fail(`${dependency} has no authoritative item status`);
341
+ const status = statusMatch[1].trim();
342
+ if (!TERMINAL_STATUS.test(status))
343
+ fail(`${dependency} has nonterminal status ${JSON.stringify(status)}`);
344
+ const evidenceLabel = dependency === "C75" ? "C75 Evidence" : "Evidence";
345
+ const evidenceField = new RegExp(String.raw `^- ${evidenceLabel}: ([\s\S]*?)(?=\n\n|\n- [A-Z][^\n]*:|(?![\s\S]))`, "m").exec(section[1]);
346
+ if (!evidenceField)
347
+ fail(`${dependency} has no explicit authoritative evidence field`);
348
+ const paths = [...evidenceField[1].matchAll(/`([^`]+)`/g)].map((match) => match[1]);
349
+ if (paths.length === 0 || new Set(paths).size !== paths.length)
350
+ fail(`${dependency} authoritative evidence field must name distinct paths`);
351
+ if (!paths.some((candidate) => IMMUTABLE_EVIDENCE_PATH.test(candidate)))
352
+ fail(`${dependency} must declare at least one immutable evidence or evaluation path`);
353
+ evidencePaths.set(dependency, paths);
354
+ }
355
+ return evidencePaths;
356
+ }
357
+ function verifyFreshEvidenceDirectory(repositoryRoot, plan) {
358
+ const expected = `docs/evidence/releases/${plan.version}`;
359
+ if (plan.evidenceDirectory !== expected)
360
+ fail(`evidence directory must be the version-bound path ${expected}`);
361
+ const parts = plan.evidenceDirectory.split("/");
362
+ let candidate = repositoryRoot;
363
+ for (const part of parts) {
364
+ candidate = path.join(candidate, part);
365
+ let stat;
366
+ try {
367
+ stat = fs.lstatSync(candidate);
368
+ }
369
+ catch (error) {
370
+ if (error.code === "ENOENT")
371
+ continue;
372
+ throw error;
373
+ }
374
+ if (stat.isSymbolicLink())
375
+ fail("evidence directory must not traverse a symlink");
376
+ if (candidate !== path.join(repositoryRoot, plan.evidenceDirectory) && !stat.isDirectory())
377
+ fail("every existing evidence-directory prefix must be a directory");
378
+ if (candidate === path.join(repositoryRoot, plan.evidenceDirectory))
379
+ fail("evidence directory already exists and must not be overwritten");
380
+ }
381
+ }
382
+ function verifyProductionTrust(plan, evidence) {
383
+ const paths = [plan.trustRootPath, plan.trustCeremonyManifestPath, plan.trustReviewReceiptPath];
384
+ if (new Set(paths).size !== paths.length ||
385
+ paths.some((candidate) => !plan.dependencyEvidence.C62.includes(candidate)))
386
+ fail("production trust artifacts must be three distinct C62 dependency-evidence paths");
387
+ const rootBytes = evidence.get(plan.trustRootPath);
388
+ const manifestBytes = evidence.get(plan.trustCeremonyManifestPath);
389
+ const receiptBytes = evidence.get(plan.trustReviewReceiptPath);
390
+ if (!rootBytes || !manifestBytes || !receiptBytes)
391
+ fail("production trust artifacts were not retained in the validated evidence set");
392
+ const manifest = validateRootCeremonyManifest(parseJson(manifestBytes, "production root ceremony manifest"));
393
+ if (manifest.purpose !== "production")
394
+ fail("trust ceremony manifest must record a production ceremony");
395
+ const rootEnvelope = parseJson(rootBytes, "production root envelope");
396
+ let digest;
397
+ try {
398
+ for (const role of ["root", "targets", "snapshot", "timestamp"]) {
399
+ const rootRole = rootEnvelope.signed.roles[role];
400
+ if (rootRole.threshold !== manifest.thresholds[role] ||
401
+ [...rootRole.keyids].sort(compareBytes).join("\0") !==
402
+ [...manifest.roleKeyIds[role]].sort(compareBytes).join("\0"))
403
+ fail(`production ceremony ${role} role does not match the signed root`);
404
+ }
405
+ for (const key of Object.values(rootEnvelope.signed.keys)) {
406
+ const canonical = createPublicKey(key.keyval.public)
407
+ .export({ type: "spki", format: "pem" })
408
+ .toString();
409
+ if (canonical !== key.keyval.public)
410
+ fail("production root keys must use canonical public SPKI PEM only");
411
+ }
412
+ digest = createHash("sha256").update(canonicalizeUpdateMetadata(rootEnvelope)).digest("hex");
413
+ verifyUpdateRootChain(rootEnvelope, digest, []);
414
+ }
415
+ catch (error) {
416
+ fail(`production trust root must be a valid self-threshold-signed root envelope: ${error instanceof Error ? error.message : "invalid root"}`);
417
+ }
418
+ if (digest !== plan.trustRootSha256)
419
+ fail("trust-root digest does not match the canonical C62 root envelope");
420
+ const receipt = record(parseJson(receiptBytes, "production root review receipt"), "production root review receipt");
421
+ exactKeys(receipt, [
422
+ "schemaVersion",
423
+ "ceremonyId",
424
+ "rootEnvelopeSha256",
425
+ "reviewerId",
426
+ "reviewedAt",
427
+ "decision",
428
+ "authentication",
429
+ "productionActivation",
430
+ ], "production root review receipt");
431
+ const reviewedAt = text(receipt.reviewedAt, "production root review time", 64);
432
+ const reviewedTime = new Date(reviewedAt);
433
+ if (receipt.schemaVersion !== 1 ||
434
+ receipt.ceremonyId !== manifest.ceremonyId ||
435
+ receipt.rootEnvelopeSha256 !== digest ||
436
+ typeof receipt.reviewerId !== "string" ||
437
+ manifest.custodians.some(({ id }) => id === receipt.reviewerId) ||
438
+ receipt.decision !== "approved" ||
439
+ receipt.authentication !== "externally-authenticated" ||
440
+ receipt.productionActivation !== false ||
441
+ !Number.isFinite(reviewedTime.getTime()) ||
442
+ reviewedTime.toISOString() !== reviewedAt ||
443
+ reviewedTime.getTime() > Date.now())
444
+ fail("production root review receipt is not independent, authenticated, and digest-bound");
445
+ }
446
+ export function evaluateReleasePreflight(options) {
447
+ const repositoryRoot = fs.realpathSync(options.repositoryRoot);
448
+ const manifestFile = readBoundedRegularFile(repositoryRoot, path.resolve(options.planPath), "release-plan manifest", MAX_MANIFEST_BYTES);
449
+ const plan = parsePlan(parseJson(manifestFile.bytes, "release-plan manifest"));
450
+ const git = options.git ?? defaultGit(repositoryRoot);
451
+ if (git.statusPorcelain() !== "")
452
+ fail("checkout must be clean before release mutation");
453
+ if (git.headCommit() !== plan.sourceCommit)
454
+ fail("checkout HEAD does not equal source commit");
455
+ if (git.tagObjectType(plan.tag) !== "tag")
456
+ fail("release tag must be an annotated tag object");
457
+ if (git.tagCommit(plan.tag) !== plan.sourceCommit)
458
+ fail("release tag does not resolve to the source commit");
459
+ const packageFile = readBoundedRegularFile(repositoryRoot, path.join(repositoryRoot, "package.json"), "package manifest", MAX_MANIFEST_BYTES);
460
+ if (!git.isTracked(packageFile.relativePath))
461
+ fail("package manifest must be checked in");
462
+ requireTaggedBytes(git, plan.sourceCommit, packageFile.relativePath, packageFile.bytes, "package manifest");
463
+ const packageManifest = record(parseJson(packageFile.bytes, "package manifest"), "package manifest");
464
+ if (packageManifest.version !== plan.version)
465
+ fail("package version does not equal release-plan version");
466
+ const workflowFile = readBoundedRegularFile(repositoryRoot, path.join(repositoryRoot, plan.workflow.path), "C64 candidate workflow", MAX_MANIFEST_BYTES);
467
+ if (!git.isTracked(workflowFile.relativePath))
468
+ fail("C64 candidate workflow must be checked in");
469
+ requireTaggedBytes(git, plan.sourceCommit, workflowFile.relativePath, workflowFile.bytes, "C64 candidate workflow");
470
+ const roadmapFile = readBoundedRegularFile(repositoryRoot, path.join(repositoryRoot, "roadmap/competitive-roadmap.md"), "authoritative roadmap", MAX_EVIDENCE_BYTES);
471
+ if (!git.isTracked(roadmapFile.relativePath))
472
+ fail("authoritative roadmap must be checked in");
473
+ requireTaggedBytes(git, plan.sourceCommit, roadmapFile.relativePath, roadmapFile.bytes, "authoritative roadmap");
474
+ const roadmapEvidence = verifyRoadmap(roadmapFile.bytes.toString("utf8"));
475
+ let totalEvidenceBytes = 0;
476
+ const uniquePaths = new Set();
477
+ const evidenceBytes = new Map();
478
+ for (const dependency of RELEASE_PREFLIGHT_DEPENDENCIES) {
479
+ const declared = plan.dependencyEvidence[dependency];
480
+ const authoritative = roadmapEvidence.get(dependency) ?? [];
481
+ if (declared.length !== authoritative.length ||
482
+ [...declared]
483
+ .sort(compareBytes)
484
+ .some((candidate, index) => candidate !== [...authoritative].sort(compareBytes)[index]))
485
+ fail(`${dependency} evidence paths must equal its complete authoritative Evidence field`);
486
+ for (const relativePath of plan.dependencyEvidence[dependency]) {
487
+ if (uniquePaths.has(relativePath))
488
+ fail(`evidence path is declared more than once: ${relativePath}`);
489
+ uniquePaths.add(relativePath);
490
+ const evidence = readBoundedRegularFile(repositoryRoot, path.join(repositoryRoot, relativePath), `${dependency} evidence ${relativePath}`, MAX_EVIDENCE_BYTES);
491
+ if (evidence.relativePath !== relativePath)
492
+ fail(`${dependency} evidence path must be normalized`);
493
+ if (!git.isTracked(relativePath))
494
+ fail(`${dependency} evidence must be checked in: ${relativePath}`);
495
+ requireTaggedBytes(git, plan.sourceCommit, relativePath, evidence.bytes, `${dependency} evidence ${relativePath}`);
496
+ evidenceBytes.set(relativePath, evidence.bytes);
497
+ totalEvidenceBytes += evidence.size;
498
+ if (totalEvidenceBytes > MAX_TOTAL_EVIDENCE_BYTES)
499
+ fail("dependency evidence exceeds the aggregate byte budget");
500
+ }
501
+ }
502
+ verifyProductionTrust(plan, evidenceBytes);
503
+ verifyFreshEvidenceDirectory(repositoryRoot, plan);
504
+ return {
505
+ ok: true,
506
+ version: plan.version,
507
+ tag: plan.tag,
508
+ sourceCommit: plan.sourceCommit,
509
+ workflowCommit: plan.workflow.commit,
510
+ evidenceDirectory: plan.evidenceDirectory,
511
+ dependencies: [...RELEASE_PREFLIGHT_DEPENDENCIES],
512
+ };
513
+ }
@@ -0,0 +1,85 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ const MAX_LEASE_BYTES = 4_096;
5
+ function leasePath(repo) {
6
+ return path.join(path.resolve(repo), ".knodin", "repair.lock");
7
+ }
8
+ function processIsAlive(pid) {
9
+ try {
10
+ process.kill(pid, 0);
11
+ return true;
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ }
17
+ function readLease(target) {
18
+ try {
19
+ const stat = fs.statSync(target);
20
+ if (!stat.isFile() || stat.size <= 0 || stat.size > MAX_LEASE_BYTES)
21
+ return null;
22
+ const value = JSON.parse(fs.readFileSync(target, "utf8"));
23
+ if (value.schemaVersion !== 1 ||
24
+ !Number.isInteger(value.pid) ||
25
+ (value.pid ?? 0) <= 0 ||
26
+ typeof value.startedAt !== "string" ||
27
+ !Number.isFinite(Date.parse(value.startedAt)) ||
28
+ typeof value.nonce !== "string" ||
29
+ !value.nonce)
30
+ return null;
31
+ return value;
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ }
37
+ function writeLease(target, record) {
38
+ fs.writeFileSync(target, `${JSON.stringify(record)}\n`, {
39
+ encoding: "utf8",
40
+ flag: "wx",
41
+ mode: 0o600,
42
+ });
43
+ }
44
+ function removeStaleLeaseOrThrow(target) {
45
+ const current = readLease(target);
46
+ if (current && processIsAlive(current.pid)) {
47
+ throw new Error(`knodin repair: another repair is already in progress (pid ${current.pid}, started ${current.startedAt}); wait for it to finish, then run \`knodin status\``);
48
+ }
49
+ try {
50
+ fs.unlinkSync(target);
51
+ }
52
+ catch (error) {
53
+ if (error.code !== "ENOENT")
54
+ throw error;
55
+ }
56
+ }
57
+ /** Atomically serialize repair post-processing across independent CLI/MCP processes. */
58
+ export function acquireRepairLease(repo) {
59
+ const target = leasePath(repo);
60
+ fs.mkdirSync(path.dirname(target), { recursive: true });
61
+ const record = {
62
+ schemaVersion: 1,
63
+ pid: process.pid,
64
+ startedAt: new Date().toISOString(),
65
+ nonce: crypto.randomUUID(),
66
+ };
67
+ for (let attempt = 0; attempt < 2; attempt += 1) {
68
+ try {
69
+ writeLease(target, record);
70
+ return {
71
+ release() {
72
+ const current = readLease(target);
73
+ if (current?.nonce === record.nonce)
74
+ fs.rmSync(target, { force: true });
75
+ },
76
+ };
77
+ }
78
+ catch (error) {
79
+ if (error.code !== "EEXIST")
80
+ throw error;
81
+ removeStaleLeaseOrThrow(target);
82
+ }
83
+ }
84
+ throw new Error("knodin repair: could not acquire the repository repair lease; retry shortly");
85
+ }