skalpel 4.0.43 → 4.0.45

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/card.mjs CHANGED
@@ -69,7 +69,20 @@ export function isGenuineCatch(row) {
69
69
  // Ship-status catches (category:"ship", outcome SHIP_FAIL) record the check under probe_command, not
70
70
  // proof_command — accept either so a genuine "you said deployed, it's 404" catch is not invisible.
71
71
  const command = row.proof_command || row.probe_command;
72
- return failed && !!command && !!row.claim_text;
72
+ if (!failed || !command || !row.claim_text) return false;
73
+ // RETRO catches (category:"retro") are RECORDED at claim time — never re-run. The whole indictment rests
74
+ // on the exit code the transcript already recorded, so a retro row is a genuine catch ONLY when it carries
75
+ // that recorded non-zero exit code; without it we can't render the "already failed" provenance truthfully.
76
+ if (row.category === "retro") {
77
+ return Number.isInteger(row.recorded_exit_code) && row.recorded_exit_code !== 0;
78
+ }
79
+ return true;
80
+ }
81
+
82
+ // A retro row is RECORDED at claim time (from the user's own transcript), never re-run — so its wording
83
+ // must never borrow the live card's "skalpel re-ran its own proof" language.
84
+ function isRetro(row) {
85
+ return row && row.category === "retro";
73
86
  }
74
87
 
75
88
  // readCatches() → all genuine-catch rows in the shadow log, oldest→newest as written. Fail-open: any
@@ -133,9 +146,17 @@ export function extractCode(evidence) {
133
146
 
134
147
  // verdictPhrase(row) → the honest one-line verdict. Always truthful for a catch row.
135
148
  function verdictPhrase(row) {
136
- const code = extractCode(row.evidence);
137
149
  const failN = extractFailCount(row.evidence);
138
150
  const count = failN != null ? ` · ${failN} failing` : "";
151
+ // RETRO: the exit code was RECORDED in the transcript at claim time — we never re-ran it, so the verdict
152
+ // must say "had already failed", never "re-ran". recorded_exit_code is a required field for a retro catch.
153
+ if (isRetro(row)) {
154
+ const n = Number.isInteger(row.recorded_exit_code)
155
+ ? ` (recorded exit ${row.recorded_exit_code})`
156
+ : "";
157
+ return `FAIL — its own test had already failed when it said this${n}${count}`;
158
+ }
159
+ const code = extractCode(row.evidence);
139
160
  if (code && code.kind === "http") return `FAIL — its own proof got HTTP ${code.value}${count}`;
140
161
  if (code && code.kind === "exit") return `FAIL — its own proof exited ${code.value}${count}`;
141
162
  return `FAIL — its own proof re-ran and exited non-zero${count}`;
@@ -152,13 +173,18 @@ export function renderPlainCard(row) {
152
173
  const claim = clean(row.claim_text, 200) || "it's done";
153
174
  const proof = clean(row.proof_command || row.probe_command, 200) || "its own proof";
154
175
  const evidence = clean(row.evidence, 200);
176
+ const retro = isRetro(row);
155
177
  const L = [];
156
- L.push("🔬 skalpel — verified catch");
178
+ L.push(retro ? "🔬 skalpel — recorded catch" : "🔬 skalpel — verified catch");
157
179
  L.push("");
158
180
  L.push("The agent claimed:");
159
181
  L.push(` "${claim}"`);
160
182
  L.push("");
161
- L.push("skalpel re-ran the agent's OWN proof, out-of-band:");
183
+ L.push(
184
+ retro
185
+ ? "Its own test had already failed when it said this — recorded in your transcript at claim time:"
186
+ : "skalpel re-ran the agent's OWN proof, out-of-band:",
187
+ );
162
188
  L.push(` $ ${proof}`);
163
189
  L.push(` → ${verdictPhrase(row)}`);
164
190
  if (evidence) {
@@ -167,8 +193,16 @@ export function renderPlainCard(row) {
167
193
  L.push(` ${evidence}`);
168
194
  }
169
195
  L.push("");
170
- L.push(`caught ${stamp(row.ts)}`);
171
- L.push("The agent said done. Its own proof, re-run, disagreed.");
196
+ L.push(
197
+ retro
198
+ ? `recorded ${stamp(row.ts)} (your transcript, at claim time)`
199
+ : `caught ${stamp(row.ts)}`,
200
+ );
201
+ L.push(
202
+ retro
203
+ ? "The agent said done. Its own test, recorded at that moment, had already failed."
204
+ : "The agent said done. Its own proof, re-run, disagreed.",
205
+ );
172
206
  return L.join("\n");
173
207
  }
174
208
 
@@ -177,13 +211,18 @@ export function renderMarkdownCard(row) {
177
211
  const claim = clean(row.claim_text, 200) || "it's done";
178
212
  const proof = clean(row.proof_command || row.probe_command, 200) || "its own proof";
179
213
  const evidence = clean(row.evidence, 200);
214
+ const retro = isRetro(row);
180
215
  const FENCE = "```";
181
216
  const L = [];
182
- L.push("**🔬 skalpel — verified catch**");
217
+ L.push(retro ? "**🔬 skalpel — recorded catch**" : "**🔬 skalpel — verified catch**");
183
218
  L.push("");
184
219
  L.push(`> The agent claimed: _"${claim}"_`);
185
220
  L.push("");
186
- L.push("skalpel re-ran the agent's own proof, out-of-band:");
221
+ L.push(
222
+ retro
223
+ ? "Its own test had already failed when it said this — recorded in your transcript at claim time:"
224
+ : "skalpel re-ran the agent's own proof, out-of-band:",
225
+ );
187
226
  L.push(FENCE);
188
227
  L.push(`$ ${proof}`);
189
228
  L.push(`→ ${verdictPhrase(row)}`);
@@ -194,7 +233,11 @@ export function renderMarkdownCard(row) {
194
233
  L.push(evidence);
195
234
  L.push(FENCE);
196
235
  }
197
- L.push(`_Caught ${stamp(row.ts)}. The agent said done; its own proof, re-run, disagreed._`);
236
+ L.push(
237
+ retro
238
+ ? `_Recorded ${stamp(row.ts)} in your transcript, at claim time. The agent said done; its own test had already failed._`
239
+ : `_Caught ${stamp(row.ts)}. The agent said done; its own proof, re-run, disagreed._`,
240
+ );
198
241
  return L.join("\n");
199
242
  }
200
243
 
@@ -203,14 +246,21 @@ function renderTerminalCard(row) {
203
246
  const claim = clean(row.claim_text, 200) || "it's done";
204
247
  const proof = clean(row.proof_command || row.probe_command, 200) || "its own proof";
205
248
  const evidence = clean(row.evidence, 180);
249
+ const retro = isRetro(row);
206
250
  const L = [];
207
251
  L.push("");
208
- L.push(` ${B}🔬 skalpel${X} ${D}·${X} ${B}${R}verified catch${X}`);
252
+ L.push(
253
+ ` ${B}🔬 skalpel${X} ${D}·${X} ${B}${R}${retro ? "recorded catch" : "verified catch"}${X}`,
254
+ );
209
255
  L.push(` ${D}${"─".repeat(70)}${X}`);
210
256
  L.push(` ${D}The agent claimed:${X}`);
211
257
  L.push(` ${B}"${claim}"${X}`);
212
258
  L.push("");
213
- L.push(` ${D}skalpel re-ran the agent's OWN proof, out-of-band:${X}`);
259
+ L.push(
260
+ retro
261
+ ? ` ${D}Its own test had already failed when it said this — recorded at claim time:${X}`
262
+ : ` ${D}skalpel re-ran the agent's OWN proof, out-of-band:${X}`,
263
+ );
214
264
  L.push(` ${D}$${X} ${proof}`);
215
265
  L.push(` ${R}${B}→ ${verdictPhrase(row)}${X}`);
216
266
  if (evidence) {
@@ -219,7 +269,9 @@ function renderTerminalCard(row) {
219
269
  }
220
270
  L.push(` ${D}${"─".repeat(70)}${X}`);
221
271
  L.push(
222
- ` ${D}caught ${stamp(row.ts)} — the agent said done; its own proof, re-run, disagreed.${X}`,
272
+ retro
273
+ ? ` ${D}recorded ${stamp(row.ts)} — the agent said done; its own test had already failed.${X}`
274
+ : ` ${D}caught ${stamp(row.ts)} — the agent said done; its own proof, re-run, disagreed.${X}`,
223
275
  );
224
276
  L.push("");
225
277
  return L.join("\n");