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"
# All three tiers deliberately point at the same promoted checkpoint at its
# full trained capability. The labels are placeholders until genuinely
# stronger models are promoted as medium.json/hard.json; difficulty is not
# simulated with reaction delay or action noise in the meantime.
# Every tier runs its promoted checkpoint at full trained capability —
# difficulty is a genuinely different policy, never the same policy
# handicapped with reaction delay or action noise. Easy and Medium are now
# 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 := [
{"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": "Hard", "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/medium.json", "reaction_ticks": 8, "action_noise": 0.0},
]
@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
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
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
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
@@ -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
files from the flat dump.
Until distinct `medium.json` and `hard.json` policies earn promotion, the
three menu tiers all run this same `easy.json` policy at its full trained
cadence (`reaction_ticks=8`, `action_noise=0`). The tiers are labels only;
the game does not manufacture difficulty gaps by handicapping this model.
Easy and Medium are now genuinely different policies. Hard still points at
`medium.json` and remains a label-only duplicate until a stronger policy earns
`hard.json`. Every tier runs at full trained cadence (`reaction_ticks=8`,
`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 `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
finishing goals. Stage 5 adds moving high-ball intercept
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
episode reset. Each later stage preserves the preceding shaping and adds one
new difficulty.
+7
View File
@@ -14,6 +14,13 @@ if tmux has-session -t "$SESSION" 2>/dev/null; then
exec tmux attach -t "$SESSION"
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 \
".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,
"attempt": 2,
"status": "blocked",
"stage_index": 1,
"attempt": 0,
"status": "in_progress",
"log": [
{
"stage_index": 0,
@@ -249,7 +249,15 @@
"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."
}
}
]
}