In this commit, we move the objective's arithmetic into one function, put
the design rule that constrains its fee term next to the constants that
would break it, and give both evaluators the sentence about how to read a
falling fee.
The rule has a number in it. A scored file holds 6 to 10 payments, so
abandoning one payment in the smallest file costs 1/6 = 0.167 of
objective, while the entire fee term is worth at most FEE_PPM_CAP *
FEE_WEIGHT = 0.100. The fee term is therefore structurally incapable of
paying for abandonment, by a factor of 1.67, and that margin is the only
thing standing between it and the exp-013 give-up attractor. The rule:
the fee term's maximum value must stay strictly below 1/N, where N is the
payment count of the smallest scored file. Doubling the weight breaks it;
removing the cap breaks it unconditionally.
The safe way to make fees matter more is a different metric rather than a
bigger weight, so the metric is now named: FEE_METRIC is what every
published number was scored with, FEE_METRIC_ATTEMPTED is the alternative
whose denominator abandonment cannot shrink. composite_score takes either,
which is what lets the pre-registered arm re-score archived runs offline
with no re-execution and no change to what the optimizer maximizes.
The hint gains the fee rule in the unconditional style exp-017
established, because a thresholded warning fails here for the reason it
failed there: fees falling is not by itself evidence of anything. Fees
fall for two reasons, cheaper routes and fewer completed payments, and
only the first is an improvement. The code evaluator additionally tells a
candidate that a fee budget exists and where to read it, since a route
refused for cost spends an attempt and teaches nothing.
The scores are unchanged to the last bit, checked against the old inline
formula on the pre-change binary's own output.
In this commit, we replace the give_up_rate threshold warning with an
unconditional read-the-pair rule. exp-017 measured that for candidate
routers give_up_rate equals 1 - success_rate on every tier, since a
candidate "gives up" whenever it returns failure without exhausting
its attempt budget, which is how candidates always fail. A warning
keyed on that field alone fires universally and becomes noise the
reflection model learns to skip. Abandonment is only readable jointly
(attempts AND success both falling), so the hint now states that rule
every time instead of pretending the field can detect it.
In this commit, we lift give_up_rate and bg_settle_rate out of the
aggregate dict into top-level side information, and inject an
explicit warning into the reflection hint whenever a candidate
abandons more than 5% of its payments. exp-013 showed the composite
objective hides giving up on hard payments inside the same number as
attempt efficiency, so a seed at the attempt frontier evolves toward
abandonment; the searcher can only avoid that attractor if it can
see it. This is also one of the two prerequisites the advisor named
for the exp-018 omni adjudication run.
In this commit, we extend the reflection background with the atomic
arena's economics -- held shards reserve real liquidity, siblings
contend, traffic drifts one slice per attempt, and failed payments
release atomically -- so candidates design for the new costs instead
of rediscovering them from failure traces. The exploit grep gains the
new hold-ledger API names (HoldHtlc, SettleHold, ReleaseHold): the
ledger belongs to the runner, and a candidate that touches it is
reaching through the sealed view.
In this commit, we act on the deep audit of our optimize_anything
usage against the library source. The evaluator gains a batch form:
one Go compile per unique candidate instead of one per (candidate,
example) pair, which turns an eight-file valset pass from eight
identical builds into one -- a several-fold wall-clock win the gepa
engine consumes through the batch_evaluator hook, now wired into both
runner paths. Crash paths that could abort an overnight run are
closed: a build timeout under host load and malformed routesim output
now score zero with feedback instead of raising through the engine,
and raise_on_exception is off as a backstop, with
max_candidate_proposals as the enforceable cap now that evaluation
caching makes max_evals count only misses.
The reward-hack guard moves from post-hoc greps into the evaluator
itself: the banned-token regex now covers the hidden-state surfaces
(LocalBalances, AssignLiquidity, BalanceNodeChannels, SendHtlc, and
*routing.SimGraph assertions), verified clean against the seed and
every archived candidate. Fence stripping also slices from the
package clause when an agentic proposer prepends prose. Finally, the
codex harness home moves reflection to high reasoning effort --
medium was an unexamined default for 300-800 line Go rewrites.
In this commit, we act on two independent advisor reviews that
reframed the program: the paradigm ceiling we have been attributing to
algorithm space is partly a measurement ceiling, and the validation
story has holes that would surface immediately upstream.
Measurement: the evaluator now emits separate objective axes
(success, retry efficiency with shards disentangled from retries, and
fee efficiency) so the engine's hybrid Pareto frontier can keep
specialists alive, and evaluation caching is enabled now that the
evaluator is verified deterministic. The split corpus generator gains
--split-leads, replacing the single ambitious payment -- which left
two thirds of every file's score as free probes and quantized
minibatch selection above the very signal being selected for -- with
a descending ladder of mandatory-split payments whose completion count
grades the score. The original --split output is regression-tested
byte-identical.
Validation: sweep_validate.py replaces ad-hoc sweeps with paired
per-file comparisons, bootstrap confidence intervals, and sign tests;
gen_mainnet_scenarios.py generates multi-vantage mainnet corpora with
log-spaced source degrees (2024 down to 2) so claims stop resting on a
single hub-resident vantage; and params_lnd_bimodal.json adds the
baseline arm reviewers will ask for first, since lnd ships a bimodal
estimator that our defaults-only comparisons never exercised. The
exp-010 writeup gains a pre-registered caveat, logged before the live
runs finish, that corpus resolution may mute their verdicts.
In this commit, we add the Python harness that drives GEPA's
optimize_anything over the routesim evaluator. Two modes are supported:
parameter mode tunes the existing heuristic's knobs as a JSON candidate,
while code mode evolves the entire routing algorithm as the full Go
source of candidate_impl.go, compiled per-eval via go build -overlay
with compiler errors fed back to the proposer as reflection signal.
The evaluator scores success rate with small saturating penalties for
retry attempts and fees, and guards against reward hacking by rejecting
candidates that reach for unsafe, reflect, or exec surfaces. The
reflection LM runs through the Codex CLI in headless mode via a small
LM-protocol wrapper, and omni-style two-phase composition (parallel
explore, then a fresh engine seeded with the winner) is available
alongside plain and adaptive runs. The background prompt encodes the
insights discovered by prior champion runs so follow-up evolution builds
on them rather than rediscovering them.