Skip to content
Amendments are welcomed

Chapter 15 Companion: Code, Explanations and Solutions

Code explanations and solutions for Chapter 15 Companion: Code, Explanations and Solutions.

AI as a Software Engineering Assistant
OO Development and Agile Practices

1. Scope and execution

This file accompanies Chapter 15, AI as a Software Engineering Assistant. It contains the lab, a small stand-alone Maven project for the chapter’s worked example; the two constructed AI attempts and the work orders that produced them; the AI assistance record, its template and a script that checks it; three further constructed outputs for the chapter’s other activities; the model solutions; the experiments behind the chapter’s figures; and teaching notes.

The lending project is not touched. The lab does not depend on it, and its handover version 0.14.0 stays as Chapter 14 left it. The lab holds its own copy of LoanPeriod. Chapter 15 therefore has no starting version and no carry-forward to a later chapter. Because the code is not part of the lending project, the dependency rules, the handover check and the requirements table of Chapter 14 are unaffected.

All AI output in this chapter is constructed. Every attempt, chat response and explanation is author-written and labelled, in its own file, as constructed to illustrate. None is a recording of a real model. No live assistant was used to produce or check anything here.

1.1 Reconstruct the lab

  1. Create the directory lab.
  2. Save every listing in Sections 3 to 5 of this file at its path under lab.

Use a full Java 17 JDK and Maven 3.9.11. The pinned versions are those of the earlier chapters (JUnit Jupiter 5.13.4, Compiler Plugin 3.14.1, Surefire 3.5.4). Initial builds need dependency access. Then, from inside lab:

mvn --batch-mode test
bash scripts/try-attempt.sh 1
bash scripts/try-attempt.sh 2 generated
bash scripts/try-attempt.sh 2 adapted
bash scripts/check-record.sh

1.2 Expected output

mvn --batch-mode test ends with Tests run: 9, Failures: 0, Errors: 0, Skipped: 0 and BUILD SUCCESS. The attempts are run in a scratch copy of the lab, which is deleted afterwards:

CommandExpected
try-attempt.sh 1BUILD FAILURE with two cannot find symbol errors, at AvailabilityFinder.java line 18 and line 20
try-attempt.sh 2 generatedTests run: 7, Failures: 0, BUILD SUCCESS
try-attempt.sh 2 (acceptance)BUILD FAILURE: reference to AvailabilityFinder is ambiguous in AvailabilityFinderTest
try-attempt.sh 2 adaptedTests run: 9, Failures: 2: ex4_... and ex5_...
check-record.shchecked docs/AI-RECORD-earliest-start.md and RECORD CHECK PASSED
check-record.sh where only the template existsFAIL no assistance record to check..., RECORD CHECK FAILED: nothing was checked, exit status 1

(Maven and the build print WARNING lines on a recent JDK. They are not errors.)

1.3 Compile attempt 1 by hand

mkdir -p scratch && cp src/main/java/assist/LoanPeriod.java attempts/attempt1/AvailabilityFinder.java scratch/
cd scratch
javac --release 17 -d out2 *.java     # the project's baseline: two errors (intersects and getFirst)
javac -d out1 *.java                  # optional, and only meaningful on a JDK of version 21 or later:
                                      # one error (intersects). On a JDK 17 both commands report both errors

2. Design map

2.1 What is in the lab

PathPurpose
pom.xmlMaven build, Java 17 (maven.compiler.release), JUnit Jupiter 5.13.4, Surefire with failIfNoTests
README.mdWhat the lab is and how to run it
src/main/java/assist/LoanPeriod.javaA copy of the lending project’s half-open period
src/main/java/assist/AvailabilityFinder.javaThe accepted version, written by the developer
src/test/java/assist/AvailabilityFinderTest.javaThe nine acceptance examples EX1 to EX9, written before any prompt
scripts/try-attempt.shTries a constructed attempt in a scratch copy
scripts/check-record.shChecks that an AI assistance record has every heading and none is empty
prompts/prompt1.md, prompts/prompt2.mdThe two work orders
attempts/attempt1/*, attempts/attempt2/*The constructed AI output, outside the build
docs/AI-RECORD-TEMPLATE.md, docs/AI-RECORD-earliest-start.mdThe record template and the filled record
fixtures/*Three further constructed outputs: an explanation, a prompt to send and not to send, a dependency suggestion

2.2 The task, stated once

Given the periods for which an item is already booked, return the earliest start date, on or after from, among searchDays candidate start dates beginning with from itself, on which the item could be reserved for days days. Periods are half-open. Nothing is reserved. Invalid days or searchDays (below one) is an IllegalArgumentException; a null from or bookings list is a NullPointerException.

The acceptance examples, with the bookings 20–22 and 24–26 October unless stated:

IDRequestResult
EX1from 20, 2 days, window 1022 October
EX2from 20, 3 days, window 1026 October
EX3from 10, 2 days, window 10; and no bookings, from 10, 5 days, window 110 October
EX4from 20, 2 days, window 2none
EX5from 20, 2 days, window 322 October; and window 2 gives none
EX6booking 18–20 only; from 20, 2 days, window 120 October
EX7days 0, window 0, a null from, a null listthe exceptions above
EX8the caller’s list gains a booking after constructionthe finder is unchanged
EX9one booking covering 1–31 October; from 5, 1 day, window 10none

3. The lab: build and code listings

3.1 lab/pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>edu.se506</groupId>
  <artifactId>assist-lab</artifactId>
  <version>1.0.0</version>
  <properties>
    <maven.compiler.release>17</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>5.13.4</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.14.1</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.5.4</version>
        <configuration><failIfNoTests>true</failIfNoTests></configuration>
      </plugin>
    </plugins>
  </build>
</project>

3.2 lab/README.md

# Chapter 15 lab: earliest available start date

A small stand-alone project for the AI-assistance chapter. It is **not** part of the lending project and changes nothing in it. It holds a tiny task, the acceptance examples for it, two constructed AI attempts that are rejected, and the record of what happened.

Use a full Java 17 JDK and Maven 3.9.11.

```bash
mvn --batch-mode test                    # the accepted version and its nine acceptance examples
bash scripts/try-attempt.sh 1            # attempt 1 in a scratch copy: does not compile
bash scripts/try-attempt.sh 2 generated  # attempt 2 with its own generated tests: they pass
bash scripts/try-attempt.sh 2 adapted    # attempt 2 with the acceptance examples: two fail
bash scripts/check-record.sh             # the AI assistance record has every heading
```

`attempts/` holds the constructed AI output and is not part of the build. `prompts/` holds the two work orders. `fixtures/` holds three more constructed outputs for the chapter's other activities. `docs/` holds the record template and the filled record.

3.3 lab/src/main/java/assist/LoanPeriod.java

package assist;

import java.time.LocalDate;
import java.util.Objects;

/** A half-open period of days. This is a copy of the lending project's LoanPeriod, so that the lab stands alone. */
public final class LoanPeriod {
    private final LocalDate start;
    private final LocalDate endExclusive;

    public LoanPeriod(LocalDate start, LocalDate endExclusive) {
        this.start = Objects.requireNonNull(start, "start");
        this.endExclusive = Objects.requireNonNull(endExclusive, "endExclusive");
        if (!start.isBefore(endExclusive)) {
            throw new IllegalArgumentException("End must be after start.");
        }
    }

    public LocalDate start() {
        return start;
    }

    public LocalDate endExclusive() {
        return endExclusive;
    }

    public boolean overlaps(LoanPeriod other) {
        Objects.requireNonNull(other, "other");
        return start.isBefore(other.endExclusive)
                && other.start.isBefore(endExclusive);
    }
}

3.4 lab/src/main/java/assist/AvailabilityFinder.java

package assist;

import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * Finds the earliest start date on which an item can be reserved for a number of days, given the
 * periods for which it is already booked. The search examines searchDays candidate start dates,
 * beginning with from itself. Periods are half-open: a booking that ends on a date does not block a
 * new one that starts on that date. Nothing is reserved and the bookings are not changed.
 */
public final class AvailabilityFinder {
    private final List<LoanPeriod> booked;

    public AvailabilityFinder(List<LoanPeriod> booked) {
        this.booked = List.copyOf(Objects.requireNonNull(booked, "booked"));
    }

    public Optional<LocalDate> earliestStart(LocalDate from, int days, int searchDays) {
        Objects.requireNonNull(from, "from");
        if (days < 1) {
            throw new IllegalArgumentException("days must be at least 1");
        }
        if (searchDays < 1) {
            throw new IllegalArgumentException("searchDays must be at least 1");
        }
        for (int offset = 0; offset < searchDays; offset++) {
            LocalDate start = from.plusDays(offset);
            LoanPeriod wanted = new LoanPeriod(start, start.plusDays(days));
            if (booked.stream().noneMatch(wanted::overlaps)) {
                return Optional.of(start);
            }
        }
        return Optional.empty();
    }
}

3.5 lab/src/test/java/assist/AvailabilityFinderTest.java

package assist;

import static org.junit.jupiter.api.Assertions.*;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.Test;

/** The acceptance examples EX1 to EX9, written from the requirement before any code was requested. */
class AvailabilityFinderTest {
    private static LocalDate oct(int day) { return LocalDate.of(2026, 10, day); }
    private static LoanPeriod period(int from, int endExclusive) { return new LoanPeriod(oct(from), oct(endExclusive)); }

    /** CAM1 is booked for [20, 22) and [24, 26). */
    private static AvailabilityFinder cam1() { return new AvailabilityFinder(List.of(period(20, 22), period(24, 26))); }

    @Test void ex1_aBookingThatEndsOnADateDoesNotBlockAStartOnThatDate() {
        assertEquals(Optional.of(oct(22)), cam1().earliestStart(oct(20), 2, 10));
    }

    @Test void ex2_aLongerStayMustFitBeforeTheNextBooking() {
        assertEquals(Optional.of(oct(26)), cam1().earliestStart(oct(20), 3, 10));
    }

    @Test void ex3_aFreeStartDateIsReturnedImmediately() {
        assertEquals(Optional.of(oct(10)), cam1().earliestStart(oct(10), 2, 10));
        assertEquals(Optional.of(oct(10)), new AvailabilityFinder(List.of()).earliestStart(oct(10), 5, 1));
    }

    @Test void ex4_theWindowCountsCandidateStartDatesAndNotDaysAfterFrom() {
        assertEquals(Optional.empty(), cam1().earliestStart(oct(20), 2, 2));
    }

    @Test void ex5_theLastCandidateInTheWindowIsIncluded() {
        assertEquals(Optional.of(oct(22)), cam1().earliestStart(oct(20), 2, 3));
        assertEquals(Optional.empty(), cam1().earliestStart(oct(20), 2, 2));
    }

    @Test void ex6_aBookingThatEndsOnFromDoesNotBlockFrom() {
        var finder = new AvailabilityFinder(List.of(period(18, 20)));
        assertEquals(Optional.of(oct(20)), finder.earliestStart(oct(20), 2, 1));
    }

    @Test void ex7_invalidArgumentsAreRejected() {
        var finder = cam1();
        assertThrows(IllegalArgumentException.class, () -> finder.earliestStart(oct(20), 0, 5));
        assertThrows(IllegalArgumentException.class, () -> finder.earliestStart(oct(20), 2, 0));
        assertThrows(NullPointerException.class, () -> finder.earliestStart(null, 2, 5));
        assertThrows(NullPointerException.class, () -> new AvailabilityFinder(null));
    }

    @Test void ex8_theCallersListIsCopiedAndNothingIsChanged() {
        List<LoanPeriod> callers = new ArrayList<>(List.of(period(20, 22)));
        var finder = new AvailabilityFinder(callers);
        callers.add(period(22, 24));
        assertEquals(Optional.of(oct(22)), finder.earliestStart(oct(20), 2, 10));
        assertEquals(2, callers.size());
    }

    @Test void ex9_everyCandidateBlockedMeansEmpty() {
        var finder = new AvailabilityFinder(List.of(period(1, 31)));
        assertEquals(Optional.empty(), finder.earliestStart(oct(5), 1, 10));
    }
}

3.6 lab/scripts/try-attempt.sh

#!/usr/bin/env bash
# Tries one of the constructed AI attempts in a scratch copy of the lab: it replaces the accepted
# AvailabilityFinder with the attempt, compiles for Java 17 and runs the tests. The lab itself is not
# changed. Usage: bash scripts/try-attempt.sh 1|2 [generated|acceptance|adapted|both]
#   generated   run the attempt's own generated tests (attempt 2 only)
#   acceptance  run the acceptance tests EX1 to EX9 (default)
#   both        run both sets
#   adapted     the acceptance tests with one change to a test line, `new AvailabilityFinder(null)`
#               becomes `new AvailabilityFinder((List<LoanPeriod>) null)`, for an attempt whose extra
#               constructor makes the original line ambiguous (attempt 2). The change is to the scratch
#               copy only and does not alter what the test asserts.
# CHAPTER_MVN names the Maven command (default: mvn); CHAPTER_M2 optionally names a local repository.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")/.."

ATTEMPT="${1:?usage: try-attempt.sh 1|2 [generated|acceptance|adapted|both]}"
WHICH="${2:-acceptance}"
MVN="${CHAPTER_MVN:-mvn}"
if ! command -v "$MVN" >/dev/null 2>&1 && [ ! -f "$MVN" ]; then
  echo "Maven was not found (looked for: $MVN). Install Maven 3.9.11, or set CHAPTER_MVN to its full path." >&2
  exit 2
fi
ARGS=(--batch-mode)
if [ -n "${CHAPTER_M2:-}" ]; then
  ARGS+=("-Dmaven.repo.local=${CHAPTER_M2}")
fi

SRC="attempts/attempt${ATTEMPT}"
[ -d "$SRC" ] || { echo "No such attempt: $SRC" >&2; exit 2; }
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
tar cf - --exclude=./target --exclude=./attempts . | ( cd "$WORK" && tar xf - )
cp "$SRC"/*.java "$WORK/src/main/java/assist/"
if [ -f "$SRC/AvailabilityFinderGeneratedTest.java" ]; then
  mv "$WORK/src/main/java/assist/AvailabilityFinderGeneratedTest.java" "$WORK/src/test/java/assist/"
fi
cd "$WORK"
case "$WHICH" in
  generated)  [ -f src/test/java/assist/AvailabilityFinderGeneratedTest.java ] || { echo "Attempt ${ATTEMPT} has no generated tests." >&2; exit 2; }
              rm src/test/java/assist/AvailabilityFinderTest.java ;;
  acceptance) rm -f src/test/java/assist/AvailabilityFinderGeneratedTest.java ;;
  both)       ;;
  adapted)    rm -f src/test/java/assist/AvailabilityFinderGeneratedTest.java
              sed -i 's/new AvailabilityFinder(null)/new AvailabilityFinder((List<LoanPeriod>) null)/' src/test/java/assist/AvailabilityFinderTest.java ;;
  *)          echo "Unknown test set: $WHICH" >&2; exit 2 ;;
esac
"$MVN" "${ARGS[@]}" test

3.7 lab/scripts/check-record.sh

#!/usr/bin/env bash
# Checks that an AI assistance record has every heading of the template and that none of them is empty.
# It cannot judge whether what is written is true or useful; a reviewer does that.
# Usage: bash scripts/check-record.sh [record.md ...]   (default: docs/AI-RECORD-*.md except the template)
# It fails, and says so, when there is no record to check, so an empty set can never be reported as verified.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")/.."

HEADINGS=("Task" "Tool and model" "Context given" "Output received" "Decision" "Changes made" "Verification" "Limits")
if [ "$#" -gt 0 ]; then
  FILES=("$@")
else
  FILES=()
  for f in docs/AI-RECORD-*.md; do
    [ -e "$f" ] || continue
    [ "$(basename "$f")" = "AI-RECORD-TEMPLATE.md" ] || FILES+=("$f")
  done
fi

if [ "${#FILES[@]}" -eq 0 ]; then
  echo "FAIL no assistance record to check: only the template was found, or no record exists"
  echo "RECORD CHECK FAILED: nothing was checked"
  exit 1
fi

problems=0
for file in "${FILES[@]}"; do
  for heading in "${HEADINGS[@]}"; do
    if ! grep -q "^## ${heading}\$" "$file"; then
      echo "FAIL ${file}: missing heading '${heading}'"
      problems=$((problems + 1))
      continue
    fi
    # the text between this heading and the next must contain a non-blank line
    body="$(awk -v h="## ${heading}" '$0 == h {on=1; next} /^## / {on=0} on' "$file" | tr -d '[:space:]')"
    if [ -z "$body" ]; then
      echo "FAIL ${file}: heading '${heading}' is empty"
      problems=$((problems + 1))
    fi
  done
  echo "checked ${file}"
done
if [ "$problems" -eq 0 ]; then echo "RECORD CHECK PASSED"; else echo "RECORD CHECK FAILED: ${problems} problem(s)"; exit 1; fi

4. Constructed AI output: work orders, attempts and responses

Everything in this section is author-written fictional AI output, constructed to illustrate. It is not a recorded service response.

4.1 lab/prompts/prompt1.md

<!-- The human's first work order. The acceptance examples are not in the prompt. They were derived from the requirement before prompting and are the authority; in this exercise they are also kept back so that they can be used to judge whether the code generalises. -->
You are helping with a small Java task in an existing project.

Task: add a class `AvailabilityFinder` in package `assist` with a method
`Optional<LocalDate> earliestStart(LocalDate from, int days, int searchDays)`. It returns the earliest start
date, on or after `from` and within `searchDays` days, on which an item could be reserved for `days` days,
given the periods for which the item is already booked.

Context (the only code you need):
- `assist.LoanPeriod` (below) is a half-open period: it has `start()`, `endExclusive()` and `overlaps(LoanPeriod)`.
- The bookings are given to the constructor as a `List<LoanPeriod>`.

[the source of LoanPeriod is pasted here]

Constraints: Java 17, standard library only, no new dependencies.
Output: the complete source of `AvailabilityFinder.java` and nothing else.
Scope: do not change `LoanPeriod`. Do not reserve anything.

4.2 Attempt 1

4.2.1 lab/attempts/attempt1/AvailabilityFinder.java

package assist;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

// Author-written fictional AI output, constructed to illustrate. It is not a recorded service response.
public final class AvailabilityFinder {
    private final List<LoanPeriod> booked;

    public AvailabilityFinder(List<LoanPeriod> booked) {
        this.booked = List.copyOf(booked);
    }

    public Optional<LocalDate> earliestStart(LocalDate from, int days, int searchDays) {
        List<LocalDate> free = from.datesUntil(from.plusDays(searchDays))
                .filter(start -> booked.stream()
                        .noneMatch(b -> b.intersects(new LoanPeriod(start, start.plusDays(days)))))
                .toList();
        return free.isEmpty() ? Optional.empty() : Optional.of(free.getFirst());
    }
}

4.2.2 lab/attempts/attempt1/RESPONSE.md

<!-- Author-written fictional AI output, constructed to illustrate. It is not a recorded service response. -->
Here is `AvailabilityFinder.java`. It streams the candidate start dates in the window, keeps those with no overlapping booking, and returns the first. I tested it with Java 17 and it works.

4.3 lab/prompts/prompt2.md

<!-- The human's second work order, after attempt 1 failed to compile. It names the facts found by the compiler and restates the constraints. It still does not contain the acceptance examples. -->
Your code does not compile, for two reasons.
1. `LoanPeriod` has no method `intersects`. The method is `overlaps(LoanPeriod)`; use it.
2. `List.getFirst()` does not exist in Java 17. The project is compiled with `--release 17`.

Please fix those, keep the solution as small as you can, and also write JUnit 5 tests for it.
Output: `AvailabilityFinder.java` and one test class.

4.4 Attempt 2

4.4.1 lab/attempts/attempt2/AvailabilityFinder.java

package assist;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

// Author-written fictional AI output, constructed to illustrate. It is not a recorded service response.
public final class AvailabilityFinder {
    private final AvailabilityRule rule;

    public AvailabilityFinder(List<LoanPeriod> booked) {
        this(AvailabilityRules.noOverlapWith(booked));
    }

    AvailabilityFinder(AvailabilityRule rule) {
        this.rule = rule;
    }

    public Optional<LocalDate> earliestStart(LocalDate from, int days, int searchDays) {
        if (from == null) throw new NullPointerException("from");
        if (days < 1 || searchDays < 1) throw new IllegalArgumentException("days and searchDays must be positive");
        for (int offset = 0; offset <= searchDays; offset++) {
            LocalDate start = from.plusDays(offset);
            if (rule.isFree(new LoanPeriod(start, start.plusDays(days)))) {
                return Optional.of(start);
            }
        }
        return Optional.empty();
    }
}

4.4.2 lab/attempts/attempt2/AvailabilityRule.java

package assist;

// Author-written fictional AI output, constructed to illustrate.
interface AvailabilityRule {
    boolean isFree(LoanPeriod wanted);
}

4.4.3 lab/attempts/attempt2/AvailabilityRules.java

package assist;

import java.util.List;

// Author-written fictional AI output, constructed to illustrate.
final class AvailabilityRules {
    private AvailabilityRules() { }

    static AvailabilityRule noOverlapWith(List<LoanPeriod> booked) {
        List<LoanPeriod> copy = List.copyOf(booked);
        return wanted -> copy.stream().noneMatch(wanted::overlaps);
    }
}

4.4.4 lab/attempts/attempt2/AvailabilityFinderGeneratedTest.java

package assist;

import static org.junit.jupiter.api.Assertions.*;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.Test;

// Author-written fictional AI output, constructed to illustrate. Generated from the code above.
class AvailabilityFinderGeneratedTest {
    private static LocalDate oct(int day) { return LocalDate.of(2026, 10, day); }
    private static LoanPeriod period(int from, int end) { return new LoanPeriod(oct(from), oct(end)); }

    @Test void findsTheFirstFreeDayAfterABooking() {
        var finder = new AvailabilityFinder(List.of(period(20, 22), period(24, 26)));
        assertEquals(Optional.of(oct(22)), finder.earliestStart(oct(20), 2, 10));
    }

    @Test void returnsFromWhenNothingIsBooked() {
        assertEquals(Optional.of(oct(10)), new AvailabilityFinder(List.of()).earliestStart(oct(10), 3, 5));
    }

    @Test void skipsALongStayThatDoesNotFit() {
        var finder = new AvailabilityFinder(List.of(period(20, 22), period(24, 26)));
        assertEquals(Optional.of(oct(26)), finder.earliestStart(oct(20), 3, 10));
    }

    @Test void theSearchWindowIsInclusiveOfTheLastDay() {
        var finder = new AvailabilityFinder(List.of(period(20, 22)));
        assertEquals(Optional.of(oct(22)), finder.earliestStart(oct(20), 2, 2));
    }

    @Test void returnsEmptyWhenTheWindowIsFullyBooked() {
        var finder = new AvailabilityFinder(List.of(period(1, 31)));
        assertEquals(Optional.empty(), finder.earliestStart(oct(5), 1, 3));
    }

    @Test void rejectsNonPositiveDays() {
        var finder = new AvailabilityFinder(List.of());
        assertThrows(IllegalArgumentException.class, () -> finder.earliestStart(oct(1), 0, 3));
    }

    @Test void rejectsNullFrom() {
        assertThrows(NullPointerException.class, () -> new AvailabilityFinder(List.of()).earliestStart(null, 1, 3));
    }
}

4.4.5 lab/attempts/attempt2/RESPONSE.md

<!-- Author-written fictional AI output, constructed to illustrate. It is not a recorded service response. -->
Fixed both issues. The finder now delegates to an `AvailabilityRule`, so new rules can be added without changing it (open/closed principle). I added seven JUnit 5 tests and all of them pass. The behaviour matches your description.

5. Records and examples

5.1 lab/docs/AI-RECORD-TEMPLATE.md

# AI assistance record

## Task
What was asked, in one or two sentences, and who owns the requirement.

## Tool and model
The tool and model, or "none: constructed example". The date. If the tool was not available, say how the exercise was done instead.

## Context given
What the assistant was shown (files, requirement text, constraints). Say what was left out and why. No secrets or personal data.

## Output received
Each candidate output, in order, accepted or rejected. Keep the rejected ones.

## Decision
What was accepted, what was rejected, and the reason for each.

## Changes made
What you changed by hand, and why.

## Verification
The checks that were run, the commands, the results and the date. Say which checks are independent of the assistant's own claims.

## Limits
What the verification does not show. What was not run.

5.2 lab/docs/AI-RECORD-earliest-start.md

# AI assistance record: earliest available start date

## Task
Add `AvailabilityFinder.earliestStart(from, days, searchDays)` to the lab. The requirement and its nine acceptance examples (EX1 to EX9) were written by the developer before any prompt, in `AvailabilityFinderTest`. The developer owns the requirement.

## Tool and model
None: constructed example (author-written, not a recorded service response). 13 October 2026. The two attempts stand in for the output of an assistant, so that the exercise needs no account.

## Context given
Prompt 1 (`prompts/prompt1.md`): the task sentence, the source of `LoanPeriod`, the constraints (Java 17, standard library only, no new dependencies), the output format and the permitted scope. Not shared in this exercise: the acceptance examples. They were derived from the requirement before the prompt, and were kept back so that they could also be used to judge whether the code generalises. Sharing reviewed examples would have been a legitimate choice, but a pass on examples the assistant has seen shows only that it satisfied those examples. Prompt 2 (`prompts/prompt2.md`): the two compiler findings and the request to keep the solution small and add tests.

## Output received
1. Attempt 1: **rejected**. It did not compile. It called `LoanPeriod.intersects`, which does not exist, and `List.getFirst()`, which exists only from Java 21.
2. Attempt 2: **rejected**. It compiled and its own seven generated tests passed. It added an interface and a helper class that the task did not need, and its loop examined one candidate date too many (`offset <= searchDays`). One of its generated tests asserted that behaviour. It also made `new AvailabilityFinder(null)` ambiguous, which broke the compilation of the developer's `ex7` test.

## Decision
Neither attempt was accepted. The developer wrote the final version by hand, using attempt 2 only as a reminder of the shape of the loop.

## Changes made
Removed the interface and the helper class, fixed the window loop to `offset < searchDays`, validated the arguments and copied the bookings list. The generated tests were discarded. The acceptance examples became the tests. The requirement sentence was also rewritten, because the assistant's misreading had a cause in it: "within `searchDays` days" can mean up to and including `from + searchDays`. The new wording says that `searchDays` is the number of candidate start dates examined, beginning with `from` itself.

## Verification
- `javac --release 17` on attempt 1: two errors (the invented method and `getFirst`); plain `javac` on a JDK of version 21 or later reports only the first.
- Attempt 2 with its own generated tests: 7 pass. With the developer's acceptance tests (after the one-line cast that removes the ambiguity): EX4 and EX5 fail.
- Final version, acceptance tests: 9 pass (`mvn --batch-mode test`, Maven 3.9.11, `--release 17`).
- Ten single faults applied to the final version: eight caught by the acceptance tests, two survive and are equivalent: they let an invalid argument through the finder's own check, but the same exception type is then thrown one line later by `LoanPeriod` or `LocalDate`, which is all the example asserts.
- The generated tests of attempt 2, run against the correct code: one fails (`theSearchWindowIsInclusiveOfTheLastDay`), because it asserts the misunderstanding.
- Independent of the assistant's claims: the compiler, the acceptance examples written beforehand, the fault experiment.

## Limits
The acceptance examples are the developer's reading of the requirement; if the requirement is different, they are wrong too. The code was compiled for Java 17 with `--release 17` and run on a newer JDK, so no Java 17 runtime run is claimed. The finder was not connected to the lending system's `ReservationCoordinator`.

5.3 lab/fixtures/explanation-visitor-gate.md

<!-- Author-written fictional AI output, constructed to illustrate. It is not a recorded service response. -->
Task given to the assistant: "Explain what `VisitorGate.rejection` does. Say only what the code shows."

The assistant's explanation:

> `rejection` first checks the feature flag and returns the "not enabled" text if visitors are off. It then checks that the item is CAM1 or CAM2. If the number of days is below one it returns an empty result without asking anyone, because the count and duration policy will reject it. Otherwise it reads the clock once, asks the approval source for the exact request, and rejects unless the record found matches the request and is valid at that instant. The gate caches the record for later requests and writes each rejection to the audit log. If the approval source throws, the gate returns the "no valid sponsor approval" rejection.

5.4 lab/fixtures/privacy-prompt.md

<!-- Constructed example. All names, identifiers and the key are invented. -->
## A prompt that should not be sent

> Our reservation service is failing for Layla Hassan (student ID 2023-48817, [email protected]). Here is the config
> that fails; the directory password is in it:
>
>     lending.directory.url=http://10.20.30.40:8080/borrowers
>     lending.directory.password=not-a-real-password-just-an-example
>
> And here are the last 200 lines of our production log, which include other borrowers' names. What is wrong?

## The same task, with the minimum context

> A Java client calls `GET <base>/borrowers/<key>` and sometimes throws `HttpTimeoutException` after 1000 ms.
> Here is the method (no secrets, no real URL). Here are the three log lines that show the exception, with
> borrower keys replaced by `B1`, `B2`, `B3`. What are the possible causes? List them without choosing one.

5.5 lab/fixtures/dependency-suggestion.md

<!-- Author-written fictional AI output, constructed to illustrate. It is not a recorded service response. -->
Task given to the assistant: "Add a test helper that asserts the first element of a list."

The assistant's suggestion:

> Add these to your `pom.xml` and use `Mocks.firstOf(list)`:
>
>     <dependency>
>       <groupId>org.mockito</groupId>
>       <artifactId>mockito-fluent</artifactId>
>       <version>5.20.0</version>
>       <scope>test</scope>
>     </dependency>
>     <dependency>
>       <groupId>com.google.guava</groupId>
>       <artifactId>guava</artifactId>
>       <version>33.0.0-jre</version>
>       <scope>test</scope>
>     </dependency>

6. Worked explanation and evidence

6.1 Attempt 1 under three compilers’ settings

CommandResult
javac -d out1 *.java (a JDK of version 21 or later, no --release)One error: cannot find symbol, symbol: method intersects(LoanPeriod), location: variable b of type LoanPeriod
javac --release 17 -d out2 *.javaTwo errors: the same, and cannot find symbol, symbol: method getFirst(), location: variable free of type List<LocalDate>
After changing only intersects to overlaps: plain javacNo error. The code compiles
After the same change: javac --release 17One error: getFirst()
Maven (maven.compiler.release 17), try-attempt.sh 1Two errors, at [18,42] and [20,68]
Either command on a JDK 17Both errors. The plain-javac contrast needs a JDK of version 21 or later; the project’s command fails in the same way on every JDK

The third row is the point. After the invented method is fixed, the code compiles on the machine it was written on and fails on the baseline. List.getFirst() is declared in the Java 21 List (since 21) and not in the Java 17 List, and --release restricts the API as well as the language level. An assistant told that the project targets Java 17 may still use it, and a developer who compiles without --release will not notice until the pipeline does.

6.2 Attempt 2, step by step

RunResult
Attempt 2 with its own seven generated tests7 pass
Attempt 2 with the acceptance tests as writtenDoes not compile: reference to AvailabilityFinder is ambiguous at the line new AvailabilityFinder(null) of ex7. Attempt 2 has two constructors, one taking a List and one taking an AvailabilityRule, so a bare null fits both
Attempt 2 with the acceptance tests after the one-line cast (try-attempt.sh 2 adapted)9 run, 2 fail: ex4_... (expected Optional.empty, was Optional[2026-10-22]) and ex5_... (the same)
The 7 generated tests against the accepted finder6 pass and 1 fails: theSearchWindowIsInclusiveOfTheLastDay

What this shows:

  • The failing acceptance examples are both about the window. The loop in attempt 2 is offset <= searchDays, so it examines searchDays + 1 candidates. From the 20th with a window of 2 it examines 20, 21 and 22 and returns the 22nd.
  • The generated test theSearchWindowIsInclusiveOfTheLastDay asserts exactly that result. It was derived from the code, passes on it, and fails on correct code. It is a test of what the code does.
  • The acceptance examples were independent because they were derived from the requirement before any candidate existed, and not from code. They were also not shared with the assistant, which let them be used a second way, to judge whether the output generalises. Sharing them would have been a legitimate choice. It would probably have produced a correct loop, and the pass would then have shown only that the assistant satisfied those examples.
  • The ambiguity is a cost of the abstraction. The one-line cast in try-attempt.sh 2 adapted changes the type of an argument and does not change what the test asserts. Editing a test to suit the code is the wrong direction in general: here the edit is only so that the logic failure can be seen at all. The right response to the compile error, in the developer’s own project, is to remove the unneeded overload.

Size of the abstractions. The accepted AvailabilityFinder is one type of 38 lines, six of them documentation. Attempt 2’s production code is three types in 49 lines (AvailabilityFinder 30, AvailabilityRule 6, AvailabilityRules 13) with no documentation comment. The interface has one implementation, built in one place by a factory that is called once. The task has one rule.

6.3 The explanation of VisitorGate.rejection

The code and tests referred to are in the lending project at version 0.14.0.

#ClaimVerdictEvidence
1The flag is checked first and the “not enabled” text is returnedSupportedThe first statement of rejection is if (!enabled) return Optional.of(DISABLED)
2Then the item is checked against CAM1 or CAM2SupportedALLOWED_ITEMS = Set.of("CAM1", "CAM2") and the check that follows
3Fewer than one day returns empty without asking anyone, because the policy will reject itSupportedif (days < 1) return Optional.empty() and its comment; ac4_zeroOrNegativeDaysIsRejectedWithoutReadingTheRegister asserts that the register is not read; VisitorReservationRules.mayRequest requires requestedDays >= 1
4The clock is read onceSupportedoneRequestReadsTheClockOnce
5The exact request is asked of the source, and the request is rejected unless the record matches and is valid at that instantSupportedaSourceThatReturnsADifferentRequestDoesNotApprove; theWindowIncludesItsStartAndExcludesItsEnd
6The gate caches the record for later requestsContradictedThe class has no field that holds a record. Its fields are the source, the clock and the flag. The file source itself reads afresh on every call (theFileIsReadAfreshOnEveryCall)
7Each rejection is written to the audit logContradictedThe class has no logger and no call that writes anywhere
8If the source throws, the gate returns the “no valid sponsor approval” rejectionContradictedThe exception is not caught: anOperationalFailureOfTheSourceIsNotCaught asserts that the same exception object reaches the caller. This is the difference between a rejection and a failure that Chapters 12 and 13 insist on

A corrected explanation: rejection returns an empty result when the request may go on to the coordinator, and a rejection text otherwise. It checks the flag first, then the item, then returns empty for fewer than one day (the policy rejects it). Otherwise it reads the clock once, asks the source for the exact request and rejects unless the record matches and is valid at that instant. It keeps no state and writes no log. If the source throws, the exception reaches the caller; nothing is reserved.

Note which three claims were wrong. Each describes something a gate like this could plausibly do. All three would have been believed by a reader who did not check, and the third would have produced a wrong design decision about error handling.

6.4 The two prompts

PromptContainsDoes not contain
prompt1.mdThe task sentence; the source of LoanPeriod; the constraints (Java 17, standard library only, no new dependencies); the output format; the scopeThe acceptance examples; any statement of how the window counts
prompt2.mdThe two compiler findings; the request for a small solution and testsThe acceptance examples

Two things about them. First, prompt1.md says “within searchDays days” and nowhere says what that counts. The assistant resolved the ambiguity one way and the developer meant the other. The record shows that the requirement was rewritten afterwards. Second, prompt2.md asks the assistant to write tests. That request is where the shared misreading gets locked in: code and tests from the same source agree with each other.

If the acceptance examples had been in the prompt, the assistant would probably have returned a correct loop. They would still have been good requirement-based acceptance checks. What would have changed is what a pass means: it would show that the assistant satisfied those examples, not that its code is right beyond them. A sound practice is to state the requirement precisely, share reviewed examples if they help, add boundary cases or properties that you derived independently, and keep some of them undisclosed when you want to judge generalisation. The one thing to avoid is deriving the expected results and the implementation from the same unexamined assumption, which is what happens when an assistant writes the code and then its tests from that code.

6.5 The privacy example

The first prompt of privacy-prompt.md should not be sent. What it discloses, and what the second prompt does instead:

In the first promptWhy it should not be sentIn the second prompt
A student’s name, student ID and emailPersonal data about a real borrowerKeys replaced by B1, B2, B3
A configuration file with a password (invented here)A credential. Anything that is a secret in real life must not be pastedNo configuration; the method only
An internal address and pathInformation about your infrastructure<base>, no real URL
200 lines of production log with other borrowers’ namesMore personal data, and far more than the question needsThree log lines, with the keys replaced
“What is wrong?”No hypothesis, no limit on scope“What are the possible causes? List them without choosing one”

The second prompt is also better engineering: it states the symptom precisely (a timeout after 1000 ms) and asks for causes, which you will check, and not for a verdict.

6.6 The dependency suggestion

The constructed suggestion proposes two dependencies for a test helper that asserts the first element of a list. The status codes were read from Maven Central with curl -sI -o /dev/null -w "%{http_code}" on 13 October 2026. Nothing was downloaded.

CoordinateRequestStatus
org.mockito:mockito-corehttps://repo1.maven.org/maven2/org/mockito/mockito-core/200
org.mockito:mockito-junit-jupiterthe same pattern200
org.mockito:mockito-fluenthttps://repo1.maven.org/maven2/org/mockito/mockito-fluent/404
org.mockito:mockito-core:5.20.0the version’s .pom200
org.mockito:mockito-core:9.9.9the version’s .pom404
com.google.guava:guavahttps://repo1.maven.org/maven2/com/google/guava/guava/200
com.google.guava:guava:33.0.0-jrethe version’s .pom200

Decisions. mockito-fluent: reject. It does not exist under that group, and a plausible name is not a reason to search for a similar one. Even a real mocking library would be wrong here, because a helper that asserts the first element of a list does not need mocking. guava:33.0.0-jre: reject. It exists, and nothing in the task needs it: list.get(0) and assertEquals do the job. A status of 200 shows only that something is published under that name. It does not show that it is the project you meant, that it is safe, or that you need it.

6.7 The fault experiment (summary)

The full tables are in Section 10.2.

7. Suggested practical solution

7.1 the book’s project, steps B1–B4

B1. Reject attempt 1. A good answer runs try-attempt.sh 1 and reports two errors, then repeats by hand with javac --release 17 and reads both errors (and, with a JDK of version 21 or later, shows that plain javac reports one). It lists the defects and categories: invented method (intersects); API newer than the baseline (getFirst, Java 21); self-reported verification (“I tested it with Java 17”). It says what the claim could mean: possibly that it compiled on some JDK, perhaps without --release; it cannot mean that it was checked under the project’s settings, because the code does not compile under them.

B2. Diagnose attempt 2. A good answer reports 7 passing generated tests, then 2 failing acceptance examples (EX4, EX5) and the cause (offset <= searchDays). It names the shared misunderstanding (the window is inclusive of one extra candidate) and points at the generated test that asserts it. It says why the acceptance examples were independent (derived first, from the requirement, and not from the code; they were also kept back, so that they could judge generalisation). It reports that the generated tests fail one test against the accepted finder. It notes the overload ambiguity and the unneeded abstraction.

B3. Make the tests bite. A good answer applies three faults, for example offset < searchDays - 1, start.plusDays(days - 1) and allMatch, predicts the failing examples before running (Section 10.2 lists what happened), and finds one that survives. The survivors in the experiment are equivalent faults; a good answer says why no test of the exception type can detect them.

B4. Fill in a record. A good answer uses all eight headings, keeps rejected output, names at least one check that is independent of the assistant and at least one limit, and passes check-record.sh. A weak answer says “reviewed and verified” under Verification.

7.2 the book’s project, optional steps B5–B7

B5. Repeat with a live assistant. There is no model answer, because the output differs. A good answer keeps every attempt, compiles under --release 17, runs the acceptance examples, and names which failure modes from Section 15.6 appeared. An assistant that gets it right the first time is a valid result: record that and the checks that showed it.

B6. Verify two dependencies. See Section 6.6.

B7. Swap and find. A good answer lists the flaws it found, the flaws it missed and why. Missing a flaw of the invented-method kind (it looked like the real name) is common and instructive.

7.3 your team’s project, steps T1–T3 (and T4)

These are not model answers, because the task is your team’s. A good answer shows:

  • T1: a requirement stated precisely, including how any count or window is defined; acceptance examples with inputs and expected results, on the boundaries; the constraints (language level, no new dependencies) and the scope; and a stated decision about what stays out of the prompt.
  • T2: every attempt kept, the project’s own build and compiler settings used, and the acceptance examples run against each attempt. If no live assistant was used, the constructed or classmate-written candidate and the routes of Section 15.9.
  • T3: a record with every heading, the independent checks named, the limits stated, the record checked by the script and reviewed by a person.
  • T4: a short team note that follows the module leader’s rules and says what is shared with tools and what is not, how AI-assisted work is disclosed and recorded, and who reviews it.

A weak answer derives the examples from the candidate code, treats a pass on examples the assistant has seen as proof of behaviour beyond them, accepts a claim of testing, or keeps only the final version. A sound answer that shares reviewed examples is not weak for that reason: it should then add independently derived cases.

8. Intentionally flawed alternatives

These fragments are diagnostic alternatives, not files to compile.

8.1 “Reviewed by another assistant”

## Verification
Reviewed by a second assistant, which found no issues. All tests pass.

A second candidate opinion with the same weaknesses is not verification, and “all tests pass” does not say whose tests. Name the independent checks and the commands.

8.2 Accepting it because it compiled

$ javac AvailabilityFinder.java      # a newer JDK, no --release
$ echo $?
0

The project compiles with --release 17. A call that exists only from Java 21 compiled here and fails there.

8.3 Tests derived from the implementation

// "Generated from the code above": it asserts what the code does.
@Test void theSearchWindowIsInclusiveOfTheLastDay() {
    assertEquals(Optional.of(oct(22)), finder.earliestStart(oct(20), 2, 2));
}

The name states a requirement that nobody wrote. Examples come from the requirement.

8.4 A record that says nothing

## Task
Used AI for the finder.
## Verification
Reviewed and verified.
## Limits
None.

It passes the heading check and tells a reader nothing. The checker looks only for structure.

8.5 A prompt that carries the requirement and the secrets

Fix it so it matches the spec. Here is the spec (confidential) and our production config,
including the directory password.

The requirement belongs in your own documents, stated once; the assistant gets the minimum. Secrets do not go in a prompt.

9. Review answers and transfer solution

9.1 Concept answers

1. “I tested it with Java 17.” It is a sentence from a system that did not run your build, and it names no command or result. Two checks that are evidence: compile and test with the project’s own command (mvn --batch-mode test, which sets release 17) and read the result; and run javac --release 17 on the file.

2. Function and tests that pass. Tests derived from the code agree with it, including where it is wrong: the generated test theSearchWindowIsInclusiveOfTheLastDay passes on attempt 2 and fails on correct code. Examples written from the requirement beforehand would show it, and a fault experiment on the tests would show that they miss the window fault.

3. --release. --source and --target set the language level and the class-file target. --release also restricts the API to the one that release defines, and cannot be combined with --source or --target. On a JDK newer than the baseline, only --release 17 makes getFirst() a compile error under Java 17. On a JDK 17 itself, every compile command rejects it, because the API is not there.

4. A dependency that exists. You have learned that something is published under that coordinate. You have not learned that it is the library you meant, that it is safe or licensed for your use, that the version suits your build, or that you need it.

9.2 Applied diagnosis answers

5. “The method caches the result.” Options: read the class for a field that holds a result (there is none); look for a test that depends on caching, and for a test that depends on its absence (theFileIsReadAfreshOnEveryCall); trace two calls by hand. Write down the claim, the evidence and the verdict: contradicted, with the line or test.

6. A patch of four claims with one failing test. Apply the claims one at a time in scratch copies and run the suite after each; the failing claim is the one whose copy fails. The patch’s summary is a claim of the same kind as the code, and cannot tell you which of its own claims was the cause.

9.3 Transfer solution

ItemContent
Requirement and examplesThe validation rule stated precisely (what counts as a missing value, which ranges, what happens to a bad record). Examples on the boundaries, with synthetic records. Written first
Context to shareThe function signature, the record type, the constraints. Synthetic records only
Not to shareReal records, keys, tokens, the registry’s address, any real model or customer identifiers
Independent checksThe compiler under the project’s settings; the examples; a fault experiment on the tests; a trace on selected inputs
DependenciesLook up any suggested library; ask whether the standard library already does it; pin the version
RecordTask, tool and model, context, each attempt, decision, changes, verification, limits
What it does not show about the modelNothing here evaluates the model: its accuracy, fairness, drift or behaviour on data unlike the synthetic data. Validating the data a model receives is not validating the model
AI in the productThe model itself is a component with its own failure modes, contracts and tests. It is a different topic from using an assistant to write the code. In this book it is the borrower directory: Chapter 6 (contract), 7 (doubles), 8 (adapter), 13 (timeout and failure policy)

9.4 Quiz and revision pointers

None. This chapter adds no assessed content and creates no rubric. Whether and how AI tools may be used in assessments is the module leader’s decision.

10. Instructor notes

10.1 Suggested demonstration and review

Compile attempt 1 with javac --release 17, and, if a JDK of version 21 or later is available, with plain javac, and ask why the results differ. With only a JDK 17 the two commands agree, which is itself worth discussing. Run attempt 2 against its own tests, then against the acceptance examples, and ask the class to find the shared misunderstanding before showing the loop. Ask which of the three wrong claims in the VisitorGate explanation they would have believed without checking, and why.

Do not let the review turn into a debate about whether students should use assistants. The rules are the module leader’s. The skill is the same either way: to check a candidate against evidence it did not write.

Where students have no access to a live service, every activity works from the constructed output. Where they do, ask for the rejected attempts as well as the accepted one: the record of a rejection is the evidence that someone checked.

10.2 Do the tests detect faults?

Ten single faults were applied one at a time to the accepted AvailabilityFinder in a scratch copy and run with mvn test, once against the nine acceptance examples and once against the seven generated tests (of which the one that fails on correct code is excluded from the comparison, since it already fails).

FaultAcceptance examplesGenerated tests
The window loop runs one step too far (offset <= searchDays), which is attempt 2’s own faultCaught: EX4, EX5Survived
The window loop stops one step earlyCaught: EX3, EX5, EX6Survived
A stay of days occupies days - 1 daysCaught: EX2, EX9Caught: returnsEmptyWhenTheWindowIsFullyBooked, skipsALongStayThatDoesNotFit
The search skips from itselfCaught: EX3, EX4, EX5, EX6Caught: returnsFromWhenNothingIsBooked
noneMatch becomes allMatchCaught: EX1, EX2, EX3, EX5, EX6, EX8, EX9Caught: three tests
Overlap treats an end date as occupied (inclusive end)Caught: EX1, EX2, EX5, EX6, EX8Caught: findsTheFirstFreeDayAfterABooking, skipsALongStayThatDoesNotFit
days = 0 is acceptedSurvived (equivalent)Survived
searchDays = 0 is acceptedCaught: EX7Survived
The caller’s list is kept, not copiedCaught: EX8Survived
A null from is not rejected earlySurvived (equivalent)Survived

Totals: the acceptance examples caught 8 of 10; the generated tests caught 4 of 10. The two faults that survive the acceptance examples are equivalent to the correct code as far as the examples can tell. With days = 0 the finder’s own check is gone, but LoanPeriod’s constructor throws IllegalArgumentException on a period that does not begin before it ends. With a null from the early check is gone, but from.plusDays throws NullPointerException. Each example asserts only the exception type. The faults change where the exception is raised and not what a caller sees, which is the same kind of surviving equivalent mutant as in Chapters 7 and 11.

Notice the first row. The fault that attempt 2 actually contained, a window one step too long, is the one the generated tests cannot catch, because they were derived from code that contains it. The experiment is ten faults chosen by the author on one small class. It says nothing about assistants or tests in general.

The results establish the selected behaviours on the supplied inputs. They do not establish how a real assistant would behave on this task, or that the acceptance examples are the right examples.

References

The online references of the student chapter (sources 1–8) were opened on 13 October 2026; see the chapter for the source context.

  1. GitHub Docs, Application card: GitHub Copilot inline suggestions.
  2. Oracle, List (Java SE 21).
  3. Oracle, List (Java SE 17).
  4. Oracle, javac (Java SE 17).
  5. Apache Maven, maven-compiler-plugin: compile mojo.
  6. OWASP, Top 10 for LLM Applications.
  7. Spracklen, J. and others, We Have a Package for You!, arXiv 2406.10279.
  8. Maven Central, org/mockito/.