skalpel 4.0.55 → 4.0.57
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/first-run.mjs +5 -1
- package/insights.mjs +8 -3
- package/package.json +1 -1
- package/skalpel-statusline.mjs +73 -51
- package/verify-safeguards.test.mjs +24 -12
package/first-run.mjs
CHANGED
|
@@ -586,7 +586,11 @@ export function assignVariant(home, env = process.env) {
|
|
|
586
586
|
return forced;
|
|
587
587
|
}
|
|
588
588
|
if (state.variant === "reveal" || state.variant === "holdout") return state.variant;
|
|
589
|
-
|
|
589
|
+
// DEFAULT: everyone gets the reveal (founder decision 2026-07-14 — "everything off dark, to prod").
|
|
590
|
+
// The 50/50 holdout A/B is retired for new installs; the SKALPEL_FIRST_RUN_VARIANT override above
|
|
591
|
+
// still forces either arm (tests / a future re-run of the experiment), and previously-assigned
|
|
592
|
+
// holdout users keep their recorded arm (the persisted-state branch above) so old data stays clean.
|
|
593
|
+
const variant = "reveal";
|
|
590
594
|
state.variant = variant;
|
|
591
595
|
state.assigned_at = new Date().toISOString();
|
|
592
596
|
writeState(home, state);
|
package/insights.mjs
CHANGED
|
@@ -59,7 +59,7 @@ export function verifyConfigArmed() {
|
|
|
59
59
|
return null;
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
// revealEnabled(env) — is the USER-VISIBLE reveal armed? env override > consented config > default
|
|
62
|
+
// revealEnabled(env) — is the USER-VISIBLE reveal armed? env override > consented config > default ON.
|
|
63
63
|
// SKALPEL_VERIFY_SHADOW is deliberately NOT consulted here: it arms only the DARK log-only shadow (no
|
|
64
64
|
// reveal); the spawn gate ORs it in separately (verifySpawnArmed). Wrapped so it can never throw.
|
|
65
65
|
export function revealEnabled(env) {
|
|
@@ -67,11 +67,16 @@ export function revealEnabled(env) {
|
|
|
67
67
|
const envOverride = parseArmToken((env || {}).SKALPEL_VERIFY_REVEAL);
|
|
68
68
|
if (envOverride !== null) return envOverride; // 1) dev/CI env override wins
|
|
69
69
|
const cfg = verifyConfigArmed();
|
|
70
|
-
if (cfg !== null) return cfg; // 2) consented config
|
|
70
|
+
if (cfg !== null) return cfg; // 2) consented config (skalpel verify on|off still rules)
|
|
71
71
|
} catch {
|
|
72
72
|
/* fall through to default */
|
|
73
73
|
}
|
|
74
|
-
|
|
74
|
+
// 3) DEFAULT ON — founder decision 2026-07-14 ("everything off dark, to prod"): the live catch +
|
|
75
|
+
// reveal ship ARMED out of the box. The safeguards that make this sane are unchanged and still
|
|
76
|
+
// override this default: `skalpel verify off` (consented config, step 2), the env kill (step 1),
|
|
77
|
+
// the flake double-confirm before any red, recorded-failure corroboration on Codex, and the
|
|
78
|
+
// adjudicated auto-darken (precision < 90% over ≥ 10 verdicts silently re-darkens the reveal).
|
|
79
|
+
return true;
|
|
75
80
|
}
|
|
76
81
|
// verifySpawnArmed(env) — should the per-turn hook SPAWN the detached shadow worker at all? True when the
|
|
77
82
|
// reveal is armed OR the dev log-only shadow (SKALPEL_VERIFY_SHADOW=1) is set. This is the single gate the
|
package/package.json
CHANGED
package/skalpel-statusline.mjs
CHANGED
|
@@ -9,22 +9,31 @@
|
|
|
9
9
|
// that verifiably happened; else
|
|
10
10
|
// • the current steer's plain-English label (what skalpel is doing now) — a description, not a number;
|
|
11
11
|
// else
|
|
12
|
-
// • the ALWAYS-ON
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
12
|
+
// • the ALWAYS-ON AMBIENT line: the `🔬 skalpel` wordmark, a dim em-dash, then a COMPOSED body of only
|
|
13
|
+
// the clauses that have real value (see THE INSTRUMENT LINE below). Nothing yet reads
|
|
14
|
+
// "🔬 skalpel — watching your agent's claims" — present, quiet, never "0 steers".
|
|
15
|
+
//
|
|
16
|
+
// THE INSTRUMENT LINE (redesign, 2026-07-14 — founder: "the bottom of my terminal is ugly, zero
|
|
17
|
+
// aesthetic, zero gratifying feeling" about `skalpel · 0 steers`). Typography + hierarchy, not
|
|
18
|
+
// decoration. The ambient body composes, in order, ONLY the clauses whose value exists:
|
|
19
|
+
// clean → "N claims verified[ · Xm caught][ · N steers]"
|
|
20
|
+
// caught → "N checked · M false[ · Xm caught][ · N steers]"
|
|
21
|
+
// nothing → "watching your agent's claims" (dim — quiet confidence, NOT a dead zero)
|
|
22
|
+
// · <Xm> caught — the CUMULATIVE LIFETIME sum of every real MEASURED red→green delta (each caught
|
|
23
|
+
// false "done" and the moment that SAME proof, that SAME session, was later logged PASS — real
|
|
24
|
+
// timestamps; reuses ledger.mjs's buildFixDeltas logic). A measured total, NEVER an estimate; a
|
|
25
|
+
// catch with no later PASS adds 0. Cumulative framing (not present-tense) keeps it honest.
|
|
26
|
+
// · N steers — steers ACTUALLY applied (fired + not suppressed), counted from the append-only
|
|
27
|
+
// ~/.skalpel/steers.ndjson.
|
|
28
|
+
// IMMUTABLE ALWAYS-ON COUNTER (owner product decision — unchanged in substance): the counter surfaces
|
|
29
|
+
// (`N steers` when > 0, the measured `Xm caught` whenever it has a value) remain ALWAYS-ON on the
|
|
30
|
+
// ambient line — never gated, never hidden. This redesign only removes DEAD ZEROS: a zero-value clause
|
|
31
|
+
// is omitted, and the always-on presence is carried by the wordmark + the watching line instead of a
|
|
32
|
+
// literal "0 steers". "0 steers" never renders again.
|
|
33
|
+
// COLOR DISCIPLINE (Ive restraint): wordmark bold; live numbers cyan; chrome words dim. The skalpel red
|
|
34
|
+
// is ONE accent used ONCE — only on the `M false` clause when lies > 0. Semantic color only.
|
|
24
35
|
// Every value here is a real count or a real measured timestamp delta (or a clearly `~`-marked estimate).
|
|
25
|
-
//
|
|
26
|
-
// ZERO bytes; this emits "skalpel · 0 steers" (Ryan: always-on presence, non-negotiable). Byte-identical
|
|
27
|
-
// to origin/main is preserved ONLY for the untouched reveal / verdict / re-ask surfaces, not clean-idle.
|
|
36
|
+
// Byte-identical to origin/main is preserved for the untouched reveal / verdict surfaces.
|
|
28
37
|
// Fail-open: any error → print nothing (or just the presence line), exit 0. Never blocks the bar.
|
|
29
38
|
import { readFileSync, rmSync } from "node:fs";
|
|
30
39
|
import { homedir } from "node:os";
|
|
@@ -37,14 +46,22 @@ const GREEN = "\x1b[32m";
|
|
|
37
46
|
const BOLD = "\x1b[1m";
|
|
38
47
|
const DIM = "\x1b[2m";
|
|
39
48
|
const RESET = "\x1b[0m";
|
|
49
|
+
// The skalpel red — the ONE accent, used ONCE per line at most (only on `M false` when lies > 0).
|
|
50
|
+
const RED = "\x1b[38;2;233;38;31m";
|
|
51
|
+
|
|
52
|
+
// The composed wordmark + separators of the instrument line. Em-dash after the wordmark (it breathes);
|
|
53
|
+
// dim mid-dots between clauses. ANSI only, one line, no animation — the bar re-invokes this command.
|
|
54
|
+
const WORDMARK = `🔬 ${BOLD}skalpel${RESET}`;
|
|
55
|
+
const EMDASH = `${DIM} — ${RESET}`;
|
|
56
|
+
const MIDDOT = ` ${DIM}·${RESET} `;
|
|
40
57
|
|
|
41
|
-
// ── RENDER MODE — the ONE constant Ryan flips. Governs the optional TIME clause on the
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
// "steers" →
|
|
45
|
-
// "measured" →
|
|
46
|
-
// "saved" →
|
|
47
|
-
// "both" →
|
|
58
|
+
// ── RENDER MODE — the ONE constant Ryan flips. Governs the optional TIME clause(s) on the ambient
|
|
59
|
+
// instrument line. The heartbeat (claims verified / checked · false) and the `N steers` clause are
|
|
60
|
+
// mode-independent. Nothing else on the bar (reveal / verdict / re-ask / steer label) is affected.
|
|
61
|
+
// "steers" → no time clause
|
|
62
|
+
// "measured" → · 38m caught (DEFAULT — cumulative lifetime MEASURED red→green)
|
|
63
|
+
// "saved" → · ~7m saved (Raahil's own-graph ESTIMATE, from steers.ndjson)
|
|
64
|
+
// "both" → · 38m caught · ~7m saved
|
|
48
65
|
const BASELINE_TIME_MODE = "measured";
|
|
49
66
|
const SHOW_MEASURED = BASELINE_TIME_MODE === "measured" || BASELINE_TIME_MODE === "both";
|
|
50
67
|
const SHOW_SAVED = BASELINE_TIME_MODE === "saved" || BASELINE_TIME_MODE === "both";
|
|
@@ -423,7 +440,7 @@ function main() {
|
|
|
423
440
|
const n = reaskCount(payload.transcript_path);
|
|
424
441
|
if (n >= 2) {
|
|
425
442
|
process.stdout.write(
|
|
426
|
-
`${
|
|
443
|
+
`${WORDMARK}${EMDASH}${CYAN}${n}${RESET} ${DIM}re-asks/interrupts this stretch${RESET}${MIDDOT}${DIM}run: skalpel autopsy${RESET}`,
|
|
427
444
|
);
|
|
428
445
|
return;
|
|
429
446
|
}
|
|
@@ -431,44 +448,49 @@ function main() {
|
|
|
431
448
|
// Otherwise, if a steer is live, name what it's doing — a description, no number.
|
|
432
449
|
const label = steerLabel();
|
|
433
450
|
if (label) {
|
|
434
|
-
process.stdout.write(`${
|
|
451
|
+
process.stdout.write(`${WORDMARK}${EMDASH}${CYAN}${label}${RESET}`);
|
|
435
452
|
return;
|
|
436
453
|
}
|
|
437
454
|
|
|
438
|
-
//
|
|
439
|
-
// never hidden).
|
|
440
|
-
//
|
|
441
|
-
//
|
|
442
|
-
//
|
|
443
|
-
//
|
|
444
|
-
//
|
|
445
|
-
//
|
|
446
|
-
//
|
|
447
|
-
//
|
|
455
|
+
// THE AMBIENT INSTRUMENT LINE — skalpel's always-on terminal presence (owner product decision: never
|
|
456
|
+
// gated, never hidden). The wordmark leads; the body composes ONLY the clauses with real value, each
|
|
457
|
+
// an AGGREGATE number (never per-catch claim text / proof — those live in the opt-in reveal + the TUI):
|
|
458
|
+
// • THE HEARTBEAT (founder: "I only see 0 steers — the product is invisible while it works"):
|
|
459
|
+
// clean so far → "3 claims verified" (the earned-silence receipt — real verdicts only)
|
|
460
|
+
// lies caught → "31 checked · 2 false" (the honest split; `M false` carries the one red accent)
|
|
461
|
+
// • MEASURED "Xm caught" — the CUMULATIVE LIFETIME sum of every real measured red→green delta
|
|
462
|
+
// (shadowTally). IMMUTABLE product surface: rendered whenever it has a value; a MEASURED
|
|
463
|
+
// wall-clock, never an estimate. Cumulative framing keeps it honest for old catches.
|
|
464
|
+
// • "N steers" — the user's own applied-steer count, whenever > 0 (always-on WHEN IT HAS VALUE;
|
|
465
|
+
// the redesign only removed the dead "0 steers" zero — presence is now the wordmark itself).
|
|
466
|
+
// • Raahil's `~`-prefixed saved ESTIMATE ("~7m saved") — mode-gated, shown only when saved_min > 0
|
|
467
|
+
// (its data is the user's own steers.ndjson credit, not the shadow log — #609 behavior preserved).
|
|
468
|
+
// Nothing yet (no verdicts, no steers, no measured) → the quiet watching line, never a dead zero.
|
|
448
469
|
const { steers, savedMin } = readState();
|
|
449
|
-
const tally =
|
|
450
|
-
|
|
470
|
+
const tally = shadowTally();
|
|
471
|
+
const clauses = [];
|
|
472
|
+
if (tally.checked > 0) {
|
|
473
|
+
if (tally.lies > 0) {
|
|
474
|
+
clauses.push(`${CYAN}${tally.checked}${RESET} ${DIM}checked${RESET}`);
|
|
475
|
+
clauses.push(`${RED}${tally.lies} false${RESET}`); // the ONE red accent, used once
|
|
476
|
+
} else {
|
|
477
|
+
clauses.push(
|
|
478
|
+
`${CYAN}${tally.checked}${RESET} ${DIM}claim${tally.checked === 1 ? "" : "s"} verified${RESET}`,
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
451
482
|
if (SHOW_MEASURED) {
|
|
452
483
|
const caught = compactMeasured(tally.totalMs); // null when < 1s of real measured catches
|
|
453
|
-
if (caught)
|
|
454
|
-
// THE HEARTBEAT (founder: "I only see 0 steers — the product is invisible while it works").
|
|
455
|
-
// Once the armed shadow has genuinely CHECKED at least one claim, the line SHOWS the work:
|
|
456
|
-
// clean so far → "· 3 claims verified" (the earned-silence receipt — real verdicts only)
|
|
457
|
-
// lies caught → "· 31 checked · 2 false" (the honest split; the measured clause carries the time)
|
|
458
|
-
// Real counts from the same single log pass. A fresh install (checked = 0) emits EXACTLY the
|
|
459
|
-
// baseline line — byte-identical, nothing invented.
|
|
460
|
-
if (tally.checked > 0) {
|
|
461
|
-
line +=
|
|
462
|
-
tally.lies > 0
|
|
463
|
-
? ` · ${CYAN}${tally.checked} checked · ${tally.lies} false${RESET}`
|
|
464
|
-
: ` · ${CYAN}${tally.checked} claim${tally.checked === 1 ? "" : "s"} verified${RESET}`;
|
|
465
|
-
}
|
|
484
|
+
if (caught) clauses.push(`${CYAN}${caught}${RESET} ${DIM}caught${RESET}`);
|
|
466
485
|
}
|
|
486
|
+
if (steers > 0)
|
|
487
|
+
clauses.push(`${CYAN}${steers}${RESET} ${DIM}steer${steers === 1 ? "" : "s"}${RESET}`);
|
|
467
488
|
if (SHOW_SAVED) {
|
|
468
489
|
const phrase = savedPhrase(savedMin);
|
|
469
|
-
if (phrase)
|
|
490
|
+
if (phrase) clauses.push(`${CYAN}${phrase}${RESET}`);
|
|
470
491
|
}
|
|
471
|
-
|
|
492
|
+
const body = clauses.length ? clauses.join(MIDDOT) : `${DIM}watching your agent's claims${RESET}`;
|
|
493
|
+
process.stdout.write(`${WORDMARK}${EMDASH}${body}`);
|
|
472
494
|
}
|
|
473
495
|
|
|
474
496
|
// Fail-open: wrap the whole render so a thrown error can never spill a stack trace into the live status
|
|
@@ -37,11 +37,11 @@ beforeEach(() => {
|
|
|
37
37
|
});
|
|
38
38
|
const writeCfg = (o) => writeFileSync(CLIENT_JSON_PATH, JSON.stringify(o));
|
|
39
39
|
|
|
40
|
-
// ---- arming resolver precedence: env > config >
|
|
41
|
-
test("arming —
|
|
40
|
+
// ---- arming resolver precedence: env > config > DEFAULT ON (founder 2026-07-14, off-dark) -------
|
|
41
|
+
test("arming — DEFAULT ON when config absent and env unset (ships armed)", () => {
|
|
42
42
|
assert.equal(verifyConfigArmed(), null);
|
|
43
|
-
assert.equal(revealEnabled({}),
|
|
44
|
-
assert.equal(verifySpawnArmed({}),
|
|
43
|
+
assert.equal(revealEnabled({}), true);
|
|
44
|
+
assert.equal(verifySpawnArmed({}), true);
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
test("arming — consented config on/off", () => {
|
|
@@ -59,17 +59,29 @@ test("arming — env override BEATS config in both directions", () => {
|
|
|
59
59
|
assert.equal(revealEnabled({ SKALPEL_VERIFY_REVEAL: "1" }), true, "env on beats config off");
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
-
test("arming — SKALPEL_VERIFY_SHADOW arms the spawn
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
test("arming — SKALPEL_VERIFY_SHADOW arms the spawn even when the user turned the reveal OFF", () => {
|
|
63
|
+
// The meaningful spawn-vs-reveal split now lives on the DECLINED path: with config verify:"off",
|
|
64
|
+
// the reveal stays dark but the dev/CI log-only shadow env can still arm the spawn.
|
|
65
|
+
writeCfg({ verify: "off" });
|
|
66
|
+
assert.equal(
|
|
67
|
+
revealEnabled({ SKALPEL_VERIFY_SHADOW: "1" }),
|
|
68
|
+
false,
|
|
69
|
+
"user's off still rules the reveal",
|
|
70
|
+
);
|
|
71
|
+
assert.equal(
|
|
72
|
+
verifySpawnArmed({ SKALPEL_VERIFY_SHADOW: "1" }),
|
|
73
|
+
true,
|
|
74
|
+
"log-only shadow still spawns",
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("arming — fail-open: a corrupt client.json never throws, resolves to the DEFAULT (on)", () => {
|
|
69
79
|
writeFileSync(CLIENT_JSON_PATH, "{ this is not json");
|
|
70
80
|
assert.doesNotThrow(() => revealEnabled({}));
|
|
71
81
|
assert.equal(verifyConfigArmed(), null);
|
|
72
|
-
|
|
82
|
+
// Unreadable config falls through to the ships-armed default. A user who declined keeps OFF via a
|
|
83
|
+
// VALID config (previous test); the corrupt-file edge resolves like a fresh install.
|
|
84
|
+
assert.equal(revealEnabled({}), true);
|
|
73
85
|
});
|
|
74
86
|
|
|
75
87
|
// ---- adjudication ledger + <90% auto-darken derivation ------------------------------------------
|