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 output04 / 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);
}
}
}