skalpel 4.0.34 → 4.0.35
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/package.json +1 -1
- package/skalpel-hook.mjs +49 -0
- package/skalpel-statusline.mjs +211 -24
package/package.json
CHANGED
package/skalpel-hook.mjs
CHANGED
|
@@ -146,6 +146,43 @@ function writePending(obj) {
|
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
// ALWAYS-ON PRESENCE tally — the honest lifetime record the statusline reads for its baseline line.
|
|
150
|
+
// Concept is Raahil's (#609): steers = count of steers ACTUALLY applied (fired AND not suppressed) across
|
|
151
|
+
// all sessions; saved_min = Σ, over those applied steers, of the user's OWN median cost of one loop of
|
|
152
|
+
// that steer's work-type (the server's `median_loop_min`, from the causal graph's sink/loop stats). It is
|
|
153
|
+
// NEVER a server wall-clock estimate; when the server has no median (null) we credit NOTHING.
|
|
154
|
+
//
|
|
155
|
+
// RACE-FREE by construction (audit Finding 2 — a read-modify-write single-counter loses increments under
|
|
156
|
+
// concurrent hook double-fires; metrics.mjs's own comment warns the same). So this is an APPEND-ONLY log:
|
|
157
|
+
// ONE atomic `appendFileSync` line per applied steer. POSIX guarantees a single small append is atomic, so
|
|
158
|
+
// N concurrent applied-steer writes yield exactly N lines (0 loss). No read, no tmp, no rename, no mutated
|
|
159
|
+
// counter. Same idiom as metrics.ndjson / insights.ndjson.
|
|
160
|
+
//
|
|
161
|
+
// ── STEERS SOURCE CONTRACT (the source of truth for the statusline baseline AND #620's analytics TUI) ──
|
|
162
|
+
// file : ~/.skalpel/steers.ndjson (append-only; one line per APPLIED steer; never rewritten/trimmed)
|
|
163
|
+
// line : {"ts": <epoch ms>, "saved_min_delta": <number ≥ 0>}
|
|
164
|
+
// ts = Date.now() when the steer was applied.
|
|
165
|
+
// saved_min_delta = the median_loop_min credited for THIS steer (≥ 0; 0 when the server sent
|
|
166
|
+
// no positive median — never fabricated).
|
|
167
|
+
// derive: steers = count of valid JSON lines (one line == one applied steer).
|
|
168
|
+
// saved_min = Σ saved_min_delta over valid lines (round to 1 dp; garbage/partial lines skipped).
|
|
169
|
+
// Readers MUST fail open (unreadable/absent → {steers:0, saved_min:0}) and MUST NOT mutate this file.
|
|
170
|
+
const STATE_LOG_PATH = join(homedir(), ".skalpel", "steers.ndjson");
|
|
171
|
+
function bumpState(medianMin) {
|
|
172
|
+
try {
|
|
173
|
+
// credit ONLY a real, positive median from the server; null/NaN/<=0 → 0 credit (no fabrication).
|
|
174
|
+
const savedMinDelta = Number.isFinite(medianMin) && medianMin > 0 ? medianMin : 0;
|
|
175
|
+
mkdirSync(join(homedir(), ".skalpel"), { recursive: true });
|
|
176
|
+
// ONE atomic append == ONE applied steer. Race-free (never a read-modify-write of a counter).
|
|
177
|
+
appendFileSync(
|
|
178
|
+
STATE_LOG_PATH,
|
|
179
|
+
JSON.stringify({ ts: Date.now(), saved_min_delta: savedMinDelta }) + "\n",
|
|
180
|
+
);
|
|
181
|
+
} catch {
|
|
182
|
+
/* the presence tally is a bonus; never block the hook */
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
149
186
|
// STEER label — the statusline reads this so the user SEES skalpel steering LIVE, every turn a steer
|
|
150
187
|
// injects, INDEPENDENT of whether the model surfaces a 🔬 line (the deterministic awareness channel).
|
|
151
188
|
// A one-glance 2-word label derived from the top injected steer type. Overwritten each turn.
|
|
@@ -559,6 +596,7 @@ async function main() {
|
|
|
559
596
|
let steerType = null; // raw top-steer type (TELL/SINK/…) — for the feedback loop + session log
|
|
560
597
|
let steerWt = null; // the work-type the steer is about (refine/debug/…)
|
|
561
598
|
let brokeOut = false; // behavioral-only: did expected-waste-ahead drop after last turn's steer (adherence)
|
|
599
|
+
let medianLoopMin = null; // server's per-work-type median loop cost (min) — the ONLY input to "saved"
|
|
562
600
|
|
|
563
601
|
if (!authNote) {
|
|
564
602
|
const remaining = deadline - Date.now();
|
|
@@ -596,6 +634,14 @@ async function main() {
|
|
|
596
634
|
steerLabel = j?.steer_label; // surfaced on the statusline (written once below, with any credit)
|
|
597
635
|
steerType = j?.steer_type || null;
|
|
598
636
|
steerWt = j?.steer_wt || (j?.entry ? String(j.entry).split("/")[0] : null);
|
|
637
|
+
// the user's OWN median cost of one loop of this steer's work-type (from the graph's sink stats).
|
|
638
|
+
// The ONLY quantity we ever turn into "saved" minutes, and only for an APPLIED steer (below).
|
|
639
|
+
medianLoopMin =
|
|
640
|
+
typeof j?.median_loop_min === "number" &&
|
|
641
|
+
isFinite(j.median_loop_min) &&
|
|
642
|
+
j.median_loop_min > 0
|
|
643
|
+
? j.median_loop_min
|
|
644
|
+
: null;
|
|
599
645
|
if (j?.entry) pushTraj(String(j.entry).split("/")[0]); // remember this turn's work-type
|
|
600
646
|
dbg(` -> sim=${j?.top_sim ?? "?"} ${injection ? "INJECT" : "silent"}`);
|
|
601
647
|
|
|
@@ -693,6 +739,9 @@ async function main() {
|
|
|
693
739
|
const steerId = injection ? String(Date.now()) : null;
|
|
694
740
|
writeSteer(steerLabel, steerType, steerWt, steerId);
|
|
695
741
|
if (injection) logSessionEvent({ type: steerType, wt: steerWt }); // feed the end-of-session recap
|
|
742
|
+
// ALWAYS-ON presence tally: a steer was APPLIED this turn (fired + survived suppression). Count it,
|
|
743
|
+
// and credit its honest median-loop cost ONLY when the server sent a real number (null -> +0).
|
|
744
|
+
if (injection) bumpState(medianLoopMin);
|
|
696
745
|
|
|
697
746
|
// LOCAL INSIGHT RECORDS (~/.skalpel/insights.ndjson) — one plain-English row per note kind that
|
|
698
747
|
// fired this turn, post-suppression (a muted/suppressed steer writes nothing). recordInsight
|
package/skalpel-statusline.mjs
CHANGED
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// skalpel statusline —
|
|
3
|
-
// estimate dressed up as a measurement.
|
|
4
|
-
//
|
|
2
|
+
// skalpel statusline — HONEST-OR-NOTHING. Prints only facts grounded in local state, never a server
|
|
3
|
+
// estimate dressed up as a measurement. In priority order (highest first):
|
|
4
|
+
// • the AHA CATCH reveal — agent claimed done, its OWN proof re-run GENUINELY failed (opt-in,
|
|
5
|
+
// SKALPEL_VERIFY_REVEAL; dark by default → byte-identical to the always-on baseline when off); else
|
|
6
|
+
// • the green "✓ verified" stamp — that same proof re-run really PASSED (same opt-in flag); else
|
|
5
7
|
// • the live re-ask/interrupt COUNT for THIS session, read straight from the transcript Claude Code
|
|
6
8
|
// hands us on stdin — the exact material `skalpel autopsy` re-derives from disk, a count of things
|
|
7
|
-
// that verifiably happened
|
|
8
|
-
// • the current steer's plain-English label (what skalpel is doing now) — a description, not a number
|
|
9
|
-
//
|
|
9
|
+
// that verifiably happened; else
|
|
10
|
+
// • the current steer's plain-English label (what skalpel is doing now) — a description, not a number;
|
|
11
|
+
// else
|
|
12
|
+
// • the ALWAYS-ON presence line, "skalpel · N steers[ · <time clause>]".
|
|
13
|
+
// N = steers ACTUALLY applied (fired + not suppressed), counted from the append-only
|
|
14
|
+
// ~/.skalpel/steers.ndjson — a fresh install reads "0 steers", present, never blank. The optional
|
|
15
|
+
// TIME clause is chosen by BASELINE_TIME_MODE (Ryan flips one constant):
|
|
16
|
+
// · <Xm> caught — the CUMULATIVE LIFETIME sum of every real MEASURED red→green delta (each caught
|
|
17
|
+
// false "done" and the moment that SAME proof, that SAME session, was later logged PASS — real
|
|
18
|
+
// timestamps; reuses ledger.mjs's measureFixDelta logic). ALWAYS-ON once real catches exist (it is
|
|
19
|
+
// skalpel's terminal presence — never gated/hidden). A measured total, NEVER an estimate; a catch
|
|
20
|
+
// with no later PASS adds 0. Cumulative framing (not present-tense) keeps it honest for old data.
|
|
21
|
+
// · ~Xm saved — Raahil's #609 estimate: (applied steers) × (the user's OWN median loop cost from
|
|
22
|
+
// their causal graph). A `~`-prefixed ESTIMATE grounded in their own data, shown only when
|
|
23
|
+
// saved_min > 0. Never a server wall-clock number; never "skalpel saved you X" as a fact.
|
|
24
|
+
// Every value here is a real count or a real measured timestamp delta (or a clearly `~`-marked estimate).
|
|
25
|
+
// NOTE: this always-on presence line is a DELIBERATE new behavior — origin/main's clean-idle bar emitted
|
|
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.
|
|
28
|
+
// Fail-open: any error → print nothing (or just the presence line), exit 0. Never blocks the bar.
|
|
10
29
|
import { readFileSync, rmSync } from "node:fs";
|
|
11
30
|
import { homedir } from "node:os";
|
|
12
31
|
import { join } from "node:path";
|
|
@@ -19,16 +38,34 @@ const BOLD = "\x1b[1m";
|
|
|
19
38
|
const DIM = "\x1b[2m";
|
|
20
39
|
const RESET = "\x1b[0m";
|
|
21
40
|
|
|
41
|
+
// ── RENDER MODE — the ONE constant Ryan flips. Governs the optional TIME clause on the always-on
|
|
42
|
+
// `skalpel · N steers` baseline. The steers count + Raahil's #609 BOLD/CYAN styling are ALWAYS present,
|
|
43
|
+
// unchanged, in every mode. Nothing else on the bar (reveal / verdict / re-ask / steer label) is affected.
|
|
44
|
+
// "steers" → skalpel · N steers
|
|
45
|
+
// "measured" → skalpel · N steers · 38m caught (DEFAULT — cumulative lifetime MEASURED red→green)
|
|
46
|
+
// "saved" → skalpel · N steers · ~7m saved (Raahil's own-graph ESTIMATE, from steers.ndjson)
|
|
47
|
+
// "both" → skalpel · N steers · 38m caught · ~7m saved
|
|
48
|
+
const BASELINE_TIME_MODE = "measured";
|
|
49
|
+
const SHOW_MEASURED = BASELINE_TIME_MODE === "measured" || BASELINE_TIME_MODE === "both";
|
|
50
|
+
const SHOW_SAVED = BASELINE_TIME_MODE === "saved" || BASELINE_TIME_MODE === "both";
|
|
51
|
+
|
|
22
52
|
// AHA CATCH REVEAL — how long a fresh catch stays on the bar before it auto-clears (so it never
|
|
23
53
|
// wallpapers). verify-shadow.mjs writes the reveal the moment a GENUINE_FAIL mismatch is re-run; it also
|
|
24
54
|
// retracts it if a later proof re-run PASSES. This TTL covers the abandoned case (user moved on).
|
|
25
55
|
const REVEAL_TTL_MS = 90_000;
|
|
26
56
|
// LIVE VERDICT green stamp — the earned "✓ verified" written on a clean PASS. DELIBERATELY short-lived:
|
|
27
57
|
// it flashes right after the claim, then recedes so a stream of greens stays ambient (quiet trust) and
|
|
28
|
-
// never competes with the loud red catch. Short TTL is the whole point — a green that lingers is clutter
|
|
29
|
-
// a green that blinks by, turn after turn, is what makes the rare red land like a thunderclap.
|
|
58
|
+
// never competes with the loud red catch. Short TTL is the whole point — a green that lingers is clutter.
|
|
30
59
|
const VERDICT_TTL_MS = 20_000;
|
|
31
60
|
|
|
61
|
+
// The verify-shadow log the measured clause reads. Defined LOCALLY (not imported from verify-shadow.mjs)
|
|
62
|
+
// on purpose: this runs on EVERY bar render, so it must not pull that module's heavy (child_process /
|
|
63
|
+
// probe) import graph onto the hot path — it only READS the file verify-shadow.mjs owns. verify-shadow.mjs
|
|
64
|
+
// self-trims the log to ~1MB/500 lines, so this bounded tail reads the whole retained log in practice
|
|
65
|
+
// while staying safe against a pathological huge file (a bounded read can only UNDERCOUNT, never overstate).
|
|
66
|
+
const VERIFY_LOG_PATH = join(homedir(), ".skalpel", "verify-shadow.log");
|
|
67
|
+
const SHADOW_READ_BYTES = 1024 * 1024;
|
|
68
|
+
|
|
32
69
|
// Claude Code pipes a JSON status payload on stdin (transcript_path, session_id, …). Read it ONLY when
|
|
33
70
|
// stdin is actually piped — never block on a TTY (manual invocation) where reading fd 0 would hang.
|
|
34
71
|
function readPayload() {
|
|
@@ -101,7 +138,7 @@ function steerLabel() {
|
|
|
101
138
|
// The opt-in AHA CATCH: read the reveal state file verify-shadow.mjs wrote. Returns the plain reveal
|
|
102
139
|
// line (fresh + honest — the statusline never invents it, it only renders what the re-run recorded) or
|
|
103
140
|
// null. Auto-clears when stale (past the TTL) so an old catch can't wallpaper the bar. GATED entirely on
|
|
104
|
-
// SKALPEL_VERIFY_REVEAL: when off, this is never called and the bar is byte-identical to the
|
|
141
|
+
// SKALPEL_VERIFY_REVEAL: when off, this is never called and the bar is byte-identical to the baseline.
|
|
105
142
|
function revealNote() {
|
|
106
143
|
try {
|
|
107
144
|
const r = JSON.parse(readFileSync(REVEAL_PATH, "utf8"));
|
|
@@ -125,7 +162,7 @@ function revealNote() {
|
|
|
125
162
|
// The LIVE VERDICT green stamp: read the verdict state verify-shadow.mjs wrote on a clean PASS. Returns
|
|
126
163
|
// the plain "✓ verified" line (fresh + honest — the statusline never invents it, it only renders what the
|
|
127
164
|
// re-run recorded) or null. Auto-clears past its short TTL so a stale green never lingers. GATED entirely
|
|
128
|
-
// on SKALPEL_VERIFY_REVEAL: when off, this is never called and the bar is byte-identical to the
|
|
165
|
+
// on SKALPEL_VERIFY_REVEAL: when off, this is never called and the bar is byte-identical to the baseline.
|
|
129
166
|
function verdictNote() {
|
|
130
167
|
try {
|
|
131
168
|
const r = JSON.parse(readFileSync(VERDICT_PATH, "utf8"));
|
|
@@ -146,21 +183,148 @@ function verdictNote() {
|
|
|
146
183
|
}
|
|
147
184
|
}
|
|
148
185
|
|
|
149
|
-
|
|
186
|
+
// The always-on presence tally — derived from ~/.skalpel/steers.ndjson, the APPEND-ONLY log the hook
|
|
187
|
+
// writes (one atomic line per APPLIED steer; see the STEERS SOURCE CONTRACT in skalpel-hook.mjs). This is
|
|
188
|
+
// the RACE-FREE replacement for the old mutated state.json counter (audit Finding 2): steers = the count
|
|
189
|
+
// of valid lines, saved_min = Σ saved_min_delta. Defaults to zeros so the baseline line is never blank;
|
|
190
|
+
// garbage/partial lines are skipped; the sum only ever adds ≥0 deltas, so it is honest and never negative.
|
|
191
|
+
function readState() {
|
|
192
|
+
try {
|
|
193
|
+
const raw = readFileSync(join(homedir(), ".skalpel", "steers.ndjson"), "utf8");
|
|
194
|
+
let steers = 0;
|
|
195
|
+
let savedMin = 0;
|
|
196
|
+
for (const line of raw.split("\n")) {
|
|
197
|
+
const l = line.trim();
|
|
198
|
+
if (!l) continue;
|
|
199
|
+
let rec;
|
|
200
|
+
try {
|
|
201
|
+
rec = JSON.parse(l);
|
|
202
|
+
} catch {
|
|
203
|
+
continue; // a partial/garbage line — skip it (fail-open), keep counting the rest
|
|
204
|
+
}
|
|
205
|
+
if (!rec || typeof rec !== "object") continue;
|
|
206
|
+
steers += 1; // one valid line == one applied steer (append-only ⇒ exact, race-free count)
|
|
207
|
+
if (Number.isFinite(rec.saved_min_delta) && rec.saved_min_delta > 0)
|
|
208
|
+
savedMin += rec.saved_min_delta;
|
|
209
|
+
}
|
|
210
|
+
return { steers, savedMin: Math.round(savedMin * 10) / 10 };
|
|
211
|
+
} catch {
|
|
212
|
+
return { steers: 0, savedMin: 0 }; // absent/unreadable → honest zero-state (fail-open)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// The `~`-prefixed saved ESTIMATE, or null when there's no real credit (never show a fake/zero number).
|
|
217
|
+
// Minutes below an hour render as ~Xm; an hour or more as ~Xh (one decimal, trimmed of a trailing .0).
|
|
218
|
+
// [Raahil #609 — preserved verbatim so the "saved"/"both" modes match his line byte-for-byte.]
|
|
219
|
+
function savedPhrase(min) {
|
|
220
|
+
if (!(min > 0)) return null;
|
|
221
|
+
if (min >= 60) {
|
|
222
|
+
const h = min / 60;
|
|
223
|
+
const s = h >= 10 ? String(Math.round(h)) : h.toFixed(1).replace(/\.0$/, "");
|
|
224
|
+
return `~${s}h saved`;
|
|
225
|
+
}
|
|
226
|
+
return `~${Math.round(min)}m saved`;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ── MEASURED red→green delta (real timestamps only — the honest replacement for the banned "~Xh saved").
|
|
230
|
+
// tsMs + measureFixDelta are the EXACT logic from ledger.mjs, replicated here (not imported) so the hot
|
|
231
|
+
// statusline path never pulls verify-shadow.mjs's heavy graph. Parse an ISO ts to epoch ms, or null.
|
|
232
|
+
function tsMs(ts) {
|
|
233
|
+
if (typeof ts !== "string") return null;
|
|
234
|
+
const n = Date.parse(ts);
|
|
235
|
+
return Number.isFinite(n) ? n : null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// measureFixDelta(falseRow, rows) → { ms, atTs } for the EARLIEST later row that is, in the SAME session,
|
|
239
|
+
// the SAME proof_command, and a genuine PASS whose timestamp is strictly after the caught-false claim.
|
|
240
|
+
// That is the real moment the false "done" became a true "done": the exact proof that failed later passed.
|
|
241
|
+
// Returns null when no such row exists (then we show NO time — never an invented minute). [ledger.mjs]
|
|
242
|
+
function measureFixDelta(falseRow, rows) {
|
|
243
|
+
const t0 = tsMs(falseRow.ts);
|
|
244
|
+
if (t0 == null) return null;
|
|
245
|
+
const session = falseRow.session ?? null;
|
|
246
|
+
// Finding 3: a REAL session id is required to tie a fix to a claim. With a null session, the
|
|
247
|
+
// `(r.session ?? null) !== session` guard below would treat every OTHER null-session row as
|
|
248
|
+
// "same session" and cross-attribute a bogus delta — so refuse to measure when session is null.
|
|
249
|
+
if (session == null) return null;
|
|
250
|
+
const proof = falseRow.proof_command || null;
|
|
251
|
+
if (!proof) return null; // can't tie a fix to a claim with no proof — count only
|
|
252
|
+
let best = null;
|
|
253
|
+
for (const r of rows) {
|
|
254
|
+
if (r === falseRow) continue;
|
|
255
|
+
if ((r.session ?? null) !== session) continue;
|
|
256
|
+
if ((r.proof_command || null) !== proof) continue;
|
|
257
|
+
if (r.outcome !== "PASS") continue; // the SAME proof genuinely passing = the real done
|
|
258
|
+
const t1 = tsMs(r.ts);
|
|
259
|
+
if (t1 == null || t1 <= t0) continue; // must be strictly LATER than the false claim
|
|
260
|
+
if (best == null || t1 < best) best = t1;
|
|
261
|
+
}
|
|
262
|
+
return best == null ? null : { ms: best - t0, atTs: new Date(best).toISOString() };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Compact one-line duration for the bar. Input is a real (t_pass − t_false) MEASURED delta; this only
|
|
266
|
+
// formats it. FLOORS every unit so it can never round UP into a number larger than the real measurement.
|
|
267
|
+
// Sub-second deltas → null (nothing worth a clause). Seconds < 1m; whole minutes < 1h; else hours.
|
|
268
|
+
function compactMeasured(ms) {
|
|
269
|
+
if (!Number.isFinite(ms) || ms < 1000) return null;
|
|
270
|
+
const s = Math.floor(ms / 1000);
|
|
271
|
+
if (s < 60) return `${s}s`;
|
|
272
|
+
const m = Math.floor(s / 60);
|
|
273
|
+
if (m < 60) return `${m}m`;
|
|
274
|
+
const h = s / 3600;
|
|
275
|
+
return h >= 10
|
|
276
|
+
? `${Math.floor(h)}h`
|
|
277
|
+
: `${(Math.floor(h * 10) / 10).toFixed(1).replace(/\.0$/, "")}h`;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// measuredCaughtTotalMs() → the CUMULATIVE LIFETIME sum, in ms, of EVERY real measured red→green delta:
|
|
281
|
+
// each caught false "done" (mismatch row) that has a later same-session same-proof PASS contributes that
|
|
282
|
+
// one measured (t_pass − t_false) delta; a catch with no later PASS contributes 0 (never estimated). This
|
|
283
|
+
// is an AGGREGATE — only the total number is ever rendered, never any per-catch claim text or proof (those
|
|
284
|
+
// stay in the opt-in reveal + the analytics TUI). Framed on the bar as a cumulative "Xm caught", NOT a
|
|
285
|
+
// present-tense "just caught", so it stays honest for old catches — the TOTAL genuinely happened.
|
|
286
|
+
//
|
|
287
|
+
// This always-on aggregate counter is an EXPLICIT PRODUCT DECISION by the owner: it is skalpel's terminal
|
|
288
|
+
// presence and must never be gated/hidden ("never remove it from the terminal otherwise skalpel is
|
|
289
|
+
// invisible"). Aggregate-only + no fabrication is what keeps that always-on surface honest.
|
|
290
|
+
function measuredCaughtTotalMs() {
|
|
291
|
+
try {
|
|
292
|
+
const lines = tailLines(VERIFY_LOG_PATH, SHADOW_READ_BYTES);
|
|
293
|
+
if (!lines.length) return 0;
|
|
294
|
+
const rows = [];
|
|
295
|
+
for (const l of lines) {
|
|
296
|
+
try {
|
|
297
|
+
const r = JSON.parse(l);
|
|
298
|
+
if (r && typeof r === "object") rows.push(r);
|
|
299
|
+
} catch {
|
|
300
|
+
/* partial / garbage line — skip it, keep the rest (fail-open) */
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
let total = 0;
|
|
304
|
+
for (const r of rows) {
|
|
305
|
+
if (r.mismatch !== true) continue; // only a caught false "done" (verify-shadow's money metric)
|
|
306
|
+
const d = measureFixDelta(r, rows); // exact ledger.mjs logic; null-session rows excluded (Finding 3)
|
|
307
|
+
if (d && Number.isFinite(d.ms) && d.ms > 0) total += d.ms; // real measured delta only; else +0
|
|
308
|
+
}
|
|
309
|
+
return total;
|
|
310
|
+
} catch {
|
|
311
|
+
return 0; // any error → no measured total (fail-open: never break or slow the bar)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function main() {
|
|
150
316
|
const payload = readPayload();
|
|
151
317
|
|
|
152
|
-
// AHA CATCH REVEAL (opt-in). The
|
|
153
|
-
// re-run, GENUINELY failed
|
|
154
|
-
// the
|
|
318
|
+
// AHA CATCH REVEAL / green ✓ verified (opt-in). The highest-priority surfaces: the agent claimed done
|
|
319
|
+
// and its OWN proof, re-run, GENUINELY failed (red) — or really passed (green). Fully gated on
|
|
320
|
+
// SKALPEL_VERIFY_REVEAL — when off (the default) this branch never runs and the bar is byte-identical to
|
|
321
|
+
// the always-on baseline below.
|
|
155
322
|
if (revealEnabled(process.env)) {
|
|
156
323
|
const reveal = revealNote();
|
|
157
324
|
if (reveal) {
|
|
158
325
|
process.stdout.write(`${BOLD}${reveal}${RESET}`);
|
|
159
326
|
return;
|
|
160
327
|
}
|
|
161
|
-
// No red catch pending → the quiet green half. A fresh PASS stamp shows dim (ambient, not shouting)
|
|
162
|
-
// for a short beat. Lower priority than the red catch, higher than the re-ask count: it's the most
|
|
163
|
-
// recent verdict on the last claim, and it's what earns the trust the red catch spends.
|
|
164
328
|
const verdict = verdictNote();
|
|
165
329
|
if (verdict) {
|
|
166
330
|
process.stdout.write(`${DIM}${GREEN}${verdict}${RESET}`);
|
|
@@ -178,17 +342,40 @@ async function main() {
|
|
|
178
342
|
return;
|
|
179
343
|
}
|
|
180
344
|
|
|
181
|
-
// Otherwise, if a steer is live, name what it's doing — a description, no number.
|
|
345
|
+
// Otherwise, if a steer is live, name what it's doing — a description, no number.
|
|
182
346
|
const label = steerLabel();
|
|
183
347
|
if (label) {
|
|
184
348
|
process.stdout.write(`${BOLD}skalpel${RESET} · ${CYAN}${label}${RESET}`);
|
|
185
349
|
return;
|
|
186
350
|
}
|
|
187
|
-
|
|
351
|
+
|
|
352
|
+
// ALWAYS-ON baseline presence line — skalpel's terminal presence (owner product decision: never gated,
|
|
353
|
+
// never hidden). `N steers` is ALWAYS shown (fresh install reads "0 steers" — honest, present, never
|
|
354
|
+
// blank; the user's own applied-steer count). Then the ONE optional time clause selected by
|
|
355
|
+
// BASELINE_TIME_MODE, each an AGGREGATE number only (never per-catch claim text / proof — those live in
|
|
356
|
+
// the opt-in reveal + the TUI), shown only when its real value exists:
|
|
357
|
+
// • MEASURED "· Xm caught" — the CUMULATIVE LIFETIME sum of every real measured red→green delta
|
|
358
|
+
// (measuredCaughtTotalMs). Always-on once real catches exist; a MEASURED wall-clock, never an
|
|
359
|
+
// estimate. Cumulative framing (not present-tense "just caught"), so it stays honest for old catches.
|
|
360
|
+
// • Raahil's `~`-prefixed saved ESTIMATE ("· ~7m saved") — shown only when saved_min > 0 (its data is
|
|
361
|
+
// the user's own steers.ndjson credit, not the shadow log, so it keeps his always-on #609 behavior).
|
|
362
|
+
const { steers, savedMin } = readState();
|
|
363
|
+
let line = `${BOLD}skalpel${RESET} · ${CYAN}${steers} steers${RESET}`;
|
|
364
|
+
if (SHOW_MEASURED) {
|
|
365
|
+
const caught = compactMeasured(measuredCaughtTotalMs()); // null when < 1s of real measured catches
|
|
366
|
+
if (caught) line += ` · ${CYAN}${caught} caught${RESET}`;
|
|
367
|
+
}
|
|
368
|
+
if (SHOW_SAVED) {
|
|
369
|
+
const phrase = savedPhrase(savedMin);
|
|
370
|
+
if (phrase) line += ` · ${CYAN}${phrase}${RESET}`;
|
|
371
|
+
}
|
|
372
|
+
process.stdout.write(line);
|
|
188
373
|
}
|
|
189
374
|
|
|
190
|
-
// Fail-open
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
375
|
+
// Fail-open: wrap the whole render so a thrown error can never spill a stack trace into the live status
|
|
376
|
+
// bar. Every helper is already individually try/caught; this is the last-line guard. Exit 0 regardless.
|
|
377
|
+
try {
|
|
378
|
+
main();
|
|
379
|
+
} catch {
|
|
380
|
+
/* never let the statusline break or slow the bar */
|
|
381
|
+
}
|