skalpel prosumer workspace

Intent Graph

Your merged behavioral DAG, read by the causal Profile engine. Twelve lenses run once at ingest over every session's causal edges and measured time. Claude Code and Codex are mined separately, because they fail differently — pool them and you get a profile that describes neither.

Pooled across every agent. Useful for volume; misleading for behaviour — the two agents fail differently.

archetype

thrash

17 of 46 sessions · 9.3h of the waste

● morphology · all agents

rework share

18%

19.1h of 106 active hours · idle-capped at 30min/turn

● headline.rework_pct

E[time to resolve]

22min

at debug/retry_refine, per the all agents profile

● absorbing markov · P(success) 0.71

deep-spiral escape

29%

at depth 5+ · down from 78% at depth 2

● survival · the cliff
aggregate dag · 30d · all agents
plan/success → feature/in_progress 22× · 18% of arrivals end in wasteexplain/success → feature/in_progress 12× · 15% of arrivals end in wastefeature/in_progress → feature/success 41× · 8% of arrivals end in wastefeature/in_progress → debug/retry_refine 24× · 58% of arrivals end in wastedebug/blocked → debug/retry_refine 21× · 62% of arrivals end in wastedebug/blocked → feature/blocked 18× · 55% of arrivals end in wastedebug/blocked → research/abandoned 6× · 83% of arrivals end in wastedebug/retry_refine → refine/retry_refine 32× · 71% of arrivals end in wastedebug/retry_refine → debug/success 28× · 9% of arrivals end in wasterefine/retry_refine → refine/success 26× · 12% of arrivals end in wasterefine/retry_refine → debug/blocked 14× · 64% of arrivals end in wastefeature/blocked → refine/retry_refine 9× · 61% of arrivals end in wastefeature/blocked → feature/success 11× · 20% of arrivals end in wasteplan/successN=41plan/success count 41 · Planning landsexplain/successN=98explain/success count 98 · Explainerfeature/in_progressN=52 · WASTEfeature/in_progress count 52 · Pushing the build E[resolve] 14min · P(success) 0.86 fork swing ±38min (n=52)debug/blockedN=44 · WASTEdebug/blocked count 44 · Debugging hits a wall fork swing ±31min (n=44)debug/retry_refineN=61 · WASTEdebug/retry_refine count 61 · Retrying the same fix E[resolve] 22min · P(success) 0.71 fork swing ±47min (n=61)refine/retry_refineN=73 · WASTErefine/retry_refine count 73 · Correcting the AI E[resolve] 17min · P(success) 0.83 fork swing ±24min (n=73)feature/blockedN=38 · WASTEfeature/blocked count 38 · The build gets stuckresearch/abandonedN=17 · WASTEresearch/abandoned count 17 · Research abandonedfeature/successN=214feature/success count 214 · The build landsdebug/successN=167debug/success count 167 · Bug foundrefine/successN=129refine/success count 129 · Correction lands
transition≥25% ends in waste≥50% ends in wastesink · success
live steer · debug/retry_refineCodex turn · cap 3
FORECAST· FORECAST — from here you're ~27min from resolving, ~58% you get there; you abandon ~42% of these — push, you're closer than it feels.markov[state] · resolve_min ≥ 8
FORK· FORK — decision point: your next move here swings the outcome ~61min. Choose deliberately, don't autopilot.forks[] · swing_min ≥ 20
SINK· SINK — debug work self-loops for you (20× / ~138min lost). If this is your 2nd debug in a row, change approach — don't stack another.sinks[] · loops ≥ 3
TRIGGER· TRIGGER — after 'debug/retry_refine' you tend to spiral into refine (~6min each). If it resolved, move on — don't re-open it.matched, but suppressed by the out[:3] anti-Clippy cap
what the user sees · render_injection()

*🔬 skalpel · you usually lose ~6min re-entering the “correcting the AI without it converging” loop here — routing around it:*

The felt line is a fixed string the model places verbatim. Behind it sits the reasoning block above, drawn from the codex profile. top_sim 0.74 — credited per dodge, confirmed at n+1.

claude code vs codexwhere the pooled profile misleads
metricClaude CodeCodexall agents
Dominant archetypeThe agents have different failure shapes. The pooled archetype describes whichever one you use more, and mislabels the other.over-polishthrashthrash
Rework shareA pooled rate is a weighted average of two populations. It is the rate for no session you have ever had.14%24%18%
Deep-spiral escapeThis is the push-vs-bail signal. Getting it wrong inverts the steer: you tell a grinder to bail and a quitter to push.44%17%29%
Biggest time sinkWhere the hours actually go, per agent — the thing you would act on.refine · 21.4hdebug · 22.5hdebug · 34.2h
Rates are recomputed per source, never averaged — a blended rate describes no session you have ever had.
survival · escape by spiral depththe when-to-fire signal
0%25%50%75%100%78%2n=96depth 2: 78% of runs escape to success (n=96)61%3n=54depth 3: 61% of runs escape to success (n=54)44%4n=28depth 4: 44% of runs escape to success (n=28)29%5+n=17depth 5: 29% of runs escape to success (n=17)ESCAPE %CONSECUTIVE WASTE INTENTS →

Escape probability collapses from 78% at depth 2 to 29% at depth 5+. A steer is worth far more early. This curve is the only thing in the system that can answer when to fire, rather than just what to say — and it is the signal that most disagrees between agents.

absorbing markovE[time-to-resolution] + P(success)
stateE[resolve]P(success)V · all agents
debug/retry_refine22 min0.7111.2m
refine/retry_refine17 min0.837.4m
feature/in_progress14 min0.866.1m
plan/in_progress9 min0.91
Absorbing states (success / blocked / abandoned) carry no forecast, so no FORECAST line fires there. V is a pooled whole-graph value iteration with no per-source decomposition — it stays “all agents” in every scope.
P(success) is what decides “push” vs “take the shortcut” — V alone could never say which.
triggerswhat starts a self-sustaining spiral
after statespirals intonmin eachtotal
debug/blockeddebug12896m
feature/in_progressdebug9868m
debug/retry_refinerefine7641m
debug/successrefine4519m
the antecedent, not the spiral — this is where a steer still costs nothing
sinks · forksself-loops · leverage points

sinks — same-type waste self-loops

debug
34× · 212m
refine
28× · 141m
feature
11× · 63m

forks — outcome swing of the next move

debug/retry_refine
±47m · n 61
feature/in_progress
±38m · n 52
debug/blocked
±31m · n 44
refine/retry_refine
±24m · n 73
bottleneckwall-clock ≠ waste-rate
debug
34.2h · 32% / 28%
feature
31.8h · 30% / 7%
refine
24.8h · 23% / 19%
explain
7.7h · 7% / 3%
research
3.9h · 4% / 56%
plan
3.6h · 3% / 6%

research has the worst waste-rate (56%) but costs only 3.9h. debug wastes proportionally less yet is 32% of the wall-clock. Rate-ranking and hour-ranking disagree — attacking the rate leader would move almost nothing.

variantspareto over whole spiral traces
spiral shapenminutes
debug→refine34198
feature→debug→refine14156
debug22121
refine1988
feature→debug1174
discriminative · early tells of a waste session3-step signatures
signaturehigh-wasteclean
feature→debug→debug91
debug→refine→refine82
explain→explain→feature50
debug→debug→debug40
gate: high ≥ 2 and high ≥ 2×(clean+1) — a real predictor, not a word list
cohortshow good vs bad sessions open

thrash tercile — 41% of time is rework

opens with: debug (14) · refine (9) · feature (6)

clean tercile — 7% of time is rework

opens with: plan (11) · explain (8) · feature (7)

The most actionable line in the profile: clean sessions open with plan; thrash sessions open with debug. It becomes a SessionStart steer, not a mid-turn one. Correlational until the do-calculus layer lands.

resolverswhat breaks a spiral

read · file · revert · smaller · test · logs · scope · print · isolate · diff

Keywords lifted from the goal text of the intent that immediately followed each waste run and resolved it. Honest caveat: the selection of which intents to mine is causal, but the extraction is still bag-of-words (_kw() over a stopword list). This is the one lens that did not fully escape the heuristic it replaced.

still on the old path
state_values · Vpooled · not yet retired
debug/blocked
14.8m · $0.42
debug/retry_refine
11.2m · $0.31
feature/blocked
9.6m · $0.24
refine/retry_refine
7.4m · $0.19
feature/in_progress
6.1m · $0.16
plan/success
2.9m · $0.07

Value iteration over the whole graph, one number per state. The n+1 counter still diffs this V, and render_injection still splices on it. It is computed over all agents pooled and has no per-source form — so a Codex turn is currently credited against a number that includes Claude Code behaviour. Retiring V for the absorbing-Markov table is open work, and it is what fixes this.

recurring transitionscount ≥ 3 · all agents
from → tocountwaste_to
feature/in_progress feature/success418%
debug/retry_refine refine/retry_refine3271%
debug/retry_refine debug/success289%
refine/retry_refine refine/success2612%
feature/in_progress debug/retry_refine2458%
plan/success feature/in_progress2218%
debug/blocked debug/retry_refine2162%
debug/blocked feature/blocked1855%
refine/retry_refine debug/blocked1464%
explain/success feature/in_progress1215%
feature/blocked feature/success1120%
feature/blocked refine/retry_refine961%
debug/blocked research/abandoned683%
// mine.profile_summary(profile) → injected at SessionStart, from the Codex profile

[skalpel — this user's behavioral profile, from 18 sessions (24% of their time is rework). Account for these silently; surface only when one clearly applies this turn.]

· Archetype: their waste is mostly 'thrash' (7.1h).

· Waste tell: the sequence 'feature→debug→debug' shows up in their bad sessions, not clean ones.

· They ABANDON — only 17% escape a deep spiral; nudge them to push, not bail.

· Clean sessions open with: plan, explain, feature — steer them to open that way.

· Time sink: 'debug' is 53% of their wall-clock.

Honest caveats · read before trusting a number on this page

  • The engine does not tag sessions by agent yet. _parse_any() dispatches on is_codex_rollout(path) and then discards the answer; Session has no source field and analyze_session() returns none. Everything this page splits by agent is fixture data. Landing the four-line engine change in docs/INTENT-GRAPH.md is what makes it real.
  • Codex tool fidelity is best-effort. parser_codex.py says so in its own docstring. Any lens that leans on n_tool_errors or step counts is weaker for Codex than for Claude Code, and the two are not comparable on those axes even after the split.
  • The waste vocabulary diverges between the two modules. aggregate_dag._WASTE = {blocked, abandoned, retry_refine, in_progress} but mine._WASTE = {retry_refine, debug, error, abandoned, blocked}. The resolution enum is {success, retry_refine, abandoned, in_progress, blocked, unknown} — so "debug" and "error" can never match a resolution, and in_progress is silently not counted as waste by the profile.
  • Causality is judge-attributed.Every lens rests on the LLM judge's parent attribution of enabled edges. The judge sees a Codex transcript through an adapter, so attribution quality is not guaranteed to be equal across sources.
  • Splitting costs statistical power.Pooled, this corpus is 46 sessions; split, it is 28 and 18. The engine's gates (forks n ≥ 6, markov ≥ 6 transitions) fire less often per source — which is correct, and why a thin source falls back to the pooled profile rather than inventing a finding.
  • Associational until do-calculus lands. Nothing here adjusts for confounders — including the obvious one: which agent you reach for depends on the task. Codex may look worse simply because you open it for the gnarlier bugs.

profile lenses 12scope all agentssteers capped at 3sim floor 0.62mine.py · 341 loc · zero deps