Files
learning-hour-maker/ideas/split-loop-with-lambda/exercise
Your Name b993f25c53 Add agent rehearsal harness, findings, and per-run evidence for split-loop-with-lambda
Six scripted pi sub-agent runs (3 tool regimes × 2 models) rehearsed the
exercise; deterministic gate = solution test + approved file. Key results:
mandated CLI triples cost on the strong model (4:06 vs 0:39, PASS both);
weak model fails only under the mandate (A+Q), passes when free or optional.
Drift documented with code: scorer.updateSelection left inside the extracted
method (A+Q, C+Q). Harness in tools/rehearse; summary in
rehearsal-findings.md; per-run evidence in rehearsal-runs/; cheat sheet in
rehearse-with-agent.md corrected (lambda-typed parameters unsupported).
2026-09-27 17:36:19 +00:00
..

Exercise folder — Split Loop with Lambda (learning hour)

Contents:

  • java/ — pre-factored exercise repo (prefactored-longrange; runs with mvn test)
  • solution/ — reference end state after step 3 (extracted method taking the lambda)
  • rehearse-with-agent.md — how Willem rehearses this with qwen-code + java-refactoring-mcp

Reference solution

See solution/. End state of the production code (simplified):

public Map<IceCream, Integer> longRangeForecast() {
    return longRangeForecast(daysForward -> {
        scorer.updateSelection();
        return scorer.lookupWeather(daysForward);
    });
}

public Map<IceCream, Integer> longRangeForecast(
        LongFunction<Boolean> dayOffsetToWeather) {
    var expectedWeather = collectExpectedWeather(dayOffsetToWeather);
    // ... reset loop + scoring loop, unchanged ...
}

private static List<Boolean> collectExpectedWeather(
        LongFunction<Boolean> dayOffsetToWeather) {
    var expectedWeather = new ArrayList<Boolean>();
    for (Instant interestingDate : DATES) {
        long daysForward = ChronoUnit.DAYS.between(ANCHOR_DATE, interestingDate);
        expectedWeather.add(dayOffsetToWeather.apply(daysForward));
    }
    return expectedWeather;
}

Notes on the end state:

  • Key move: scorer is used only inside the lambda; the extracted method collectExpectedWeather has no dependency on Scorer at all — all its inputs are parameters.
  • The public no-arg overload keeps callers unchanged; it injects the "real" weather lambda. The test calls the overload with a pure daysForward -> true instead.
  • The test is pure and glanceable: alwaysSunny weather gives {Strawberry:12, Chocolate:12, Vanilla:17, } (approved file included).
  • scorer.updateSelection() turned out to be deletable (it only mutates static Scorer.flavour state that this method never reads). Leaving it in the lambda is fine for step 3; deleting it is a debrief aha.