Skip to content

Site rig follow-ups after stable: random-run judge (#1920) #1941

Open

Description

@frahlg

Follow-ups from the final review of #1920 (head 652df2c) on 28 Oct 2026. None of them makes the PR worse than master or breaks the owner's fuse rule, so they wait until after stable.

  • Random judge: a missing battery order at the start of the scan reads as 0 W
  • Random judge: a battery raise in steps under 345 W is not counted
  • Random judge: mutation survivors around batMovedIn
  • Random judge: relief withdrawn while over the fuse counts as FTW's own

Random judge: a missing battery order at the start of the scan reads as 0 W

652df2c keeps the last order while status.dispatch is missing, but only once batMovedIn has seen an order. The scan starts at a-5. If that sample already has no dispatch, prev becomes 0, and Core's unchanged order on its return counts as a raise.

Evidence: in seed 207539 the dispatch is missing from 113.7 s, which is 7 s into the inverter outage (106.7 s) and 6 s before ftw:restart (119.7 s). The restart window's scan starts at 116.7 s, inside that gap. 207539 discharged, so nothing flipped. A synthetic run in the same shape with a charging battery fails on head as FTW's own:

  • 3 kW order, inverter offline 40-70 s, restart 50 s, up 58 s, the same 3 kW back at 72 s, L1 21 A on 16 A from 65 or 75 s. Judged [75 100 75 78 own] and [65 100 65 68 own].
  • The base judge (test(site): the rig expects the room the fuse rating gives #1908) passes this case, because the restart minute runs to 110 s.
  • A 60 s meter_late that opens 4 s into Core's down time has the same flaw (judged [108 112 108 111 own]).

Fix: carry the last known order from before a-5, for example by dropping the if r.T < a-5 { continue } skip so the scan starts at the beginning of the run. With that change every TestInvariants*/TestJudge* test passes, and the 10 saved seeds of 83c2b24 judge exactly as before (same over_limit_judged).

Test idea: the 207539 shape above (charging battery, a restart inside an inverter outage, the same order restored) must pass. The mutations skip-all-missing and scan-from-start survive the current suite.

Random judge: a battery raise in steps under 345 W is not counted

batMovedIn compares each order with the one before it. A raise made of several steps, each under 0.5 A a phase, never counts.

Evidence: Core restarts at 50 s and ramps the charge from 0 to 4 kW in 300 W steps. L1 then sits at 1.31 x In (21 A on 16 A) from 70 to 105 s. Head judges this [70 105 70 110 step] and passes it under the restart's minute. With 400 W steps it fails as own ([70 105 70 73 own]). The previous head (4985523, 100 W threshold) caught the 300 W ramp; the base judge did not, so this is not worse than master.

The saved seeds show that Core does move in small steps. Scanning all 10 seeds finds same-direction runs of steps under 345 W that add up to 350-665 W within 2-10 s (for example 207534 +665 W from 259.7 s, 207538 -644 W from 562.7 s). A cumulative rule therefore needs a margin above that normal creep.

Test idea: the 300 W ramp after a restart must fail as own. A creep of +665 W in 2-10 s must not change a verdict.

Random judge: mutation survivors around batMovedIn

These mutations of go/test/site/random_test.go at 652df2c survive TestInvariants|TestJudge:

  • a move threshold of 200 W or 300 W instead of 345 W. Only 158 W (passes) and 360 W (fails) are pinned;
  • dropping r.T > a, so moves before the window's meter delay count;
  • counting a battery move toward export (more discharge, the right answer) before an import stretch as FTW's own (batMovedIn(a, s, false, 1) || batMovedIn(a, s, false, -1)).

These mutations are caught: missing order read as 0, a 500 W threshold, each export term, the export direction, the ungated in-stretch export term, and the car check before an import stretch.

Test ideas:

  • a 330 W one-sample move passes and a 350 W move fails;
  • a battery move in (a-5, a] does not count;
  • after a load step, a battery that discharges 4 kW more before the import stretch keeps the step's time.

Random judge: relief withdrawn while over the fuse counts as FTW's own

The in-stretch term counts any battery move of 345 W or more toward import while the meter shows the fuse passed. That includes moves Core may have to make: the battery reaching its floor of max(5 %, soc_min), or a slew anchored on a lagging battery reading.

Evidence: seed 207537 at 32.6-37.6 s. A car starts on the charger's 11 040 W default, with L2 at 18.9 A on 16 A. Core orders -3 037 W, then -37 W at 35.6 s while L2 is still over the fuse. The heat stayed cool, so the judge did not put this on the clock. A synthetic run held over the fuse in that shape is judged [32 42 32 35 own]. Without the move, it would be a step with the charger's time.

This existed before 652df2c (with a 100 W threshold) and in the base judge's 'own' rule. The owner should decide whether withdrawing relief at the floor is a step or FTW's own.

Test idea: a battery at its floor that stops discharging during a car step. Pin whichever verdict the owner chooses.

🤖 Generated with Claude Code

Activity

  1. frahlg commented on Oct 28, 2026

    @frahlg
    MemberAuthor

    One more item from 959cae3 (owner decisions of 28 Oct now in #1920): events can still chain a stretch over 1.2 × In until the 60 s heat passes In, about 50 s at 1.25 × In. The owner rule says over 1.2 × In is an instant cut, so the judge should give an outside step at most its lever's time above 1.2 × In, however many events follow. Test idea: 20 A on a 16 A fuse with a load event every 8 s; fail after the lever's time, not after the heat crosses In.

    🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions