In this commit, we add objective L to the evaluator as a separate
scoring function, not as a change to the default. The scored objective
is untouched and stays untouched: makespan and payment latency stay out
of it for this whole program, by the lead's decision at spec review,
until an experiment earns them a place.
Objective L REPLACES the attempt term with a time term rather than
supplementing it, which is the deepest reading of latency as a cost. The
attempt axis has been doing work it should not: three parallel shards
cost one unit of time and three units of attempt penalty, and a nine hop
route and a two hop route cost the same. exp-019 already retired the
8.6x attempt headline as a perfect-channel artifact, and this is the
question of whether the axis was ever measuring the thing it claimed to.
The weight is calibrated the way the spec asks, against one named arm:
it is chosen so the mean time penalty on the current champion equals the
mean attempt penalty it pays today, and everything else is re-scored
with that weight. A router then does better under objective L only by
being faster than the champion was, not by the term being cheaper for
everybody.
The 1/N rule governs this term as it governs the fee term, and here it
is enforced rather than stated. A term that saturates at or past the 1/6
an abandoned payment costs in the smallest scored file can be paid for
by giving up, which is exp-013. It is easier to break here than with
fees, because the weight is calibrated from data rather than typed: a
reference arm with low latencies produces a large weight, and a large
weight against a generous cap breaks the rule with nobody choosing a
number. check_latency_budget refuses that combination.
A run with no latency section reports no latency, so re-scoring it
raises rather than reading a zero and calling the tier instantaneous.
Objective L re-scores a latency tier's archived output; it does not
convert a tier that never had one.
In this commit, we fix a claim we shipped in a comment two commits ago,
because measuring it made it false.
The exp-023 design spec argues that fee_ppm_attempted escapes the 1/N
rule: the abandoned amount stays in the denominator, so abandonment
cannot improve the ratio, so the fee weight could safely rise once the
term was charged against it. Fixing the denominator only stops
abandonment from shrinking it. The numerator falls all the same, because
a payment nobody completes pays no fee: abandoning a payment that would
have cost f and spent s on partial shards moves the ratio from (F+f)/A to
(F+s)/A, a weak improvement for every payment that pays a fee at all.
fee_ppm_on_success is the partly self-limiting one by comparison. It
improves only when the abandoned payment was dearer than its file's
average, and abandoning a cheap payment makes it worse. So the
substitution the spec proposed as the safe way to raise the weight is, on
this axis, the less safe of the two, and the 1/N rule governs both.
The sealed hard tier says the same thing out loud. Re-scored on the
alternative metric with no re-execution, the lnd arm gains +0.036 of
objective and the seed candidate +0.014, and the arm that gains more is
the arm that abandoned more payments.
What the metric is actually for survives intact, and the comment now says
that instead: it counts money that LEFT THE SENDER, including on payments
that then failed, which is 41% of all fees on the sealed tiers and which
fee_ppm_on_success cannot see at all.
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 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.