Feeding post-mortem learnings into candidate selection

"how can we feed learnings from the postmortem into things like stock candidate selection?" — 2026-09-03

Part of goal #3, applied to the one place it pays off fastest.

The desk already does this, on the wrong basis

Two findings are live in learning_findings today:

grade:B-              "B- recruits have lost money on average even though their
                       grade said they'd hold up: 5 trades, down 4.8% each"
                       metric_json: {"closed":5, "mean_exit_pct":-4.85,
                                     "win_rate":0.2, "low_power":true}

source:catalyst-scan  "Recruits from catalyst-scan are no longer beating that
                       source's own raw alerts — the vetting has stopped adding
                       an edge there"
                       metric_json: {"scored":33, "avg_excess_pct":-1.07,
                                     "baseline_avg_pct":-0.34, "delta_pct":-0.73}

Both are computed from realised outcomes. Realised is the setup PLUS the person. So neither can separate "B− selection is bad" from "B− trades were mishandled" — and those have opposite fixes: one changes the grader, the other changes the trader.

Mechanical R is the setup alone. That is what this service adds here: not a new loop, a decontaminated basis for the loop that already exists.

The low_power flag and the comparison against that source's own raw alerts are both good instincts already present. Nothing below replaces them.

Four feeds, in order of what they are worth

1. Grade calibration, decontaminated

Does an A-grade produce a better setup than a B? Realised is biased here in a specific direction: conviction attracts size and intervention, so the grades a desk believes in are the ones most likely to be managed — and therefore the ones realised understates. Mechanical R by grade is the honest test.

2. Scanner and source quality, same basis

Is catalyst-scan cold, or is the desk mishandling what it finds? Recompute avg_excess_pct on mechanical R and the question answers itself.

3. Level-kind attribution — a group-by, not a model

plan_json carries reasoned levels with weights, which is more than most desks record:

"stop_level":   {"kind":"prev-high","price":177.6,"label":"yesterday's high","weight":3}
"target_level": {"kind":"round","price":182,"label":"the $182 line","weight":1}

So: do plans stopped at a prev-high resolve better than at a swing-low? Do round-number targets get reached more often than half-dollar ones? That is a GROUP BY level_kind over mechanical outcomes — no model, no fitting, and it feeds level selection directly.

4. Cost-aware selection — probably the biggest

The desk already reasons this way. From plan_change:

"take → stand down: pays 2.0-to-1 once the spread is paid, under the 2-to-1 floor"

But that spread is modeled. This service can measure the real one from tbbo at the decision instant. Measured per liquidity band it supports a much harder statement — names like this do not survive their own spread — which kills candidates before they are taken rather than after they lose.

The receipt that makes this urgent: on 34 BTC rides, $9.22 of an $11.54 loss was spread, not strategy.

Three traps

Survivorship — do not ship a selection finding yet

Grading a selector using only trades that were TAKEN measures the joint filter of selector-plus-human, not the selector. The correction is the cut plans, and those are currently ~51, not the 228 the table suggests.

So every selection statistic available today is biased in the flattering direction. Measuring is fine; publishing one into a decision is not.

Sample size

The live B− finding is 5 trades, and the desk flags it low_power itself. Mechanical R does not create data — it cleans it. Every finding keeps its N visible and a null band, and the report says which claim it is making: "you exited early on this trade" is a fact; "you exit early" needs N.

The ratchet — the non-obvious one

If a finding suppresses B− candidates, the desk stops generating B− data, and the finding can never be revisited. It is self-sealing: the first noisy result permanently closes a door and then removes the evidence that would reopen it.

Any suppression needs a holdout — keep taking a small random slice so the estimate stays alive. A suppression without one is not a decision, it is a one-way door disguised as a decision.

And a fourth, cheaply avoided

A source going cold may be the market, not the source. Six days cannot tell those apart. Always compare against a benchmark over the same window — which, again, the existing finding already does.

The mechanism

Nothing auto-applies:

observe   mechanical R grouped by grade / source / level-kind / liquidity band
  ->
propose   learning_findings, with N, a null band, and the holdout it needs
  ->
GAVEL     alina_gavels — built, 0 rows, never used
  ->
apply     thresholds and suppressions are SETTINGS (reversible, settings_audit).
          the grade FORMULA is code, so a finding there is a proposal for a
          human, never a switch.
  ->
measure   the same ledger scores the weeks after. a finding that did not move
          its own metric is reverted, not quietly kept.

Prerequisites, honestly

cost drag not built — REALISED is currently computed with no costs at all
cut plans ~51 usable, so the survivorship correction is not available yet
attribution no user_id, so "was it the grade or the person" cannot be split per person
holdout policy does not exist, and no suppression should ship without it

Which is why the order in what this is for puts cost drag first: it is the one prerequisite that is fully unblocked and already has its data sitting in the ledger.