← All tasks
cppclaude-code/cpp-t1 #4Not a task: already works

YAML Config Merger (cpp, written by Claude Code)

envgap__claude-code__cpp-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
CMakeLists.txt
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

claude-code/cpp-t1 #4 · read the task the agent was given
Claude Code wrote this cpp 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 C++ project for a clean Ubuntu 22.04 machine with only G++ 12+ and CMake 3.22+ installed. Include:
- Source code
- CMakeLists.txt 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.

CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(yaml_config_merger VERSION 1.0.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)

# yaml-cpp 0.8.0
FetchContent_Declare(
    yaml-cpp
    GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git
    GIT_TAG 0.8.0
)
FetchContent_MakeAvailable(yaml-cpp)

# nlohmann/json 3.11.3
FetchContent_Declare(
    nlohmann_json
    GIT_REPOSITORY https://github.com/nlohmann/json.git
    GIT_TAG v3.11.3
)
FetchContent_MakeAvailable(nlohmann_json)

add_executable(merger merger.cpp)
target_link_libraries(merger PRIVATE yaml-cpp::yaml-cpp nlohmann_json::nlohmann_json)
merger.cpp
/**
 * YAML Config Merger (Trial 1) — yaml-cpp + nlohmann/json
 * Merges multiple YAML config files with deep merging,
 * array handling, and conflict resolution strategies.
 */

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <stdexcept>
#include <algorithm>
#include <map>

#include <yaml-cpp/yaml.h>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

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

// Forward declarations
YAML::Node deepMerge(const YAML::Node& base, const YAML::Node& override_node,
                      ArrayStrategy arrayStrategy, ConflictResolution conflict,
                      const std::string& mergeKey);

/**
 * Merge two YAML sequences according to the chosen strategy.
 */
YAML::Node mergeArrays(const YAML::Node& base, const YAML::Node& override_node,
                        ArrayStrategy strategy, const std::string& mergeKey) {
    YAML::Node result;

    if (strategy == ArrayStrategy::REPLACE) {
        return YAML::Clone(override_node);
    }

    if (strategy == ArrayStrategy::APPEND) {
        for (std::size_t i = 0; i < base.size(); i++) {
            result.push_back(YAML::Clone(base[i]));
        }
        for (std::size_t i = 0; i < override_node.size(); i++) {
            result.push_back(YAML::Clone(override_node[i]));
        }
        return result;
    }

    if (strategy == ArrayStrategy::MERGE_BY_KEY) {
        // Build index of existing items by merge key
        std::vector<YAML::Node> merged;
        std::map<std::string, std::size_t> indexMap;

        for (std::size_t i = 0; i < base.size(); i++) {
            merged.push_back(YAML::Clone(base[i]));
            if (base[i].IsMap() && base[i][mergeKey]) {
                indexMap[base[i][mergeKey].as<std::string>()] = i;
            }
        }

        for (std::size_t i = 0; i < override_node.size(); i++) {
            if (override_node[i].IsMap() && override_node[i][mergeKey]) {
                std::string keyVal = override_node[i][mergeKey].as<std::string>();
                auto it = indexMap.find(keyVal);
                if (it != indexMap.end()) {
                    merged[it->second] = deepMerge(
                        merged[it->second], override_node[i],
                        ArrayStrategy::MERGE_BY_KEY,
                        ConflictResolution::LAST_WINS, mergeKey);
                } else {
                    indexMap[keyVal] = merged.size();
                    merged.push_back(YAML::Clone(override_node[i]));
                }
            } else {
                merged.push_back(YAML::Clone(override_node[i]));
            }
        }

        for (auto& node : merged) {
            result.push_back(node);
        }
        return result;
    }

    return YAML::Clone(override_node);
}

/**
 * Deep merge two YAML map nodes.
 */
YAML::Node deepMerge(const YAML::Node& base, const YAML::Node& override_node,
                      ArrayStrategy arrayStrategy, ConflictResolution conflict,
                      const std::string& mergeKey) {
    if (!base.IsMap() || !override_node.IsMap()) {
        if (conflict == ConflictResolution::FIRST_WINS) {
            return YAML::Clone(base);
        }
        return YAML::Clone(override_node);
    }

    YAML::Node result = YAML::Clone(base);

    for (auto it = override_node.begin(); it != override_node.end(); ++it) {
        std::string key = it->first.as<std::string>();
        YAML::Node overrideVal = it->second;

        if (!result[key]) {
            result[key] = YAML::Clone(overrideVal);
            continue;
        }

        YAML::Node baseVal = result[key];

        if (baseVal.IsMap() && overrideVal.IsMap()) {
            result[key] = deepMerge(baseVal, overrideVal, arrayStrategy, conflict, mergeKey);
        } else if (baseVal.IsSequence() && overrideVal.IsSequence()) {
            result[key] = mergeArrays(baseVal, overrideVal, arrayStrategy, mergeKey);
        } else {
            // Scalar conflict
            if (conflict == ConflictResolution::LAST_WINS) {
                result[key] = YAML::Clone(overrideVal);
            } else if (conflict == ConflictResolution::FIRST_WINS) {
                // keep base
            } else if (conflict == ConflictResolution::ERROR) {
                std::string baseStr, overrideStr;
                if (baseVal.IsScalar()) baseStr = baseVal.as<std::string>();
                if (overrideVal.IsScalar()) overrideStr = overrideVal.as<std::string>();
                if (baseStr != overrideStr) {
                    throw std::runtime_error(
                        "Conflict on key '" + key + "': base=" + baseStr +
                        " vs override=" + overrideStr);
                }
            }
        }
    }

    return result;
}

/**
 * Load a YAML file.
 */
YAML::Node loadYamlFile(const std::string& filepath) {
    std::ifstream fin(filepath);
    if (!fin.is_open()) {
        throw std::runtime_error("File not found: " + filepath);
    }

    YAML::Node doc;
    try {
        doc = YAML::Load(fin);
    } catch (const YAML::Exception& e) {
        throw std::runtime_error("Malformed YAML in '" + filepath + "': " + e.what());
    }

    if (doc.IsNull()) {
        return YAML::Node(YAML::NodeType::Map);
    }
    if (!doc.IsMap()) {
        throw std::runtime_error("Top-level value in '" + filepath + "' must be a mapping");
    }
    return doc;
}

/**
 * Convert YAML node to nlohmann::json.
 */
json yamlToJson(const YAML::Node& node) {
    if (node.IsNull()) {
        return json(nullptr);
    }
    if (node.IsScalar()) {
        // Try bool
        try {
            bool b = node.as<bool>();
            // Verify it was actually a bool token
            std::string s = node.as<std::string>();
            if (s == "true" || s == "false" || s == "True" || s == "False" ||
                s == "TRUE" || s == "FALSE") {
                return json(b);
            }
        } catch (...) {}
        // Try int
        try {
            int64_t i = node.as<int64_t>();
            return json(i);
        } catch (...) {}
        // Try double
        try {
            double d = node.as<double>();
            return json(d);
        } catch (...) {}
        // Fall back to string
        return json(node.as<std::string>());
    }
    if (node.IsSequence()) {
        json arr = json::array();
        for (std::size_t i = 0; i < node.size(); i++) {
            arr.push_back(yamlToJson(node[i]));
        }
        return arr;
    }
    if (node.IsMap()) {
        json obj = json::object();
        for (auto it = node.begin(); it != node.end(); ++it) {
            obj[it->first.as<std::string>()] = yamlToJson(it->second);
        }
        return obj;
    }
    return json(nullptr);
}

/**
 * Generate sample YAML config files.
 */
std::vector<std::string> generateSampleConfigs(const std::string& outputDir = ".") {
    YAML::Node configA;
    configA["app"]["name"] = "MyApp";
    configA["app"]["version"] = "1.0.0";
    configA["app"]["debug"] = false;
    configA["app"]["database"]["host"] = "localhost";
    configA["app"]["database"]["port"] = 5432;
    configA["app"]["database"]["name"] = "myapp_dev";
    configA["app"]["features"].push_back("auth");
    configA["app"]["features"].push_back("logging");
    YAML::Node s1; s1["name"] = "web1"; s1["host"] = "10.0.0.1"; s1["port"] = 8080;
    YAML::Node s2; s2["name"] = "web2"; s2["host"] = "10.0.0.2"; s2["port"] = 8080;
    configA["app"]["servers"].push_back(s1);
    configA["app"]["servers"].push_back(s2);
    configA["logging"]["level"] = "INFO";
    configA["logging"]["format"] = "%(asctime)s - %(message)s";

    YAML::Node configB;
    configB["app"]["version"] = "2.0.0";
    configB["app"]["debug"] = true;
    configB["app"]["database"]["host"] = "db.production.com";
    configB["app"]["database"]["port"] = 5432;
    configB["app"]["database"]["pool_size"] = 20;
    configB["app"]["features"].push_back("metrics");
    configB["app"]["features"].push_back("caching");
    YAML::Node s3; s3["name"] = "web1"; s3["host"] = "10.0.1.1"; s3["port"] = 9090;
    YAML::Node s4; s4["name"] = "web3"; s4["host"] = "10.0.1.3"; s4["port"] = 8080;
    configB["app"]["servers"].push_back(s3);
    configB["app"]["servers"].push_back(s4);
    configB["logging"]["level"] = "WARNING";
    configB["logging"]["file"] = "/var/log/myapp.log";
    configB["monitoring"]["enabled"] = true;
    configB["monitoring"]["interval"] = 30;

    YAML::Node configC;
    configC["app"]["database"]["ssl"] = true;
    configC["app"]["database"]["pool_size"] = 50;
    configC["app"]["features"].push_back("notifications");
    YAML::Node s5; s5["name"] = "web2"; s5["host"] = "10.0.2.2"; s5["port"] = 8443;
    configC["app"]["servers"].push_back(s5);
    configC["logging"]["level"] = "DEBUG";
    configC["logging"]["rotate"] = true;

    struct SampleConfig {
        std::string name;
        YAML::Node data;
    };
    std::vector<SampleConfig> samples = {
        {"config_a.yaml", configA},
        {"config_b.yaml", configB},
        {"config_c.yaml", configC}
    };

    std::vector<std::string> files;
    for (auto& sc : samples) {
        std::string filepath = outputDir + "/" + sc.name;
        std::ofstream fout(filepath);
        if (!fout.is_open()) {
            throw std::runtime_error("Cannot write to: " + filepath);
        }
        fout << sc.data;
        fout.close();
        std::cout << "Generated: " << filepath << std::endl;
        files.push_back(filepath);
    }
    return files;
}

void printUsage(const char* progName) {
    std::cout << "Usage: " << progName << " [options] [file1.yaml file2.yaml ...]\n\n"
              << "Options:\n"
              << "  --array-strategy, -a   Array merge strategy: append, replace, merge-by-key (default: append)\n"
              << "  --conflict, -c         Conflict resolution: last-wins, first-wins, error (default: last-wins)\n"
              << "  --merge-key, -k        Key for merge-by-key array strategy (default: name)\n"
              << "  --output-yaml, -y      Write merged YAML to file\n"
              << "  --output-json, -j      Write merged JSON to file\n"
              << "  --generate-samples, -g Generate sample config files\n"
              << "  --help, -h             Show this help message\n";
}

int main(int argc, char* argv[]) {
    std::vector<std::string> files;
    ArrayStrategy arrayStrategy = ArrayStrategy::APPEND;
    ConflictResolution conflict = ConflictResolution::LAST_WINS;
    std::string mergeKey = "name";
    std::string outputYaml, outputJson;
    bool generateSamples = false;

    // Parse CLI args
    for (int i = 1; i < argc; i++) {
        std::string arg = argv[i];
        if (arg == "--array-strategy" || arg == "-a") {
            if (++i >= argc) { std::cerr << "Missing value for " << arg << std::endl; return 1; }
            std::string val = argv[i];
            if (val == "append") arrayStrategy = ArrayStrategy::APPEND;
            else if (val == "replace") arrayStrategy = ArrayStrategy::REPLACE;
            else if (val == "merge-by-key") arrayStrategy = ArrayStrategy::MERGE_BY_KEY;
            else { std::cerr << "Unknown array strategy: " << val << std::endl; return 1; }
        } else if (arg == "--conflict" || arg == "-c") {
            if (++i >= argc) { std::cerr << "Missing value for " << arg << std::endl; return 1; }
            std::string val = argv[i];
            if (val == "last-wins") conflict = ConflictResolution::LAST_WINS;
            else if (val == "first-wins") conflict = ConflictResolution::FIRST_WINS;
            else if (val == "error") conflict = ConflictResolution::ERROR;
            else { std::cerr << "Unknown conflict mode: " << val << std::endl; return 1; }
        } else if (arg == "--merge-key" || arg == "-k") {
            if (++i >= argc) { std::cerr << "Missing value for " << arg << std::endl; return 1; }
            mergeKey = argv[i];
        } else if (arg == "--output-yaml" || arg == "-y") {
            if (++i >= argc) { std::cerr << "Missing value for " << arg << std::endl; return 1; }
            outputYaml = argv[i];
        } else if (arg == "--output-json" || arg == "-j") {
            if (++i >= argc) { std::cerr << "Missing value for " << arg << std::endl; return 1; }
            outputJson = argv[i];
        } else if (arg == "--generate-samples" || arg == "-g") {
            generateSamples = true;
        } else if (arg == "--help" || arg == "-h") {
            printUsage(argv[0]);
            return 0;
        } else {
            files.push_back(arg);
        }
    }

    if (files.empty()) {
        if (generateSamples) {
            std::cout << "No input files provided. Generating sample configs...\n" << std::endl;
            try {
                files = generateSampleConfigs();
            } catch (const std::exception& e) {
                std::cerr << "Error generating samples: " << e.what() << std::endl;
                return 1;
            }
            std::cout << std::endl;
        } else {
            std::cout << "No input files provided. Use --generate-samples or pass YAML files." << std::endl;
            std::cout << "Run with --help for usage information." << std::endl;
            return 1;
        }
    }

    // Load and merge
    try {
        YAML::Node result = loadYamlFile(files[0]);
        for (std::size_t i = 1; i < files.size(); i++) {
            YAML::Node next = loadYamlFile(files[i]);
            result = deepMerge(result, next, arrayStrategy, conflict, mergeKey);
        }

        // Output YAML
        YAML::Emitter yamlEmitter;
        yamlEmitter << result;
        std::string yamlStr = yamlEmitter.c_str();

        std::cout << "=== Merged YAML ===" << std::endl;
        std::cout << yamlStr << std::endl;

        // Output JSON
        json j = yamlToJson(result);
        std::string jsonStr = j.dump(2);

        std::cout << "\n=== Merged JSON ===" << std::endl;
        std::cout << jsonStr << std::endl;

        // Write files if requested
        if (!outputYaml.empty()) {
            std::ofstream fout(outputYaml);
            fout << yamlStr;
            fout.close();
            std::cout << "\nYAML written to: " << outputYaml << std::endl;
        }
        if (!outputJson.empty()) {
            std::ofstream fout(outputJson);
            fout << jsonStr;
            fout.close();
            std::cout << "JSON written to: " << outputJson << std::endl;
        }
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}
README.md
# YAML Config Merger — C++ (Trial 1: yaml-cpp + nlohmann/json)

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

## Dependencies

- **yaml-cpp** 0.8.0 — YAML parsing and serialization
- **nlohmann/json** 3.11.3 — JSON serialization

## Build

```bash
mkdir build && cd build
cmake ..
cmake --build .
```

## Usage

```bash
# Merge specific YAML files
./merger config1.yaml config2.yaml config3.yaml

# Generate sample configs and merge them
./merger --generate-samples

# Use merge-by-key array strategy
./merger --array-strategy merge-by-key -k name config1.yaml config2.yaml

# Use first-wins conflict resolution
./merger --conflict first-wins config1.yaml config2.yaml

# Write output to files
./merger -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