Problem
Add a Java Streams skill eval for behavior-preserving stream refactors where the old loop used a domain-specific splitter and the new stream source changed the parser contract.
This is eval-worthy because String.lines() looks like the natural stream replacement for looping over lines, but it is not behavior-equivalent to text.split("\\R", -1). String.lines() recognizes LF, CR, and CRLF. Java regex \R also recognizes linebreaks such as NEL, line separator, and paragraph separator.
Code before the prompt was executed
The pre-refactor code scanned each card text field and split logical lines with regex \R.
private static List<Declaration> declarations(Card card) {
List<Declaration> declarations = new ArrayList<>();
addDeclarations(declarations, card.title());
addDeclarations(declarations, card.description());
card.comments().stream().map(Card.Comment::text).forEach(text -> addDeclarations(declarations, text));
return List.copyOf(declarations);
}
private static void addDeclarations(List<Declaration> declarations, String text) {
if (text == null || text.isBlank()) {
return;
}
for (String line : text.split("\\R", -1)) {
Matcher labeled = LABELED_SOURCE.matcher(line);
if (labeled.matches()) {
declarations.add(new Declaration(labeled.group(2), labelMode(labeled.group(1))));
}
}
}
Prompt that caused the implementation
The maintainer asked for repeated Optional/Streams cleanup passes until actionable findings were gone:
ARE YOU SURE you covered all optioanls/streams thinfs ther3 are to cleanup with th3 skills? please do more passes until a pass comes back clean
This should have triggered the Streams skill to preserve parser semantics while refactoring the loop into a stream-producing shape.
Later prompt that exposed the issue
A later review found that the stream refactor was not behavior-preserving:
RepositorySourceResolver.java changed repository-source declaration parsing from text.split("\\R", -1) to text.lines(). That is not behavior-preserving. String.lines() recognizes only LF, CR, and CRLF, while regex \R recognizes Unicode linebreak sequences such as NEL \u0085, line separator \u2028, and paragraph separator \u2029.
Prompt-produced code before maintainer correction
The agent-produced stream code used String.lines() as the stream source.
private static List<Declaration> declarations(Card card) {
return Stream.concat(
Stream.of(card.title(), card.description()),
card.comments().stream().map(Card.Comment::text))
.flatMap(RepositorySourceResolver::declarations)
.toList();
}
private static Stream<Declaration> declarations(String text) {
if (text == null || text.isBlank()) {
return Stream.empty();
}
return text.lines()
.map(LABELED_SOURCE::matcher)
.filter(Matcher::matches)
.map(labeled -> new Declaration(labeled.group(2), labelMode(labeled.group(1))));
}
Why the prompt-produced code is bad
The code is not bad because it uses streams. It is bad because it silently changes the parser's definition of a logical line.
- The old parser recognized all Java regex
\R linebreak sequences.
String.lines() recognizes a narrower set of line terminators.
- A repository-source declaration after NEL, line separator, or paragraph separator can be missed.
- The refactor looks behavior-preserving because both APIs talk about lines, but they do not implement the same linebreak contract.
- This is a correctness issue, not only a style preference.
Maintainer-preferred code
Keep the stream-producing shape, but use a stream source that preserves the previous \R splitting semantics.
private static final Pattern LINE_BREAK = Pattern.compile("\\R");
private static List<Declaration> declarations(Card card) {
return Stream.concat(
Stream.of(card.title(), card.description()),
card.comments().stream().map(Card.Comment::text))
.flatMap(RepositorySourceResolver::declarations)
.toList();
}
private static Stream<Declaration> declarations(String text) {
if (text == null || text.isBlank()) {
return Stream.empty();
}
return LINE_BREAK.splitAsStream(text)
.map(LABELED_SOURCE::matcher)
.filter(Matcher::matches)
.map(labeled -> new Declaration(labeled.group(2), labelMode(labeled.group(1))));
}
Why the replacement is better
The replacement keeps the intended stream pipeline while preserving the old parser contract.
LINE_BREAK.splitAsStream(text) still produces a stream of logical lines.
- The linebreak grammar stays the old regex
\R grammar.
- The mapping/filtering pipeline remains readable.
- Regression tests can cover LF, CRLF, NEL, line separator, and paragraph separator without host assumptions.
Desired eval behavior
- Reward checking whether a stream source is behavior-equivalent to the old loop source.
- Reward preserving domain-specific parser splitting semantics when refactoring into streams.
- Reward using
Pattern.splitAsStream(...) when the old contract is regex-based splitting.
- Reward explaining that
String.lines() is not always equivalent to split("\\R", -1).
- Reward adding regression tests for edge-case linebreaks when parser behavior changes.
Anti-patterns the eval should reject
- Replacing
split("\\R", -1) with String.lines() without auditing linebreak semantics.
- Claiming both APIs are equivalent because both process "lines."
- Dropping Unicode linebreak behavior during a behavior-preserving cleanup.
- Reverting to the old imperative loop when a stream source can preserve the behavior directly.
- Adding broad parser compatibility or new label semantics to hide the behavior change.
Suggested eval name
preserve-regex-line-splitting-in-stream-refactor
Problem
Add a Java Streams skill eval for behavior-preserving stream refactors where the old loop used a domain-specific splitter and the new stream source changed the parser contract.
This is eval-worthy because
String.lines()looks like the natural stream replacement for looping over lines, but it is not behavior-equivalent totext.split("\\R", -1).String.lines()recognizes LF, CR, and CRLF. Java regex\Ralso recognizes linebreaks such as NEL, line separator, and paragraph separator.Code before the prompt was executed
The pre-refactor code scanned each card text field and split logical lines with regex
\R.Prompt that caused the implementation
The maintainer asked for repeated Optional/Streams cleanup passes until actionable findings were gone:
This should have triggered the Streams skill to preserve parser semantics while refactoring the loop into a stream-producing shape.
Later prompt that exposed the issue
A later review found that the stream refactor was not behavior-preserving:
Prompt-produced code before maintainer correction
The agent-produced stream code used
String.lines()as the stream source.Why the prompt-produced code is bad
The code is not bad because it uses streams. It is bad because it silently changes the parser's definition of a logical line.
\Rlinebreak sequences.String.lines()recognizes a narrower set of line terminators.Maintainer-preferred code
Keep the stream-producing shape, but use a stream source that preserves the previous
\Rsplitting semantics.Why the replacement is better
The replacement keeps the intended stream pipeline while preserving the old parser contract.
LINE_BREAK.splitAsStream(text)still produces a stream of logical lines.\Rgrammar.Desired eval behavior
Pattern.splitAsStream(...)when the old contract is regex-based splitting.String.lines()is not always equivalent tosplit("\\R", -1).Anti-patterns the eval should reject
split("\\R", -1)withString.lines()without auditing linebreak semantics.Suggested eval name
preserve-regex-line-splitting-in-stream-refactor