Skip to content

feat: add eval for constructor-owned immutable snapshots #58

Description

@martinfrancois

Problem

The Streams capture workflow missed a refactor that removed a repeated deep-copy stream from a record accessor after the compact constructor had already established the deep immutable snapshot. This is eval-worthy because the correct simplification depends on proving a constructor-owned invariant; blindly removing either copy exposes mutable state, while blindly retaining both traverses and recopies every nested map on every read.

Area

Evals or scoring

Describe the solution you want

Add a self-contained reference eval that teaches the Streams skill to inspect constructor and canonical-boundary invariants before retaining normalization or defensive-copy pipelines at every read.

Code before the prompt was executed

record LogInfo(List<Map<String, Object>> sessionLogs) {
    LogInfo {
        sessionLogs = sessionLogs.stream().map(Map::copyOf).toList();
    }

    @Override
    public List<Map<String, Object>> sessionLogs() {
        return sessionLogs.stream().map(Map::copyOf).toList();
    }
}

Prompt that caused the implementation

The original prompt required a complete Java-source API-reuse audit:

  • Audit every tracked Java source for worthwhile replacements with the Java standard library or a directly declared dependency.
  • Implement only changes that clearly reduce code, complexity, duplication, or maintenance risk.
  • Preserve observable behavior, ordering, failure semantics, null handling, concurrency, security, and relevant performance characteristics.
  • Add focused regression tests for each affected boundary.

The prompt should have activated the Streams skill because it explicitly asks the agent to inspect and simplify hand-written collection transformations.

Later prompt that exposed the issue

A maintainer later asked whether the Streams capture issue had been created and requested that the capture skill be strengthened so cases like this are not missed again. The later prompt exposes the capture failure; it should not be the only eval trigger.

Prompt-produced code before maintainer correction

The audit produced the constructor and accessor shown above. Both copies are individually safe, but the accessor repeats work whose invariant is already owned by the constructor.

Why the prompt-produced code is weak

The compact constructor detaches the input list and snapshots each nested map. Repeating map(Map::copyOf).toList() in the accessor needlessly traverses and recopies already-immutable maps on every call. Returning the field directly may still violate an exposed-representation analyzer, so the safe simplification is an outer defensive copy rather than removing the accessor boundary.

Behavior-equivalence analysis

The preferred shape preserves encounter order, outer immutability, nested-map immutability, null rejection, and source detachment. The eval must require tests for mutation of the source list, mutation of a source map, mutation of the returned list, mutation of a returned map, null elements, and encounter order. It must not assume nested values are immutable without constructor evidence.

Maintainer-preferred code

record LogInfo(List<Map<String, Object>> sessionLogs) {
    LogInfo {
        sessionLogs = sessionLogs.stream().map(Map::copyOf).toList();
    }

    @Override
    public List<Map<String, Object>> sessionLogs() {
        return List.copyOf(sessionLogs);
    }
}

Why the replacement is better

The constructor remains the single deep-snapshot boundary. The accessor reasserts only the outer immutable boundary required by exposed-representation analysis, without recopying every nested map.

Desired eval behavior

Anti-patterns the eval should reject

  • Returning the backing field without checking exposed-representation requirements.
  • Removing the constructor's Map.copyOf operation.
  • Assuming nested values are immutable without proof.
  • Retaining repeated deep-copy work solely because each individual copy is safe.

Suggested eval name

constructor-owned-immutable-snapshot

Examples

The before/after record above is the minimal example. The implementation should begin in evals-reference/, be classified with the repository workflow, and add only the runtime guidance demonstrated necessary by the eval.

Alternatives considered

Extending #56 was considered, but that issue teaches parser-versus-constructor deduplication. Combining the two would blur different failure modes and equivalence boundaries.

Current workaround

Manually inspect constructors and immutable value-object boundaries whenever a stream performs normalization or defensive copying.

Additional context

No user-facing Java contract change is intended. The proposed skill work is behavior-preserving and should add a Java 10 List.copyOf compatibility note if the runtime example uses it.

AI Assistance

  • AI-assisted issue
  • I confirm I understand and reviewed this request

AI prompts / session logs (optional)

Prepared with Codex assistance from a maintainer review of a behavior-preserving Java API-reuse audit. All code and requirements above are sanitized and self-contained.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions