This is a versioned feature-origin document. The tournament gate still feeds the v0.21 current line, but current validation should run the v0.21 smoke, calibration, precheck, and test chain.
v0.18 keeps the v0.17 tournament gate, but hardens the decision quality around two problems found in real-sample calibration:
- variants were sometimes too close for the old composite score to explain why one strategy fit the route better;
- optional LLM review could be recommended without proving what the model call would actually improve.
The result is still local and draft-only. It does not publish Skills, write memory, update prompts, change routes, evolve code, call VPS, or touch live channels.
The stable output contract now lives in
scripts/lib/evolution-tournament-contract.mjs. It freezes the report shape,
score-field names, and authority boundary for review. It does not freeze the
scoring formula or give the tournament any new power.
The tournament now also emits experience_ledger. This is not a memory write.
It is a local shadow ledger for source-backed preflight entries, safe
non-winning variants, and rejected unsafe variants.
Tournament ranking is explicit:
tournament_ranking.rule = deterministic_reducer
tournament_ranking.scorer = deterministic_proxy_v1
tournament_ranking.llm_judge_allowed = false
tournament_ranking.decision_authority = deterministic_qianxuesen_gate_only
An optional LLM reviewer may add critique notes only when the deterministic escalation gate allows it. It cannot rank winners, change routes, or promote a candidate.
The gate now makes restraint explicit instead of assuming that every tournament should move. Each tournament carries an Autoreason-inspired A/B/AB shape, but it stays inside the existing deterministic reducer:
incumbent_unchangedis the first-class do-nothing candidate;revision_candidateis the stronger change pressure;synthesis_candidateis the smaller A+B style compromise;- negative probes still exist only to prove the hard gate rejects unsafe shapes.
convergence_status is an enum, never a free string:
running
incumbent_retained_x1
incumbent_retained_x2
scope_drift_suspected
awaiting_new_evidence
The ledger records consecutive_no_change_count and convergence_status so a
later reducer can see whether the same component is repeatedly saying "hold".
This is observation only. It does not start timers, write memory, or create an
automatic cooldown.
scope_drift_risk is also deterministic. It counts recent tournament-window
pressure from control-footprint fields:
affected_setpoints_unique_count
affected_actuators_unique_count
diff_hash_unique_count
metric_gain_over_initial_baseline
complexity_growth_over_initial_baseline
No model calculates this risk. An optional model may critique the already-built numbers, but it cannot change the score, winner, route, or authority boundary.
The restraint layer also reports metric_gaming_risk. This is a deterministic
advisory guardrail for a simple failure mode: a candidate wins the proxy score
without stronger evidence, holdout, safety, or regression signals. The field
uses registered metric evolution_tournament.metric_gaming_risk, calls no LLM,
has decision_authority=none, and cannot change the winner. It only tells the
next reviewer when a score gain needs more held-out evidence.
The synthesis candidate can beat the revision candidate only when all three checks pass:
restraint(synthesis) <= restraint(revision)
metric(synthesis) >= metric(revision) * (1 - epsilon)
safety(synthesis) >= safety(revision)
epsilon is the registered setpoint
evolution_tournament.synthesis_metric_regression_tolerance, currently 0.03.
Changing it should go through the same slow-loop evidence path as other
setpoints, not an unreviewed scorer tweak.
restraint(candidate) is computed from the variant control footprint:
affected_setpoints.length
+ affected_actuators.length
+ setpoint_delta_l1
The optional review field is named critique_summary, not borda_summary,
because it is human-readable critique only. The deterministic reducer remains
the only winner authority.
The safety no-regression check uses a metric-registry-tagged safety subset:
evolution_tournament.safety_score
evolution_tournament.holdout_score
evolution_tournament.regression_score
Those metrics are marked safety_critical: true in the registry. A synthesis
candidate cannot win by trading any of them away for compactness, novelty, or a
small composite-score gain.
The tournament can optionally include replay-required candidates from
skill:evolution:
npm run evolution:tournament:misa -- --include-skill-evolutionThe bridge is deliberately narrow:
- it accepts only supervisor candidates that are already
replay_required; - it maps them onto an existing Qianxuesen route, usually
skill; - it exposes
agentskills.io-compatible-draftmetadata only; - it sets
tournament_required=trueandcan_promote_now=false; - it does not create
skills/, install skills, publish skills, write memory, or call providers.
Plain version: skill evolution can now hand tournament a better draft candidate, but tournament still only picks a local draft recommendation.
The flat ledger fields are now present on each experience_ledger entry:
iteration_id
change_diff_hash
plant_model_version
metric_registry_version
metric_id
metric_value
decision
reason_ref
timestamp
last_sample_ts
replay_proof
consecutive_no_change_count
convergence_status
plant_model_version and metric_registry_version keep the old score
replayable after the plant model or metric registry grows. last_sample_ts is
reserved for future liveness checks and does not create a new memory system.
consecutive_no_change_count is only a replayable observation for the reducer;
it does not create an automatic cooldown by itself. The count is assigned by the
ledger writer from prior ledger evidence when incumbent_unchanged wins; it is
not accepted from candidate self-report.
replay_proof is the Helix-style evidence anchor adapted to MISA's boundary.
The ledger writer injects it at write time from local git state; candidates do
not get to self-report it. It records repo_commit, worktree_dirty, the JSON
eval/replay command, the schema reference, and the ledger iteration_id.
The command is intentionally the same local dry-run JSON command for eval and replay:
npm run evolution:tournament:misa -- --json
The proof makes that shared command explicit instead of relying on naming:
replay_idempotent=true and replay_writes_ledger=false. Replay means rerun
the deterministic local report for comparison; it must not append a ledger entry
or mutate the world.
If the worktree is dirty, the proof stays honest: the surface is still
local_dry_run_only, human_approval_required=true, can_promote_now=false,
and advisory_only=true. A candidate can win a local tournament, but the ledger
does not say it is promotable.
Losers are not treated as a blacklist or hard filter.
Each non-winning variant now carries a small contrast record:
unsafe: failed a hard gate or requested a blocked operation. Keep it as a strong pressure signal, require L4 review before any similar shape re-enters, and do not let the loser record delete candidates by itself.weak: safe, but materially weaker than the winner. Do not retry without new evidence, a better trace, or changed route pressure; hold it for agent evidence checking rather than throwing it away.promising: safe and close, or useful in a narrower context. Keep it for L4 comparison and future matching.
This keeps the useful learning signal from losers without letting the ledger own route, winner, memory, skill, or production authority.
The ledger now separates two things that used to be mixed together:
failure_type: why the variant lost, such assafety_boundary,quality_inferior,evidence_deficit,context_mismatch,overfit_or_holdout_regression, orcost_or_operational_risk;candidate_pool_effect: what the system should do with that evidence, such as strong review pressure, evidence-before-reentry, or contextual alternative.
This means a similar future candidate can be reviewed against the actual failure reason instead of being treated as "loser-looking" in one big bucket.
Each loser also carries a rehabilitation_record. It does not pardon the loser
automatically. It records what new evidence must exist before pressure can be
lowered or the candidate can re-enter review. Time fields (observed_at,
last_triggered_at, source_count, decay_weight, and confidence) make the
pressure replayable and decay-aware without giving it hard-filter authority.
Every loser entry now states this boundary directly:
candidate_pool_authority = advisory_pressure_only
hard_filter_allowed = false
agent_review_required = true
rehabilitation_record.authority = advisory_reentry_only
Plain version: a loser match raises pressure and asks the agent to check the candidate. It does not become a one-vote veto.
The gate now emits loser_review_context as the place where the later review
loop consumes loser evidence. It lands the remaining Pro-review items without
giving them decision authority:
- local token prototype recall for winner, loser, and rehabilitation examples;
- route-specific loser indexes so one route does not poison another route;
- top-k diversified counterexample packs for L4 instead of repeated loser spam;
- joint winner/loser/rehabilitation recall so "how it was rehabilitated" stays visible beside "why it failed";
- prototype reservoir compression so the loser ledger can keep representatives instead of turning into an endless list;
- deterministic weak-perturbation checks with zero model calls;
- high-dispute strong-review sampling targets with zero model calls by default;
- an L3/L4 consumption plan that turns pressure into review requirements, not candidate filtering.
Plain version: this is the "how to use the loser evidence" packet. It can tell L3 to ask for proof and L4 to compare counterexamples. It still cannot change the winner, route, memory, production state, Zilliz, or public channels.
The deployable profile is shadow_advisory. That profile is intentionally
boring:
top_kis capped at 12 and each counterexample pack is capped at 5;- the reservoir is capped at 24 prototype representatives;
- API, LLM, embedding, Zilliz, VPS, and public-write budgets are all zero;
- unsupported runtime profiles fail closed and make the gate non-releaseable;
- the kill switch is
MISA_LOSER_REVIEW_CONTEXT=0; - rollback does not need data migration because winners, tournaments, and the experience ledger do not depend on the loser context.
Plain version: this can be shipped as a shadow advisory feature. If someone tries to turn it into live hard filtering, the gate blocks that configuration.
npm run loser:pressure and npm run loser:matrix stress-test whether loser
contrast stays useful after many related samples accumulate.
The pressure lane treats any local model as a sample generator only. It does not
let a model judge winners or write memory. The first matrix report recommends
weak_gate_stricter_v1 as the long-run calibration candidate because it improves
weak-loser evidence gating without increasing unsafe leakage, false suppression,
or promising-loser loss.
See docs/current/loser-pressure-quant-v0.26.md for the current report pointers and
decision.
Each variant score now includes strategy_fit.
This is a route/source fit signal, not a new authority layer:
baselinegets more credit for damping, low evidence, and low-risk hold decisions;trace_reflectivegets more credit for policy, case, high-risk, repeated failure, and public-boundary pressure;pareto_compactgets more credit for memory and skill routes when evidence is strong enough to compress safely.
The composite score still includes route preservation, evidence fit,
train/validation/holdout checks, safety, compactness, novelty, and regression
risk. strategy_fit makes the winner explanation more route-sensitive instead
of merely shorter.
judge_escalation now includes llm_review_value.
That object answers the cost question before any model call:
Should an LLM review this?
If yes, what exactly should it critique?
If no, why keep the deterministic path?
Fields:
level:none,low,medium, orhigh;expected_value:none,diagnostic_note_only, orcritique_only;should_change_winner: alwaysfalse;call_policy:do_not_call,deterministic_default_review_optional, orcall_when_auto_enabled;waste_risk:low,medium, orhigh;targets: concrete review targets with evidence and sample counts.
High-value targets can justify one offline LLM call in --judge-mode auto.
Medium-value samples stay deterministic by default and are visible for optional
manual/model review. Low/no-value samples do not spend a call unless a human
forces --judge-mode llm.
Current targets are intentionally narrow:
public_boundary: real public-channel or VPS-sanitized samples where wording, redaction, and no-live-effect boundaries need critique;batch_pattern_review: large historical batches where the useful question is strategy bias, not one winner;damping_vs_compact: very tight damping margins where weak evidence may be over-held or over-compressed;policy_skill_boundary: reusable workflow pressure that may still need to remain policy-bound;policy_memory_boundary: policy pressure that may be over-compressed into memory;close_tiebreak_review: close deterministic winners where only the rationale needs review;winner_strategy_bias: repeated winner monoculture across enough route or sample pressure;rejection_pattern_review: repeated negative shapes that may need a better fixture.
Plain close scores in small single-route samples are not enough to justify a model call by themselves.
The v0.18 calibration used local ignored runs/ artifacts only. It did not touch
VPS and did not push or publish runtime changes.
| Sample group | Tournament decisions | Escalation result |
|---|---|---|
| Default candidate preflight | 3 | deterministic only; llm_review_value=none |
| VPS sanitized conversation sample | 3 | high-value auto review target |
| 30 compound historical summaries | 87 | high-value batch/policy/damping review target |
| 15 atomic historical lessons | 17 | medium-value optional review; deterministic default |
| Six 5-source history chunks | 87 | separates high-value boundary/damping cases from low-value close ties |
| Five atomic route slices | 14 | keeps small single-route samples deterministic unless another high-value target exists |
Aggregate:
sample_groups: 14
tournament_decisions: 211
legacy_pressure_only_recommended: 7
v0.18_auto_recommended: 6
v0.18_optional_review: 2
v0.18_deterministic_only: 6
avoided_low_value_calls: 2
recommended_without_high_value: 0
high_value_not_called: 0
violations: 0
The old pressure-only gate would have recommended review for two low-value close-tie groups. v0.18 suppresses those calls because no concrete high-value review target exists. It also catches high-value damping/boundary cases that sit just below the old numeric threshold.
npm run evolution:tournament:misa
npm run evolution:tournament:misa -- --source-dir runs/history-flowtest-sources
npm run evolution:tournament:misa -- --source-dir runs/history-atomic-flowtest-sources --judge-mode auto
npm run evolution:tournament:misa -- --vps-raw-dir runs/vps-real-conversation-source --judge-mode auto
npm --silent run evolution:tournament:misa -- --json--judge-mode auto calls a reviewer only when
judge_escalation.recommended=true and llm_review_value.level=high.
- every winner remains
local_draft_report_only; experience_ledgeris populated with local comparison evidence;- every loser records a separate
failure_typeand advisorycandidate_pool_effect; - every loser records a
rehabilitation_recordand time/decay metadata before any future pressure change; loser_review_contextincludes route indexes, prototype recall, diversified counterexamples, weak perturbation checks, strong-review sampling targets, and L3/L4 consumption plans;publication_allowed=false;production_authority=false;llm_route_decision_allowed=false;should_change_winner=false;- deterministic scoring remains the decision authority;
- optional LLM review can only add critique notes and suggested local experiments;
- all live effects remain false.
Plain version:
Spend LLM review only where it can critique something specific.
Never let it own the route, winner, or production action.