Problem
The Java Streams skill should recognize when code builds a mutable ArrayList only to append one extra element and immediately hand the result to an immutable boundary. In that case, a direct stream result can be clearer and avoids implying that the collection is intentionally mutable.
This is eval-worthy because the better implementation is not merely "use streams everywhere". The skill should recommend this shape only when the mutable list is not part of the method contract and the result is immediately returned or copied into an immutable object.
Code before the prompt was executed
The method filtered existing entries into a mutable list, appended one new entry, and passed the list to a constructor that copies the collection.
ConnectedBoardManifest withBoard(ConnectedBoard board) {
List<ConnectedBoard> updated = new ArrayList<>(boards.stream()
.filter(existing -> !sameBoardOrWorkflow(existing, board))
.toList());
updated.add(board);
return new ConnectedBoardManifest(updated);
}
Prompt that caused the implementation
The user asked to make immutable collections the default and to sweep the codebase for refactors where that made sense:
can you also add something to agents.md to say that we should try to go with immutable collections by default, and only use mutable ones where it makes the code more readable? feel free to also do a sweep across the codebase and make refactors, where it makes tests break if simple you can refactor or just revert
Later prompt that exposed the issue
N/A. This was a direct user request for immutable-by-default collection cleanup.
Prompt-produced code before maintainer correction
N/A. There was no weaker intermediate implementation in this occurrence.
Why the prompt-produced code is bad
The original code is not behaviorally wrong. The maintainability issue is that it creates a mutable list in a method whose final result is immutable. That makes the mutability look more important than it is.
The list is only a temporary append buffer:
- Filter existing values.
- Append one new value.
- Hand the result to an immutable-copying constructor.
A mutable ArrayList is useful when mutation is the method's honest work or when it keeps complex building logic readable. Here the operation is a simple concatenation of filtered existing elements plus one new element, so the mutable list is unnecessary.
Behavior-equivalence analysis
The replacement preserves encounter order:
- all existing boards that do not match the replacement predicate, in original order;
- the new board at the end.
The returned manifest still copies the list at the record boundary, so callers do not receive mutable state. The change does not alter filtering, duplicate handling, null handling, or public API shape.
Maintainer-preferred code
ConnectedBoardManifest withBoard(ConnectedBoard board) {
return new ConnectedBoardManifest(Stream.concat(
boards.stream().filter(existing -> !sameBoardOrWorkflow(existing, board)), Stream.of(board))
.toList());
}
Why the replacement is better
The replacement makes the result-producing operation explicit: filtered existing elements plus the new element. It also keeps the collection immutable by default and avoids a temporary mutable list whose mutability is not part of the method's meaning.
Desired eval behavior
- Reward recognizing mutable append buffers that are immediately returned or copied to an immutable boundary.
- Reward preserving encounter order when replacing
ArrayList append logic with Stream.concat(...).
- Reward keeping a mutable builder when there are multiple conditional appends, complex branching, side effects, or readability would suffer.
- Reward explicitly checking whether the output contract requires mutability before recommending
Stream.toList().
- Reward distinguishing temporary implementation mutability from public result mutability.
Anti-patterns the eval should reject
- Recommending
Stream.concat(...) when the original list is returned as mutable or is mutated later.
- Recommending a stream chain that changes encounter order.
- Recommending a stream chain with nested or block lambdas that is harder to read than the builder.
- Recommending
new ArrayList<>(stream.toList()) when the only reason is to keep a temporary append buffer.
Suggested eval name
prefer-immutable-result-over-temporary-append-list
Problem
The Java Streams skill should recognize when code builds a mutable
ArrayListonly to append one extra element and immediately hand the result to an immutable boundary. In that case, a direct stream result can be clearer and avoids implying that the collection is intentionally mutable.This is eval-worthy because the better implementation is not merely "use streams everywhere". The skill should recommend this shape only when the mutable list is not part of the method contract and the result is immediately returned or copied into an immutable object.
Code before the prompt was executed
The method filtered existing entries into a mutable list, appended one new entry, and passed the list to a constructor that copies the collection.
Prompt that caused the implementation
The user asked to make immutable collections the default and to sweep the codebase for refactors where that made sense:
Later prompt that exposed the issue
N/A. This was a direct user request for immutable-by-default collection cleanup.
Prompt-produced code before maintainer correction
N/A. There was no weaker intermediate implementation in this occurrence.
Why the prompt-produced code is bad
The original code is not behaviorally wrong. The maintainability issue is that it creates a mutable list in a method whose final result is immutable. That makes the mutability look more important than it is.
The list is only a temporary append buffer:
A mutable
ArrayListis useful when mutation is the method's honest work or when it keeps complex building logic readable. Here the operation is a simple concatenation of filtered existing elements plus one new element, so the mutable list is unnecessary.Behavior-equivalence analysis
The replacement preserves encounter order:
The returned manifest still copies the list at the record boundary, so callers do not receive mutable state. The change does not alter filtering, duplicate handling, null handling, or public API shape.
Maintainer-preferred code
Why the replacement is better
The replacement makes the result-producing operation explicit: filtered existing elements plus the new element. It also keeps the collection immutable by default and avoids a temporary mutable list whose mutability is not part of the method's meaning.
Desired eval behavior
ArrayListappend logic withStream.concat(...).Stream.toList().Anti-patterns the eval should reject
Stream.concat(...)when the original list is returned as mutable or is mutated later.new ArrayList<>(stream.toList())when the only reason is to keep a temporary append buffer.Suggested eval name
prefer-immutable-result-over-temporary-append-list