chore(training): promote stage-4 retry2 to medium and open stage 5

20260816-2126-gen5-s4-handling-retry2 exhausted its three attempts and
missed only the 0.80 training goal-rate floor, at 0.7731. Every
evaluation gate passed: 65-22-13 versus promoted/easy.json, 87% non-draw
against an 80% floor, 12.6% physical-side imbalance against a 20%
ceiling, and both handling telemetry floors clear. The round improved the
goal rate monotonically across attempts (0.537 -> 0.683 -> 0.773) and the
checkpoint plays well by hand, so close Stage 4 by human override.

Promote it to Game/bots/promoted/medium.json. Medium and Hard both point
at the new policy: Hard stays a label-only duplicate until a stronger one
earns hard.json, which keeps the tiers monotonic rather than leaving Hard
weaker than Medium.

generation5_state.json flips that log entry to "pass" with a
decision_override block preserving the original verdict and reasoning,
and advances to Stage 5 attempt 1. This is what passing_entry() needs to
resolve Stage 5's resume checkpoint and evaluation reference, and what
league_pool() will need at Stage 6; --skip-to-next-stage would advance
the stage without marking anything as passing and die immediately.

generation5.sh now pulls before launching. Each stage ends in
commit_progress()'s push, which fails and kills the run hours in if the
box is behind origin.
This commit is contained in:
Josh Creek
2026-08-17 07:49:21 +01:00
parent 62ff85e75c
commit 0b6679e84b
5 changed files with 65 additions and 15 deletions
File diff suppressed because one or more lines are too long
+10 -6
View File
@@ -11,14 +11,18 @@ extends Control
const BOTS_DIR := "res://bots" const BOTS_DIR := "res://bots"
# All three tiers deliberately point at the same promoted checkpoint at its # Every tier runs its promoted checkpoint at full trained capability —
# full trained capability. The labels are placeholders until genuinely # difficulty is a genuinely different policy, never the same policy
# stronger models are promoted as medium.json/hard.json; difficulty is not # handicapped with reaction delay or action noise. Easy and Medium are now
# simulated with reaction delay or action noise in the meantime. # distinct models (medium.json beats easy.json 65-22-13 head-to-head); Hard
# still points at medium.json, the strongest promoted policy, and stays a
# label-only duplicate until a stronger one earns hard.json. Keep the tiers
# monotonic: never leave a lower tier pointing at a stronger model than the
# one above it.
const DIFFICULTIES := [ const DIFFICULTIES := [
{"name": "Easy", "model": "res://bots/promoted/easy.json", "reaction_ticks": 8, "action_noise": 0.0}, {"name": "Easy", "model": "res://bots/promoted/easy.json", "reaction_ticks": 8, "action_noise": 0.0},
{"name": "Medium", "model": "res://bots/promoted/easy.json", "reaction_ticks": 8, "action_noise": 0.0}, {"name": "Medium", "model": "res://bots/promoted/medium.json", "reaction_ticks": 8, "action_noise": 0.0},
{"name": "Hard", "model": "res://bots/promoted/easy.json", "reaction_ticks": 8, "action_noise": 0.0}, {"name": "Hard", "model": "res://bots/promoted/medium.json", "reaction_ticks": 8, "action_noise": 0.0},
] ]
@onready var difficulty_dropdown: OptionButton = %DifficultyDropdown @onready var difficulty_dropdown: OptionButton = %DifficultyDropdown
+35 -5
View File
@@ -164,7 +164,14 @@ flat files there, never touching subdirectories.
actually referenced by the shipped game — currently `easy.json` (promoted actually referenced by the shipped game — currently `easy.json` (promoted
2026-08-08 from generation 4's `20260806-1939-curric-s3-gauntlet`; this is 2026-08-08 from generation 4's `20260806-1939-curric-s3-gauntlet`; this is
the 320M-step MultiDiscrete policy and the foundation for the planned the 320M-step MultiDiscrete policy and the foundation for the planned
generation-5 curriculum below) and `reference-grounded.json` (added for generation-5 curriculum below), `medium.json` (promoted 2026-08-17 from
generation 5's `20260816-2126-gen5-s4-handling-retry2` — Stage 4 attempt 3,
which the pipeline recorded as a *fail* on the 80% training-goal-rate gate at
0.7731, but which beats `easy.json` 65-22-13 in the 100-episode paired
evaluation, 87% non-draw and 12.6% physical-side imbalance, both inside the
Stage-4 bars; promoted by hand on gameplay feel, so its recorded
`decision: "fail"` in `generation5_state.json` is expected and not a
bookkeeping error) and `reference-grounded.json` (added for
generation 4 — a copy of generation 3's `curric-s5-aggression`, made before generation 4 — a copy of generation 3's `curric-s5-aggression`, made before
the flat `Game/bots/` dump was scrapped for the redesign, kept as the the flat `Game/bots/` dump was scrapped for the redesign, kept as the
strongest grounded-era artifact and the fixed yardstick generations 1-3 were strongest grounded-era artifact and the fixed yardstick generations 1-3 were
@@ -174,10 +181,12 @@ promoted file is never touched by training scripts, never overwritten by a
same-named future export, and never disturbed by pruning old experiment same-named future export, and never disturbed by pruning old experiment
files from the flat dump. files from the flat dump.
Until distinct `medium.json` and `hard.json` policies earn promotion, the Easy and Medium are now genuinely different policies. Hard still points at
three menu tiers all run this same `easy.json` policy at its full trained `medium.json` and remains a label-only duplicate until a stronger policy earns
cadence (`reaction_ticks=8`, `action_noise=0`). The tiers are labels only; `hard.json`. Every tier runs at full trained cadence (`reaction_ticks=8`,
the game does not manufacture difficulty gaps by handicapping this model. `action_noise=0`) — the game does not manufacture difficulty gaps by
handicapping a model. When promoting, keep the tiers monotonic: a lower tier
must never point at a policy that beats the tier above it.
To promote a new bot into a tier: copy the chosen `Game/bots/<experiment>.json` To promote a new bot into a tier: copy the chosen `Game/bots/<experiment>.json`
to `Game/bots/promoted/<tier>.json` (overwriting the old one), and note the to `Game/bots/promoted/<tier>.json` (overwriting the old one), and note the
@@ -609,6 +618,27 @@ that fades to zero by 3m altitude. Its scoring gates deliberately run before
Stage 5: becoming upright is not progress if the resulting policy stops Stage 5: becoming upright is not progress if the resulting policy stops
finishing goals. Stage 5 adds moving high-ball intercept finishing goals. Stage 5 adds moving high-ball intercept
starts aimed toward real goals rather than a standalone air-touch reward. starts aimed toward real goals rather than a standalone air-touch reward.
**Stage 4 was closed by human override on 2026-08-17**, not by the automatic
gate. `20260816-2126-gen5-s4-handling-retry2` exhausted all three attempts and
the pipeline recorded `decision: "fail"`, on the training goal-rate floor alone
(0.7731 vs 0.80). Every other gate passed — 65-22-13 versus
`promoted/easy.json`, 87% non-draw against the 80% floor, 12.6% physical-side
imbalance against the 20% ceiling, and both handling telemetry floors clear
(`upright_fraction` 0.772 vs 0.45, `forward_motion_fraction` 0.315 vs 0.25) —
and the round's three attempts improved the training goal rate monotonically
(0.537 → 0.683 → 0.773). The same checkpoint also plays well enough by hand to
have been promoted to `medium.json`. `generation5_state.json` therefore has
that log entry's `decision` flipped to `"pass"` with a `decision_override`
block recording the original verdict and reasoning, and `stage_index`/
`attempt`/`status` advanced to Stage 5 attempt 1 — which is what
`passing_entry()` needs to resolve Stage 5's resume checkpoint and evaluation
reference, and what `league_pool()` will later need at Stage 6. Prefer this
edit over `--skip-to-next-stage`: that flag advances `stage_index` without
marking anything as passing, so the run dies immediately with `RuntimeError:
No passing generation-5 stage index 0`. Note that retry2 already clears Stage
5's own 0.75 goal-rate floor; the 0.80 Stage-4 figure was always the stricter
of the two.
Stage 6's `league` opponent mode samples a historical exported policy at each Stage 6's `league` opponent mode samples a historical exported policy at each
episode reset. Each later stage preserves the preceding shaping and adds one episode reset. Each later stage preserves the preceding shaping and adds one
new difficulty. new difficulty.
+7
View File
@@ -14,6 +14,13 @@ if tmux has-session -t "$SESSION" 2>/dev/null; then
exec tmux attach -t "$SESSION" exec tmux attach -t "$SESSION"
fi fi
# Stage start and resume point both come from committed state, and each stage
# ends in commit_progress()'s `git push` — which fails, killing the run after
# hours of training, if the box is behind origin. Pull before starting, same as
# next_run.sh. Aborts here (set -e) on a dirty tree or a conflict, which is the
# point: fix it before spending the compute, not after.
git pull --rebase
tmux new-session -d -s "$SESSION" -n curriculum \ tmux new-session -d -s "$SESSION" -n curriculum \
".venv/bin/python generation5.py $*; echo; echo '=== generation5.py exited — press Enter to close ==='; read" ".venv/bin/python generation5.py $*; echo; echo '=== generation5.py exited — press Enter to close ==='; read"
+12 -4
View File
@@ -1,7 +1,7 @@
{ {
"stage_index": 0, "stage_index": 1,
"attempt": 2, "attempt": 0,
"status": "blocked", "status": "in_progress",
"log": [ "log": [
{ {
"stage_index": 0, "stage_index": 0,
@@ -249,7 +249,15 @@
"win_rate_a": 0.65 "win_rate_a": 0.65
} }
], ],
"decision": "fail" "decision": "pass",
"decision_override": {
"date": "2026-08-17",
"original_decision": "fail",
"original_failures": [
"rollout/goal_rate=0.7731 < 0.8000"
],
"reason": "Human override. Only miss was the training goal-rate floor, at 0.7731 vs 0.80; every evaluation gate passed (65-22-13 vs promoted/easy.json, 87% non-draw, 12.6% physical-side imbalance) and the goal rate improved monotonically across all three attempts (0.537 -> 0.683 -> 0.773). Also promoted to Game/bots/promoted/medium.json on the same evidence. Stage 5 resumes from this checkpoint."
}
} }
] ]
} }