← All tasks
javascriptclaude-code/javascript-t1 #4Not a task: already works

YAML Config Merger (javascript, written by Claude Code)

envgap__claude-code__javascript-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
package.json
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

claude-code/javascript-t1 #4 · read the task the agent was given
Claude Code wrote this javascript 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 JavaScript project for a clean Ubuntu 22.04 machine with only Node.js 20+ (LTS) installed. Include:
- Source code
- package.json 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.

merger.js
#!/usr/bin/env node
/**
 * YAML Config Merger (Trial 1) — js-yaml + lodash
 * Merges multiple YAML config files with deep merging,
 * array handling, and conflict resolution strategies.
 */

"use strict";

const fs = require("fs");
const path = require("path");
const yaml = require("js-yaml");
const _ = require("lodash");

// ---- Array Strategies ----
const ARRAY_STRATEGIES = {
  APPEND: "append",
  REPLACE: "replace",
  MERGE_BY_KEY: "merge-by-key",
};

// ---- Conflict Resolution ----
const CONFLICT_MODES = {
  LAST_WINS: "last-wins",
  FIRST_WINS: "first-wins",
  ERROR: "error",
};

/**
 * Load and parse a YAML file.
 */
function loadYamlFile(filepath) {
  if (!fs.existsSync(filepath)) {
    throw new Error(`File not found: ${filepath}`);
  }
  const content = fs.readFileSync(filepath, "utf-8");
  let data;
  try {
    data = yaml.load(content);
  } catch (e) {
    throw new Error(`Malformed YAML in '${filepath}': ${e.message}`);
  }
  if (data === null || data === undefined) {
    return {};
  }
  if (typeof data !== "object" || Array.isArray(data)) {
    throw new Error(
      `Top-level value in '${filepath}' must be a mapping, got ${typeof data}`
    );
  }
  return data;
}

/**
 * Merge two arrays based on the chosen strategy.
 */
function mergeArrays(base, override, strategy, mergeKey) {
  if (strategy === ARRAY_STRATEGIES.REPLACE) {
    return _.cloneDeep(override);
  }
  if (strategy === ARRAY_STRATEGIES.APPEND) {
    return _.cloneDeep(base).concat(_.cloneDeep(override));
  }
  if (strategy === ARRAY_STRATEGIES.MERGE_BY_KEY) {
    const merged = _.cloneDeep(base);
    const indexMap = {};
    merged.forEach((item, i) => {
      if (_.isPlainObject(item) && item[mergeKey] !== undefined) {
        indexMap[item[mergeKey]] = i;
      }
    });
    for (const item of override) {
      if (_.isPlainObject(item) && item[mergeKey] !== undefined) {
        const keyVal = item[mergeKey];
        if (keyVal in indexMap) {
          const idx = indexMap[keyVal];
          merged[idx] = deepMergeDicts(
            merged[idx],
            item,
            ARRAY_STRATEGIES.MERGE_BY_KEY,
            CONFLICT_MODES.LAST_WINS,
            mergeKey
          );
        } else {
          indexMap[keyVal] = merged.length;
          merged.push(_.cloneDeep(item));
        }
      } else {
        merged.push(_.cloneDeep(item));
      }
    }
    return merged;
  }
  return _.cloneDeep(override);
}

/**
 * Deep merge two plain objects with configurable array and conflict handling.
 */
function deepMergeDicts(base, override, arrayStrategy, conflictResolution, mergeKey) {
  const result = _.cloneDeep(base);

  for (const key of Object.keys(override)) {
    const overrideVal = override[key];

    if (!(key in result)) {
      result[key] = _.cloneDeep(overrideVal);
      continue;
    }

    const baseVal = result[key];

    if (_.isPlainObject(baseVal) && _.isPlainObject(overrideVal)) {
      result[key] = deepMergeDicts(
        baseVal,
        overrideVal,
        arrayStrategy,
        conflictResolution,
        mergeKey
      );
    } else if (Array.isArray(baseVal) && Array.isArray(overrideVal)) {
      result[key] = mergeArrays(baseVal, overrideVal, arrayStrategy, mergeKey);
    } else {
      if (conflictResolution === CONFLICT_MODES.LAST_WINS) {
        result[key] = _.cloneDeep(overrideVal);
      } else if (conflictResolution === CONFLICT_MODES.FIRST_WINS) {
        // keep baseVal
      } else if (conflictResolution === CONFLICT_MODES.ERROR) {
        if (!_.isEqual(baseVal, overrideVal)) {
          throw new Error(
            `Conflict on key '${key}': base=${JSON.stringify(baseVal)} vs override=${JSON.stringify(overrideVal)}`
          );
        }
      }
    }
  }

  return result;
}

/**
 * Merge multiple YAML configs.
 */
function mergeConfigs(files, arrayStrategy, conflictResolution, mergeKey) {
  const configs = files.map((f) => loadYamlFile(f));
  if (configs.length === 0) return {};
  let result = configs[0];
  for (let i = 1; i < configs.length; i++) {
    result = deepMergeDicts(result, configs[i], arrayStrategy, conflictResolution, mergeKey);
  }
  return result;
}

/**
 * Generate sample config files with conflicts.
 */
function generateSampleConfigs(outputDir) {
  outputDir = outputDir || ".";
  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
  }

  const configA = {
    app: {
      name: "MyApp",
      version: "1.0.0",
      debug: false,
      database: { host: "localhost", port: 5432, name: "myapp_dev" },
      features: ["auth", "logging"],
      servers: [
        { name: "web1", host: "10.0.0.1", port: 8080 },
        { name: "web2", host: "10.0.0.2", port: 8080 },
      ],
    },
    logging: { level: "INFO", format: "%(asctime)s - %(message)s" },
  };

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

  const configC = {
    app: {
      database: { ssl: true, pool_size: 50 },
      features: ["notifications"],
      servers: [{ name: "web2", host: "10.0.2.2", port: 8443 }],
    },
    logging: { level: "DEBUG", rotate: true },
  };

  const files = [];
  const samples = [
    ["config_a.yaml", configA],
    ["config_b.yaml", configB],
    ["config_c.yaml", configC],
  ];
  for (const [name, data] of samples) {
    const filepath = path.join(outputDir, name);
    fs.writeFileSync(filepath, yaml.dump(data, { noRefs: true }), "utf-8");
    console.log(`Generated: ${filepath}`);
    files.push(filepath);
  }
  return files;
}

/**
 * Parse CLI arguments.
 */
function parseArgs(argv) {
  const args = {
    files: [],
    arrayStrategy: ARRAY_STRATEGIES.APPEND,
    conflict: CONFLICT_MODES.LAST_WINS,
    mergeKey: "name",
    outputYaml: null,
    outputJson: null,
    generateSamples: false,
  };

  let i = 0;
  while (i < argv.length) {
    const arg = argv[i];
    if (arg === "--array-strategy" || arg === "-a") {
      args.arrayStrategy = argv[++i];
    } else if (arg === "--conflict" || arg === "-c") {
      args.conflict = argv[++i];
    } else if (arg === "--merge-key" || arg === "-k") {
      args.mergeKey = argv[++i];
    } else if (arg === "--output-yaml" || arg === "-y") {
      args.outputYaml = argv[++i];
    } else if (arg === "--output-json" || arg === "-j") {
      args.outputJson = argv[++i];
    } else if (arg === "--generate-samples" || arg === "-g") {
      args.generateSamples = true;
    } else if (arg === "--help" || arg === "-h") {
      console.log(`Usage: node merger.js [options] [file1.yaml file2.yaml ...]

Options:
  --array-strategy, -a   Array merge strategy: append, replace, merge-by-key (default: append)
  --conflict, -c         Conflict resolution: last-wins, first-wins, error (default: last-wins)
  --merge-key, -k        Key for merge-by-key array strategy (default: name)
  --output-yaml, -y      Write merged YAML to file
  --output-json, -j      Write merged JSON to file
  --generate-samples, -g Generate sample config files
  --help, -h             Show this help message`);
      process.exit(0);
    } else {
      args.files.push(arg);
    }
    i++;
  }
  return args;
}

// ---- Main ----
function main() {
  const args = parseArgs(process.argv.slice(2));
  let files = args.files;

  if (files.length === 0) {
    if (args.generateSamples) {
      console.log("No input files provided. Generating sample configs...\n");
      files = generateSampleConfigs();
      console.log();
    } else {
      console.log("No input files provided. Use --generate-samples or pass YAML files.");
      console.log("Run with --help for usage information.");
      process.exit(1);
    }
  }

  let merged;
  try {
    merged = mergeConfigs(files, args.arrayStrategy, args.conflict, args.mergeKey);
  } catch (e) {
    console.error(`Error: ${e.message}`);
    process.exit(1);
  }

  const yamlOutput = yaml.dump(merged, { noRefs: true });
  console.log("=== Merged YAML ===");
  console.log(yamlOutput);

  const jsonOutput = JSON.stringify(merged, null, 2);
  console.log("=== Merged JSON ===");
  console.log(jsonOutput);

  if (args.outputYaml) {
    fs.writeFileSync(args.outputYaml, yamlOutput, "utf-8");
    console.log(`\nYAML written to: ${args.outputYaml}`);
  }
  if (args.outputJson) {
    fs.writeFileSync(args.outputJson, jsonOutput, "utf-8");
    console.log(`JSON written to: ${args.outputJson}`);
  }
}

main();
package.json
{
  "name": "yaml-config-merger",
  "version": "1.0.0",
  "description": "Merges multiple YAML config files with deep merging, array handling, and conflict resolution",
  "main": "merger.js",
  "scripts": {
    "start": "node merger.js",
    "merge": "node merger.js",
    "samples": "node merger.js --generate-samples"
  },
  "dependencies": {
    "js-yaml": "4.1.0",
    "lodash": "4.17.21"
  }
}
README.md
# YAML Config Merger — JavaScript (Trial 1: js-yaml + lodash)

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

## Dependencies

- **js-yaml** 4.1.0 — YAML parsing and serialization
- **lodash** 4.17.21 — Deep clone and merge utilities

## Setup

```bash
npm install
```

## Usage

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

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

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

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

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