patchwork-os 1.2.0-beta.3.canary.927 → 1.2.0-beta.3.canary.928

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.
@@ -23,6 +23,20 @@ export interface EvidenceVerification {
23
23
  dir: string;
24
24
  ok: boolean;
25
25
  ledgers: LedgerVerification[];
26
+ /** Present ledgers whose chain was walked (chained rows > 0, or an intact legacy+chain file). */
27
+ verified: number;
28
+ /** No file and no sidecar: never written here. Legitimate for a ledger with no evidence yet. */
29
+ absent: number;
30
+ /** Present, legacy rows only, no chain-start yet. Non-fatal unless `expectChained`. */
31
+ unchained: number;
32
+ /** File gone, `.head` sidecar remains: deleted. Always a break. */
33
+ missingWithHead: number;
34
+ /** Whether `unchained` was treated as a failure. */
35
+ expectChained: boolean;
36
+ }
37
+ export interface VerifyEvidenceOpts {
38
+ /** Treat a legacy-only (never chained) ledger as NOT ok. */
39
+ expectChained?: boolean;
26
40
  }
27
41
  /**
28
42
  * What `evidence verify` walks: the spine PLUS chained ledgers that are not
@@ -35,5 +49,6 @@ export declare const VERIFIED_LEDGERS: ReadonlyArray<{
35
49
  key: string;
36
50
  file: string;
37
51
  }>;
38
- export declare function verifyEvidenceChains(dir?: string): EvidenceVerification;
52
+ export declare function isUnchained(l: ChainVerification): boolean;
53
+ export declare function verifyEvidenceChains(dir?: string, opts?: VerifyEvidenceOpts): EvidenceVerification;
39
54
  export declare function formatEvidenceVerify(r: EvidenceVerification): string;
@@ -30,14 +30,36 @@ export const VERIFIED_LEDGERS = [
30
30
  { key: "run steps", file: "run_steps.jsonl" },
31
31
  { key: "pr outcomes", file: "pr_outcomes.jsonl" },
32
32
  ];
33
- export function verifyEvidenceChains(dir = patchworkHome()) {
33
+ export function isUnchained(l) {
34
+ return !l.absent && l.chainedRows === 0 && l.legacyPrefix === "unchained";
35
+ }
36
+ export function verifyEvidenceChains(dir = patchworkHome(), opts = {}) {
37
+ const expectChained = opts.expectChained === true;
34
38
  const ledgers = [];
35
39
  for (const { key, file } of VERIFIED_LEDGERS) {
36
40
  const v = verifyLedgerChain(path.join(dir, file));
37
41
  ledgers.push({ ...v, key, file });
38
42
  }
39
- const ok = ledgers.every((l) => l.ok && l.writeFailedPending === 0);
40
- return { dir, ok, ledgers };
43
+ const missingWithHead = ledgers.filter((l) => l.absent && l.head === "orphaned").length;
44
+ const absent = ledgers.filter((l) => l.absent && l.head !== "orphaned").length;
45
+ const unchained = ledgers.filter(isUnchained).length;
46
+ const verified = ledgers.filter((l) => !l.absent && !isUnchained(l)).length;
47
+ // Every ledger that exists must verify, a deleted one is a break, and a
48
+ // verdict over ZERO verified ledgers is not a verdict: a wrong --dir and a
49
+ // clean spine must not print the same word (audit 2026-10-15, H6).
50
+ const ok = ledgers.every((l) => l.ok && l.writeFailedPending === 0) &&
51
+ verified > 0 &&
52
+ (!expectChained || unchained === 0);
53
+ return {
54
+ dir,
55
+ ok,
56
+ ledgers,
57
+ verified,
58
+ absent,
59
+ unchained,
60
+ missingWithHead,
61
+ expectChained,
62
+ };
41
63
  }
42
64
  export function formatEvidenceVerify(r) {
43
65
  const L = [];
@@ -45,11 +67,16 @@ export function formatEvidenceVerify(r) {
45
67
  L.push(` ${r.dir}`);
46
68
  L.push("");
47
69
  for (const l of r.ledgers) {
70
+ if (l.absent && l.head === "orphaned") {
71
+ L.push(` MISSING ${l.file} file is gone but its .head sidecar remains — it used to exist`);
72
+ continue;
73
+ }
48
74
  if (l.absent) {
49
75
  L.push(` ABSENT ${l.file}`);
50
76
  continue;
51
77
  }
52
- const chainNote = l.chainedRows === 0 && l.legacyPrefix === "unchained"
78
+ const unchained = isUnchained(l);
79
+ const chainNote = unchained
53
80
  ? `${l.legacyRows} legacy row(s), no chain yet`
54
81
  : `${l.chainedRows} chained row(s), legacy prefix ${l.legacyPrefix}` +
55
82
  (l.legacyRows > 0 ? ` (${l.legacyRows} row(s))` : "") +
@@ -64,7 +91,9 @@ export function formatEvidenceVerify(r) {
64
91
  ? "BROKEN"
65
92
  : l.writeFailedPending > 0
66
93
  ? "PENDING"
67
- : "OK";
94
+ : unchained
95
+ ? "UNCHAINED"
96
+ : "OK";
68
97
  L.push(` ${state.padEnd(8)} ${l.file} ${chainNote}`);
69
98
  for (const b of l.breaks) {
70
99
  L.push(` ${b.kind} break at line ${b.line}` +
@@ -78,9 +107,23 @@ export function formatEvidenceVerify(r) {
78
107
  }
79
108
  }
80
109
  L.push("");
81
- L.push(r.ok
82
- ? "STATUS: INTACT"
83
- : "STATUS: NOT INTACT — a break, a mismatch, a shortened file or an unsealed failure above");
110
+ const counts = `${r.verified} of ${r.ledgers.length} verified, ${r.absent} absent` +
111
+ (r.unchained > 0 ? `, ${r.unchained} unchained` : "") +
112
+ (r.missingWithHead > 0 ? `, ${r.missingWithHead} missing` : "");
113
+ if (r.verified === 0 && r.missingWithHead === 0) {
114
+ L.push(`STATUS: NOTHING VERIFIED (${counts}) — no ledger found under this directory; a wrong --dir and a clean spine must not read the same`);
115
+ }
116
+ else if (r.ok) {
117
+ L.push(`STATUS: INTACT (${counts})`);
118
+ }
119
+ else if (r.expectChained &&
120
+ r.unchained > 0 &&
121
+ r.ledgers.every((l) => l.ok && l.writeFailedPending === 0)) {
122
+ L.push(`STATUS: NOT INTACT (${counts}) — --expect-chained and a ledger carries legacy rows only`);
123
+ }
124
+ else {
125
+ L.push(`STATUS: NOT INTACT (${counts}) — a break, a mismatch, a shortened or deleted file or an unsealed failure above`);
126
+ }
84
127
  L.push("");
85
128
  return L.join("\n");
86
129
  }
@@ -1 +1 @@
1
- {"version":3,"file":"evidenceVerify.js","sourceRoot":"","sources":["../src/evidenceVerify.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"evidenceVerify.js","sourceRoot":"","sources":["../src/evidenceVerify.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -4494,7 +4494,7 @@ if (process.argv[2] === "sweep") {
4494
4494
  if (process.argv[2] === "evidence") {
4495
4495
  const args = process.argv.slice(3);
4496
4496
  if (args.includes("--help") || args.includes("-h")) {
4497
- process.stdout.write("Usage: patchwork evidence [verify] [--dir <path>] [--json]\n\n" +
4497
+ process.stdout.write("Usage: patchwork evidence [verify] [--dir <path>] [--json] [--expect-chained]\n\n" +
4498
4498
  " verify is every spine ledger internally intact? (ADR-0027) Checks the\n" +
4499
4499
  " per-row hash chain, the legacy-prefix commitment, the head sidecar\n" +
4500
4500
  " and unsealed write failures. Exits 1 on any break — the only form\n" +
@@ -4506,7 +4506,8 @@ if (process.argv[2] === "evidence") {
4506
4506
  "correlationId is a run's taskId and these ledgers hold real task titles and\n" +
4507
4507
  "third-party record ids. A measurement may leave the machine; the rows may not.\n\n" +
4508
4508
  " --dir <path> ledger directory (default: $PATCHWORK_HOME)\n" +
4509
- " --json emit the raw report\n");
4509
+ " --json emit the raw report\n" +
4510
+ " --expect-chained (verify) a ledger with legacy rows and no chain is NOT ok\n");
4510
4511
  process.exit(0);
4511
4512
  }
4512
4513
  (async () => {
@@ -4522,7 +4523,7 @@ if (process.argv[2] === "evidence") {
4522
4523
  args,
4523
4524
  subcommands: ["verify"],
4524
4525
  valueFlags: ["--dir"],
4525
- flags: ["--json"],
4526
+ flags: ["--json", "--expect-chained"],
4526
4527
  });
4527
4528
  const dirIdx = args.indexOf("--dir");
4528
4529
  const dir = dirIdx !== -1 ? args[dirIdx + 1] : undefined;
@@ -4532,7 +4533,10 @@ if (process.argv[2] === "evidence") {
4532
4533
  // with `ok: false` in the JSON so a cron job can act on it. Still
4533
4534
  // prints positions and counts only, never a value.
4534
4535
  const { verifyEvidenceChains, formatEvidenceVerify } = await import("./evidenceVerify.js");
4535
- const r = dir ? verifyEvidenceChains(dir) : verifyEvidenceChains();
4536
+ const verifyOpts = { expectChained: args.includes("--expect-chained") };
4537
+ const r = dir
4538
+ ? verifyEvidenceChains(dir, verifyOpts)
4539
+ : verifyEvidenceChains(undefined, verifyOpts);
4536
4540
  process.stdout.write(args.includes("--json")
4537
4541
  ? `${JSON.stringify(r, null, 2)}\n`
4538
4542
  : formatEvidenceVerify(r));