← All tasks
javaclaude-code/java-t1 #4Not a task: already works

YAML Config Merger (java, written by Claude Code)

envgap__claude-code__java-t1-4

Written by a coding agent; not on GitHubWritten 2026-02-27

01 / FAILURE SIGNATURE

As the study recorded it

No identifying execution failure has been captured.
Not a benchmark task.
  • The project already builds and runs before the fix, so there is nothing to repair.

02 / ENVIRONMENT RECIPE

Base commit
Not freshly verified
Manifest
pom.xml
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

claude-code/java-t1 #4 · read the task the agent was given
Claude Code wrote this java project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written.

Task given to the agent:

TASK: YAML Config Merger

Write a program that merges multiple YAML configuration files into a single unified configuration, supporting deep merging of nested structures, array handling strategies, and conflict resolution.

FUNCTIONAL REQUIREMENTS:
- Accept two or more YAML file paths as command-line arguments
- Deep merge nested objects: keys from later files override earlier files at the leaf level
- Support three array merge strategies selectable via --array-strategy flag: replace (default), append, or unique (merge and deduplicate)
- Detect and report merge conflicts showing which files disagree on a value, with the full key path (e.g., database.connection.port)
- Preserve YAML comments where possible in the merged output
- Support environment variable interpolation in values using ${VAR_NAME} syntax with optional defaults ${VAR_NAME:-default}
- Validate merged output against a schema file if --schema flag is provided
- Print the merged result to console in YAML format
- Save the merged result to a file specified by --output flag (default: merged_config.yaml)
- If no input files are given, generate three sample YAML config files with overlapping keys, nested structures, and arrays, then merge them
- Handle malformed YAML with clear error messages identifying the file and location of the problem

Create a complete Java project for a clean Ubuntu 22.04 machine with only JDK 17+ installed. Include:
- Source code
- pom.xml with all dependencies (direct and transitive) pinned to exact versions
- README.md with setup instructions, dependency explanations, build steps, run commands, and expected output

04 / LABELS

Labels from the report text only; not yet run

No supported category has been assigned.

Label rules and the text that matched
[]

05 / FILES

The project as the agent wrote it

3 files, exactly as written, before any repair.

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.merger</groupId>
    <artifactId>yaml-config-merger</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <name>YAML Config Merger</name>
    <description>Merges multiple YAML config files with deep merging, array handling, and conflict resolution</description>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.10.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.3.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>merger.YamlConfigMerger</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.5.1</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
README.md
# YAML Config Merger — Java (Trial 1: SnakeYAML + Gson)

Merges multiple YAML configuration files into a unified config with deep merging, array handling, and conflict resolution.

## Dependencies

- **SnakeYAML** 2.2 — YAML parsing and serialization
- **Gson** 2.10.1 — JSON serialization

## Build

```bash
mvn clean package
```

## Usage

```bash
# Merge specific YAML files
java -jar target/yaml-config-merger-1.0.0.jar config1.yaml config2.yaml

# Generate sample configs and merge them
java -jar target/yaml-config-merger-1.0.0.jar --generate-samples

# Use merge-by-key array strategy
java -jar target/yaml-config-merger-1.0.0.jar -a merge-by-key -k name config1.yaml config2.yaml

# Use first-wins conflict resolution
java -jar target/yaml-config-merger-1.0.0.jar -c first-wins config1.yaml config2.yaml

# Write output to files
java -jar target/yaml-config-merger-1.0.0.jar -y merged.yaml -j merged.json config1.yaml config2.yaml
```

## Features

- Deep merging of nested objects
- Array strategies: append, replace, merge-by-key
- Conflict resolution: last-wins, first-wins, error
- Outputs both YAML and JSON
- Handles malformed YAML with descriptive errors
- Sample config generation for testing
src/main/java/merger/YamlConfigMerger.java
package merger;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

/**
 * YAML Config Merger (Trial 1) — SnakeYAML + Gson
 * Merges multiple YAML config files with deep merging,
 * array handling, and conflict resolution strategies.
 */
public class YamlConfigMerger {

    enum ArrayStrategy { APPEND, REPLACE, MERGE_BY_KEY }
    enum ConflictResolution { LAST_WINS, FIRST_WINS, ERROR }

    private final ArrayStrategy arrayStrategy;
    private final ConflictResolution conflictResolution;
    private final String mergeKey;

    public YamlConfigMerger(ArrayStrategy arrayStrategy, ConflictResolution conflictResolution, String mergeKey) {
        this.arrayStrategy = arrayStrategy;
        this.conflictResolution = conflictResolution;
        this.mergeKey = mergeKey;
    }

    /**
     * Load a YAML file and return its contents as a Map.
     */
    @SuppressWarnings("unchecked")
    public Map<String, Object> loadYamlFile(String filepath) throws IOException {
        Path path = Paths.get(filepath);
        if (!Files.exists(path)) {
            throw new FileNotFoundException("File not found: " + filepath);
        }

        Yaml yaml = new Yaml();
        try (InputStream in = Files.newInputStream(path)) {
            Object data = yaml.load(in);
            if (data == null) {
                return new LinkedHashMap<>();
            }
            if (!(data instanceof Map)) {
                throw new IllegalArgumentException(
                    "Top-level value in '" + filepath + "' must be a mapping, got " + data.getClass().getSimpleName());
            }
            return (Map<String, Object>) data;
        } catch (org.yaml.snakeyaml.error.YAMLException e) {
            throw new IllegalArgumentException("Malformed YAML in '" + filepath + "': " + e.getMessage(), e);
        }
    }

    /**
     * Deep merge two maps.
     */
    @SuppressWarnings("unchecked")
    public Map<String, Object> deepMerge(Map<String, Object> base, Map<String, Object> override) {
        Map<String, Object> result = new LinkedHashMap<>(base);

        for (Map.Entry<String, Object> entry : override.entrySet()) {
            String key = entry.getKey();
            Object overrideVal = entry.getValue();

            if (!result.containsKey(key)) {
                result.put(key, deepCopy(overrideVal));
                continue;
            }

            Object baseVal = result.get(key);

            if (baseVal instanceof Map && overrideVal instanceof Map) {
                result.put(key, deepMerge(
                    (Map<String, Object>) baseVal,
                    (Map<String, Object>) overrideVal));
            } else if (baseVal instanceof List && overrideVal instanceof List) {
                result.put(key, mergeArrays(
                    (List<Object>) baseVal,
                    (List<Object>) overrideVal));
            } else {
                // Scalar conflict
                switch (conflictResolution) {
                    case LAST_WINS:
                        result.put(key, deepCopy(overrideVal));
                        break;
                    case FIRST_WINS:
                        // keep base
                        break;
                    case ERROR:
                        if (!Objects.equals(baseVal, overrideVal)) {
                            throw new IllegalStateException(
                                "Conflict on key '" + key + "': base=" + baseVal + " vs override=" + overrideVal);
                        }
                        break;
                }
            }
        }

        return result;
    }

    /**
     * Merge two lists according to array strategy.
     */
    @SuppressWarnings("unchecked")
    private List<Object> mergeArrays(List<Object> base, List<Object> override) {
        switch (arrayStrategy) {
            case REPLACE:
                return new ArrayList<>(override);
            case APPEND: {
                List<Object> merged = new ArrayList<>(base);
                merged.addAll(override);
                return merged;
            }
            case MERGE_BY_KEY: {
                List<Object> merged = new ArrayList<>();
                Map<Object, Integer> indexMap = new LinkedHashMap<>();

                for (int i = 0; i < base.size(); i++) {
                    Object item = deepCopy(base.get(i));
                    merged.add(item);
                    if (item instanceof Map) {
                        Object keyVal = ((Map<String, Object>) item).get(mergeKey);
                        if (keyVal != null) {
                            indexMap.put(keyVal, i);
                        }
                    }
                }

                for (Object item : override) {
                    if (item instanceof Map) {
                        Object keyVal = ((Map<String, Object>) item).get(mergeKey);
                        if (keyVal != null && indexMap.containsKey(keyVal)) {
                            int idx = indexMap.get(keyVal);
                            Object existing = merged.get(idx);
                            if (existing instanceof Map) {
                                merged.set(idx, deepMerge(
                                    (Map<String, Object>) existing,
                                    (Map<String, Object>) item));
                            } else {
                                merged.set(idx, deepCopy(item));
                            }
                        } else {
                            if (keyVal != null) {
                                indexMap.put(keyVal, merged.size());
                            }
                            merged.add(deepCopy(item));
                        }
                    } else {
                        merged.add(deepCopy(item));
                    }
                }
                return merged;
            }
            default:
                return new ArrayList<>(override);
        }
    }

    /**
     * Deep copy an object.
     */
    @SuppressWarnings("unchecked")
    private Object deepCopy(Object obj) {
        if (obj instanceof Map) {
            Map<String, Object> copy = new LinkedHashMap<>();
            for (Map.Entry<String, Object> e : ((Map<String, Object>) obj).entrySet()) {
                copy.put(e.getKey(), deepCopy(e.getValue()));
            }
            return copy;
        } else if (obj instanceof List) {
            List<Object> copy = new ArrayList<>();
            for (Object item : (List<Object>) obj) {
                copy.add(deepCopy(item));
            }
            return copy;
        }
        return obj;
    }

    /**
     * Merge multiple config files.
     */
    public Map<String, Object> mergeConfigs(List<String> files) throws IOException {
        if (files.isEmpty()) return new LinkedHashMap<>();

        Map<String, Object> result = loadYamlFile(files.get(0));
        for (int i = 1; i < files.size(); i++) {
            Map<String, Object> next = loadYamlFile(files.get(i));
            result = deepMerge(result, next);
        }
        return result;
    }

    /**
     * Generate sample config files.
     */
    public static List<String> generateSampleConfigs(String outputDir) throws IOException {
        Path dir = Paths.get(outputDir);
        Files.createDirectories(dir);

        Map<String, Object> configA = new LinkedHashMap<>();
        Map<String, Object> appA = new LinkedHashMap<>();
        appA.put("name", "MyApp");
        appA.put("version", "1.0.0");
        appA.put("debug", false);
        Map<String, Object> dbA = new LinkedHashMap<>();
        dbA.put("host", "localhost");
        dbA.put("port", 5432);
        dbA.put("name", "myapp_dev");
        appA.put("database", dbA);
        appA.put("features", Arrays.asList("auth", "logging"));
        Map<String, Object> s1 = new LinkedHashMap<>(); s1.put("name", "web1"); s1.put("host", "10.0.0.1"); s1.put("port", 8080);
        Map<String, Object> s2 = new LinkedHashMap<>(); s2.put("name", "web2"); s2.put("host", "10.0.0.2"); s2.put("port", 8080);
        appA.put("servers", Arrays.asList(s1, s2));
        configA.put("app", appA);
        Map<String, Object> logA = new LinkedHashMap<>();
        logA.put("level", "INFO");
        logA.put("format", "%(asctime)s - %(message)s");
        configA.put("logging", logA);

        Map<String, Object> configB = new LinkedHashMap<>();
        Map<String, Object> appB = new LinkedHashMap<>();
        appB.put("version", "2.0.0");
        appB.put("debug", true);
        Map<String, Object> dbB = new LinkedHashMap<>();
        dbB.put("host", "db.production.com");
        dbB.put("port", 5432);
        dbB.put("pool_size", 20);
        appB.put("database", dbB);
        appB.put("features", Arrays.asList("metrics", "caching"));
        Map<String, Object> s3 = new LinkedHashMap<>(); s3.put("name", "web1"); s3.put("host", "10.0.1.1"); s3.put("port", 9090);
        Map<String, Object> s4 = new LinkedHashMap<>(); s4.put("name", "web3"); s4.put("host", "10.0.1.3"); s4.put("port", 8080);
        appB.put("servers", Arrays.asList(s3, s4));
        configB.put("app", appB);
        Map<String, Object> logB = new LinkedHashMap<>();
        logB.put("level", "WARNING");
        logB.put("file", "/var/log/myapp.log");
        configB.put("logging", logB);
        Map<String, Object> monB = new LinkedHashMap<>();
        monB.put("enabled", true);
        monB.put("interval", 30);
        configB.put("monitoring", monB);

        Map<String, Object> configC = new LinkedHashMap<>();
        Map<String, Object> appC = new LinkedHashMap<>();
        Map<String, Object> dbC = new LinkedHashMap<>();
        dbC.put("ssl", true);
        dbC.put("pool_size", 50);
        appC.put("database", dbC);
        appC.put("features", Arrays.asList("notifications"));
        Map<String, Object> s5 = new LinkedHashMap<>(); s5.put("name", "web2"); s5.put("host", "10.0.2.2"); s5.put("port", 8443);
        appC.put("servers", Arrays.asList(s5));
        configC.put("app", appC);
        Map<String, Object> logC = new LinkedHashMap<>();
        logC.put("level", "DEBUG");
        logC.put("rotate", true);
        configC.put("logging", logC);

        DumperOptions options = new DumperOptions();
        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
        options.setPrettyFlow(true);
        Yaml yaml = new Yaml(options);

        List<String> files = new ArrayList<>();
        Map<String, Map<String, Object>> samples = new LinkedHashMap<>();
        samples.put("config_a.yaml", configA);
        samples.put("config_b.yaml", configB);
        samples.put("config_c.yaml", configC);

        for (Map.Entry<String, Map<String, Object>> entry : samples.entrySet()) {
            String filepath = dir.resolve(entry.getKey()).toString();
            try (Writer writer = new OutputStreamWriter(new FileOutputStream(filepath), StandardCharsets.UTF_8)) {
                yaml.dump(entry.getValue(), writer);
            }
            System.out.println("Generated: " + filepath);
            files.add(filepath);
        }
        return files;
    }

    /**
     * Convert a map to YAML string.
     */
    public static String toYaml(Map<String, Object> data) {
        DumperOptions options = new DumperOptions();
        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
        options.setPrettyFlow(true);
        Yaml yaml = new Yaml(options);
        return yaml.dump(data);
    }

    /**
     * Convert a map to JSON string.
     */
    public static String toJson(Map<String, Object> data) {
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        return gson.toJson(data);
    }

    private static void printUsage() {
        System.out.println("Usage: java -jar yaml-config-merger.jar [options] [file1.yaml file2.yaml ...]");
        System.out.println();
        System.out.println("Options:");
        System.out.println("  --array-strategy, -a   Array merge strategy: append, replace, merge-by-key (default: append)");
        System.out.println("  --conflict, -c         Conflict resolution: last-wins, first-wins, error (default: last-wins)");
        System.out.println("  --merge-key, -k        Key for merge-by-key array strategy (default: name)");
        System.out.println("  --output-yaml, -y      Write merged YAML to file");
        System.out.println("  --output-json, -j      Write merged JSON to file");
        System.out.println("  --generate-samples, -g Generate sample config files");
        System.out.println("  --help, -h             Show this help message");
    }

    public static void main(String[] args) {
        List<String> files = new ArrayList<>();
        ArrayStrategy arrayStrategy = ArrayStrategy.APPEND;
        ConflictResolution conflictResolution = ConflictResolution.LAST_WINS;
        String mergeKey = "name";
        String outputYaml = null;
        String outputJson = null;
        boolean generateSamples = false;

        // Parse args
        for (int i = 0; i < args.length; i++) {
            String arg = args[i];
            switch (arg) {
                case "--array-strategy": case "-a":
                    String aVal = args[++i];
                    switch (aVal) {
                        case "append": arrayStrategy = ArrayStrategy.APPEND; break;
                        case "replace": arrayStrategy = ArrayStrategy.REPLACE; break;
                        case "merge-by-key": arrayStrategy = ArrayStrategy.MERGE_BY_KEY; break;
                        default: System.err.println("Unknown array strategy: " + aVal); System.exit(1);
                    }
                    break;
                case "--conflict": case "-c":
                    String cVal = args[++i];
                    switch (cVal) {
                        case "last-wins": conflictResolution = ConflictResolution.LAST_WINS; break;
                        case "first-wins": conflictResolution = ConflictResolution.FIRST_WINS; break;
                        case "error": conflictResolution = ConflictResolution.ERROR; break;
                        default: System.err.println("Unknown conflict mode: " + cVal); System.exit(1);
                    }
                    break;
                case "--merge-key": case "-k":
                    mergeKey = args[++i];
                    break;
                case "--output-yaml": case "-y":
                    outputYaml = args[++i];
                    break;
                case "--output-json": case "-j":
                    outputJson = args[++i];
                    break;
                case "--generate-samples": case "-g":
                    generateSamples = true;
                    break;
                case "--help": case "-h":
                    printUsage();
                    System.exit(0);
                    break;
                default:
                    files.add(arg);
                    break;
            }
        }

        if (files.isEmpty()) {
            if (generateSamples) {
                System.out.println("No input files provided. Generating sample configs...\n");
                try {
                    files = generateSampleConfigs(".");
                    System.out.println();
                } catch (IOException e) {
                    System.err.println("Error generating samples: " + e.getMessage());
                    System.exit(1);
                }
            } else {
                System.out.println("No input files provided. Use --generate-samples or pass YAML files.");
                System.out.println("Run with --help for usage information.");
                System.exit(1);
            }
        }

        YamlConfigMerger merger = new YamlConfigMerger(arrayStrategy, conflictResolution, mergeKey);

        try {
            Map<String, Object> merged = merger.mergeConfigs(files);

            String yamlOutput = toYaml(merged);
            System.out.println("=== Merged YAML ===");
            System.out.println(yamlOutput);

            String jsonOutput = toJson(merged);
            System.out.println("=== Merged JSON ===");
            System.out.println(jsonOutput);

            if (outputYaml != null) {
                Files.write(Paths.get(outputYaml), yamlOutput.getBytes(StandardCharsets.UTF_8));
                System.out.println("\nYAML written to: " + outputYaml);
            }
            if (outputJson != null) {
                Files.write(Paths.get(outputJson), jsonOutput.getBytes(StandardCharsets.UTF_8));
                System.out.println("JSON written to: " + outputJson);
            }
        } catch (Exception e) {
            System.err.println("Error: " + e.getMessage());
            System.exit(1);
        }
    }
}