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).
92 lines
4.9 KiB
Plaintext
92 lines
4.9 KiB
Plaintext
Done — stopping here as instructed. Summary of how each step was performed:
|
|
|
|
## Steps taken
|
|
|
|
1. **Introduce lambda (manual edit, the allowed exception):** Moved the scorer-dependent code (`scorer.updateSelection()` / `scorer.lookupWeather(...)`) into a `LongFunction<Boolean> dayOffsetToWeather` lambda defined at the top of the method; the loop now calls `dayOffsetToWeather.apply(daysForward)` at the original invocation point. ✅ compiles, same 2 pre-existing test failures.
|
|
|
|
2. **Extract method (via `java-refactor extract`):** Selected the `interestingDates`/`expectedWeather` declarations + loop; produced exactly `private List<Boolean> collectExpectedWeather(LongFunction<Boolean> dayOffsetToWeather)`. ✅ same test result.
|
|
|
|
3. **Signature change (manual fallback):** No `java-refactor` subcommand could do it — I verified with dry-runs:
|
|
- `decompose-conditional` generated broken code (out-of-scope `quarter`)
|
|
- `remove-param` refuses: parameter still referenced
|
|
- `introduce-param` refuses: "Selection is already a simple name"
|
|
- `extract` generated invalid `var` parameter types; `change-method-signature` can't add/remove parameters
|
|
|
|
So per AGENTS.md I made a minimal manual edit: signature → `longRangeForecast(LongFunction<Boolean> dayOffsetToWeather)`, added public no-arg overload wrapping the `scorer` field in a lambda, dropped the `String quarter` parameter (and the dead `if` block that used it — its writes were unconditionally overwritten by the next loop anyway), and updated the test's call site to the no-arg overload (forced by the signature change). ✅ compiles; only the 2 baseline failures remain.
|
|
|
|
## Final diff
|
|
|
|
```diff
|
|
--- a/src/main/java/codingdojo/LongRangeForecasts.java
|
|
+++ b/src/main/java/codingdojo/LongRangeForecasts.java
|
|
import java.time.Instant;
|
|
import java.time.temporal.ChronoUnit;
|
|
import java.util.*;
|
|
+import java.util.function.LongFunction;
|
|
|
|
private final Scorer scorer = new Scorer();
|
|
|
|
- public Map<IceCream, Integer> longRangeForecast(String quarter) {
|
|
+ public Map<IceCream, Integer> longRangeForecast() {
|
|
+ return longRangeForecast(daysForward -> {
|
|
+ scorer.updateSelection();
|
|
+ return scorer.lookupWeather(daysForward);
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public Map<IceCream, Integer> longRangeForecast(LongFunction<Boolean> dayOffsetToWeather) {
|
|
var result = new HashMap<IceCream, Integer>();
|
|
- if (Objects.equals(quarter, "Q1") || Objects.equals(quarter, "Q4")) {
|
|
- for (IceCream flavour : IceCream.values()) {
|
|
- result.put(flavour, 5);
|
|
- }
|
|
- }
|
|
for (IceCream flavour : IceCream.values()) {
|
|
result.put(flavour, 0);
|
|
}
|
|
- List<Instant> interestingDates = List.of(
|
|
- Instant.parse("2023-05-01T14:00:00.000-07:00"),
|
|
- Instant.parse("2023-05-18T14:00:00.000-07:00"),
|
|
- Instant.parse("2023-06-05T14:00:00.000-07:00"),
|
|
- Instant.parse("2023-06-23T14:00:00.000-07:00")
|
|
- );
|
|
- List<Boolean> expectedWeather = new ArrayList<>();
|
|
- for (Instant interestingDate : interestingDates) {
|
|
- scorer.updateSelection();
|
|
- var forecastDate = Instant.parse("2023-04-26T14:00:00.000-07:00");
|
|
- long daysForward = ChronoUnit.DAYS.between(forecastDate, interestingDate);
|
|
- Boolean lookupWeather = scorer.lookupWeather(daysForward);
|
|
- expectedWeather.add(lookupWeather);
|
|
- }
|
|
+ List<Boolean> expectedWeather = collectExpectedWeather(dayOffsetToWeather);
|
|
|
|
for (IceCream flavour : result.keySet()) {
|
|
...
|
|
}
|
|
return result;
|
|
}
|
|
+
|
|
+ private List<Boolean> collectExpectedWeather(LongFunction<Boolean> dayOffsetToWeather) {
|
|
+ List<Instant> interestingDates = List.of(
|
|
+ Instant.parse("2023-05-01T14:00:00.000-07:00"),
|
|
+ Instant.parse("2023-05-18T14:00:00.000-07:00"),
|
|
+ Instant.parse("2023-06-05T14:00:00.000-07:00"),
|
|
+ Instant.parse("2023-06-23T14:00:00.000-07:00")
|
|
+ );
|
|
+ List<Boolean> expectedWeather = new ArrayList<>();
|
|
+ for (Instant interestingDate : interestingDates) {
|
|
+ var forecastDate = Instant.parse("2023-04-26T14:00:00.000-07:00");
|
|
+ long daysForward = ChronoUnit.DAYS.between(forecastDate, interestingDate);
|
|
+ Boolean lookupWeather = dayOffsetToWeather.apply(daysForward);
|
|
+ expectedWeather.add(lookupWeather);
|
|
+ }
|
|
+ return expectedWeather;
|
|
+ }
|
|
|
|
--- a/src/test/java/codingdojo/LongRangeForecastsTest.java (forced call-site update)
|
|
- var actual = new LongRangeForecasts().longRangeForecast(quarter);
|
|
+ var actual = new LongRangeForecasts().longRangeForecast();
|
|
```
|
|
|
|
The scorer dependency is now injectable: `longRangeForecast()` delegates with a lambda wrapping the `scorer` field, and tests can pass a stub `LongFunction<Boolean>` instead.
|