YAML Config Merger (javascript, written by Codex)
envgap__codex__javascript-t1-4
Written by a coding agent; not on GitHubWritten 2026-03-02
01 / FAILURE SIGNATURE
As the study recorded it
None
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
codex/javascript-t1 #4 · read the task the agent was given
Codex 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 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
4 files, exactly as written, before any repair.
package-lock.json
{
"name": "yaml-config-merger",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "yaml-config-merger",
"version": "1.0.0"
}
}
}
package.json
{
"name": "yaml-config-merger",
"version": "1.0.0",
"description": "Deep YAML config merger with conflict detection and schema validation.",
"type": "module",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js",
"merge": "node src/index.js"
},
"engines": {
"node": ">=20.0.0"
},
"dependencies": {},
"devDependencies": {}
}
README.md
# YAML Config Merger (JavaScript)
Merges multiple YAML configuration files with deep merge, array strategies, conflict reporting, env interpolation, optional schema validation, and YAML output.
## Features
- Accepts two or more YAML files.
- Deep merge objects (later files override leaf values).
- `--array-strategy`: `replace` (default), `append`, `unique`.
- Conflict reporting with key paths and conflicting files.
- Preserves YAML comments where possible.
- Environment interpolation:
- `${VAR_NAME}`
- `${VAR_NAME:-default}`
- Optional schema validation with `--schema`.
- Prints merged YAML to console.
- Saves merged YAML via `--output` (default: `merged_config.yaml`).
- Generates three sample YAML files when no input is provided.
- Malformed YAML errors include file and line.
## Requirements
- Ubuntu 22.04
- Node.js 20+
## Dependencies (Pinned)
No external dependencies are used.
- Direct dependencies: none
- Transitive dependencies: none
## Usage
```bash
cd TMLR/code_generation/codex_generated/p_04/javascript
node src/index.js config1.yaml config2.yaml config3.yaml
```
Array strategy:
```bash
node src/index.js --array-strategy unique config1.yaml config2.yaml
```
Schema validation:
```bash
node src/index.js --schema schema.yaml --output final.yaml config1.yaml config2.yaml
```
No args (generate samples + merge):
```bash
node src/index.js
```
src/index.js
#!/usr/bin/env node
import fs from "node:fs/promises";
import path from "node:path";
class YamlParseError extends Error {
constructor(message, line) {
super(`${message} at line ${line}`);
this.name = "YamlParseError";
this.line = line;
}
}
function deepClone(value) {
if (Array.isArray(value)) return value.map((item) => deepClone(item));
if (value && typeof value === "object") {
const out = {};
for (const [k, v] of Object.entries(value)) out[k] = deepClone(v);
return out;
}
return value;
}
function deepEqual(a, b) {
if (a === b) return true;
if (typeof a !== typeof b) return false;
if (a === null || b === null) return a === b;
if (Array.isArray(a) !== Array.isArray(b)) return false;
if (Array.isArray(a)) {
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i += 1) if (!deepEqual(a[i], b[i])) return false;
return true;
}
if (typeof a === "object") {
const ak = Object.keys(a);
const bk = Object.keys(b);
if (ak.length !== bk.length) return false;
for (const key of ak) {
if (!Object.hasOwn(b, key) || !deepEqual(a[key], b[key])) return false;
}
return true;
}
return false;
}
function scalarFromText(text) {
if (text === "null" || text === "~") return null;
if (text === "true") return true;
if (text === "false") return false;
if (/^-?\d+(\.\d+)?$/.test(text)) return Number(text);
if (
(text.startsWith('"') && text.endsWith('"')) ||
(text.startsWith("'") && text.endsWith("'"))
) {
return text.slice(1, -1);
}
return text;
}
function yamlScalar(value) {
if (value === null) return "null";
if (typeof value === "boolean") return value ? "true" : "false";
if (typeof value === "number") return Number.isFinite(value) ? String(value) : "null";
if (typeof value === "string") {
if (value === "") return '""';
if (/[:#\-\[\]\{\},]|^\s|\s$|\n/.test(value)) {
return JSON.stringify(value);
}
return value;
}
return JSON.stringify(value);
}
function parseYaml(text) {
const lines = text.replace(/\r\n?/g, "\n").split("\n");
const comments = {};
function lineInfo(index) {
const raw = lines[index] ?? "";
const indent = raw.match(/^ */)[0].length;
const trimmed = raw.trim();
return { raw, indent, trimmed, lineNo: index + 1 };
}
function parseBlock(start, indent, pathPrefix) {
let index = start;
const pendingComments = [];
while (index < lines.length) {
const info = lineInfo(index);
if (info.trimmed === "") {
index += 1;
continue;
}
if (info.indent < indent) break;
if (info.indent > indent) {
throw new YamlParseError("Unexpected indentation", info.lineNo);
}
if (info.trimmed.startsWith("#")) {
pendingComments.push(info.trimmed.slice(1).trim());
index += 1;
continue;
}
break;
}
if (index >= lines.length) return { value: null, next: index };
const first = lineInfo(index);
if (first.indent !== indent) return { value: null, next: index };
const isSeq = first.trimmed.startsWith("- ");
if (isSeq) {
const arr = [];
while (index < lines.length) {
const info = lineInfo(index);
if (info.trimmed === "" || info.trimmed.startsWith("#")) {
index += 1;
continue;
}
if (info.indent < indent) break;
if (info.indent > indent) throw new YamlParseError("Unexpected indentation", info.lineNo);
if (!info.trimmed.startsWith("- ")) break;
const rest = info.trimmed.slice(2).trim();
if (rest === "") {
const nested = parseBlock(index + 1, indent + 2, `${pathPrefix}[${arr.length}]`);
arr.push(nested.value);
index = nested.next;
} else if (/^[^:#]+:\s*/.test(rest)) {
const split = rest.indexOf(":");
const key = rest.slice(0, split).trim();
const valueText = rest.slice(split + 1).trim();
const item = {};
if (valueText === "") {
const nested = parseBlock(index + 1, indent + 4, `${pathPrefix}[${arr.length}].${key}`);
item[key] = nested.value;
arr.push(item);
index = nested.next;
} else {
item[key] = scalarFromText(valueText);
arr.push(item);
index += 1;
}
} else {
arr.push(scalarFromText(rest));
index += 1;
}
}
return { value: arr, next: index };
}
const obj = {};
let localComments = pendingComments;
while (index < lines.length) {
const info = lineInfo(index);
if (info.trimmed === "") {
index += 1;
continue;
}
if (info.indent < indent) break;
if (info.indent > indent) throw new YamlParseError("Unexpected indentation", info.lineNo);
if (info.trimmed.startsWith("#")) {
localComments.push(info.trimmed.slice(1).trim());
index += 1;
continue;
}
const split = info.trimmed.indexOf(":");
if (split < 1) throw new YamlParseError("Invalid mapping entry", info.lineNo);
const key = info.trimmed.slice(0, split).trim();
const rest = info.trimmed.slice(split + 1).trim();
const fullPath = pathPrefix ? `${pathPrefix}.${key}` : key;
if (localComments.length > 0) {
comments[fullPath] = [...localComments];
localComments = [];
}
if (rest === "") {
const nested = parseBlock(index + 1, indent + 2, fullPath);
obj[key] = nested.value;
index = nested.next;
} else {
const inline = rest.split(/\s+#/, 1)[0].trim();
obj[key] = scalarFromText(inline);
index += 1;
}
}
return { value: obj, next: index };
}
const parsed = parseBlock(0, 0, "");
return { data: parsed.value ?? {}, comments };
}
function interpolateEnv(value) {
if (typeof value === "string") {
return value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)(:-([^}]*))?\}/g, (_, key, _whole, def) => {
if (process.env[key] !== undefined) return process.env[key];
return def ?? "";
});
}
if (Array.isArray(value)) return value.map(interpolateEnv);
if (value && typeof value === "object") {
const out = {};
for (const [k, v] of Object.entries(value)) out[k] = interpolateEnv(v);
return out;
}
return value;
}
function collectLeafPaths(value, pathPrefix, out) {
if (Array.isArray(value) || value === null || typeof value !== "object") {
out[pathPrefix] = value;
return;
}
for (const [k, v] of Object.entries(value)) {
const path = pathPrefix ? `${pathPrefix}.${k}` : k;
collectLeafPaths(v, path, out);
}
}
function mergeValues(base, incoming, pathText, strategy, conflicts, sourceMap, currentFile) {
const baseObj = base && typeof base === "object" && !Array.isArray(base);
const incObj = incoming && typeof incoming === "object" && !Array.isArray(incoming);
if (baseObj && incObj) {
const merged = { ...base };
for (const [key, val] of Object.entries(incoming)) {
const childPath = pathText ? `${pathText}.${key}` : key;
if (Object.hasOwn(merged, key)) {
merged[key] = mergeValues(
merged[key],
val,
childPath,
strategy,
conflicts,
sourceMap,
currentFile,
);
} else {
merged[key] = deepClone(val);
sourceMap[childPath] = currentFile;
}
}
return merged;
}
if (Array.isArray(base) && Array.isArray(incoming)) {
let merged;
if (strategy === "append") {
merged = [...base.map(deepClone), ...incoming.map(deepClone)];
} else if (strategy === "unique") {
merged = [...base.map(deepClone)];
for (const item of incoming) {
if (!merged.some((existing) => deepEqual(existing, item))) merged.push(deepClone(item));
}
} else {
merged = incoming.map(deepClone);
}
if (!deepEqual(base, incoming)) {
conflicts.push({
path: pathText,
previousFile: sourceMap[pathText] ?? "unknown",
currentFile,
previousValue: base,
currentValue: incoming,
});
}
sourceMap[pathText] = currentFile;
return merged;
}
if (!deepEqual(base, incoming)) {
conflicts.push({
path: pathText,
previousFile: sourceMap[pathText] ?? "unknown",
currentFile,
previousValue: base,
currentValue: incoming,
});
}
sourceMap[pathText] = currentFile;
return deepClone(incoming);
}
function validateSchema(data, schema, pathText, errors) {
if (!schema || typeof schema !== "object" || Array.isArray(schema)) return;
const type = schema.type;
const actualType =
data === null
? "null"
: Array.isArray(data)
? "array"
: typeof data === "number"
? "number"
: typeof data === "boolean"
? "boolean"
: typeof data === "string"
? "string"
: typeof data === "object"
? "object"
: "unknown";
if (typeof type === "string" && actualType !== type) {
errors.push(`${pathText || "$"}: expected type ${type}, got ${actualType}`);
return;
}
if (Array.isArray(schema.enum) && !schema.enum.some((v) => deepEqual(v, data))) {
errors.push(`${pathText || "$"}: value not in enum`);
}
if (actualType === "object") {
if (Array.isArray(schema.required)) {
for (const req of schema.required) {
if (!Object.hasOwn(data, req)) errors.push(`${pathText || "$"}: missing required key ${req}`);
}
}
if (schema.properties && typeof schema.properties === "object") {
for (const [k, sub] of Object.entries(schema.properties)) {
if (Object.hasOwn(data, k)) validateSchema(data[k], sub, pathText ? `${pathText}.${k}` : k, errors);
}
}
} else if (actualType === "array") {
if (schema.items) {
data.forEach((item, idx) => validateSchema(item, schema.items, `${pathText}[${idx}]`, errors));
}
}
}
function toYaml(value, commentsMap, pathText = "", indent = 0) {
const pad = " ".repeat(indent);
const lines = [];
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i += 1) {
const item = value[i];
if (item && typeof item === "object" && !Array.isArray(item)) {
lines.push(`${pad}-`);
lines.push(toYaml(item, commentsMap, `${pathText}[${i}]`, indent + 2));
} else if (Array.isArray(item)) {
lines.push(`${pad}-`);
lines.push(toYaml(item, commentsMap, `${pathText}[${i}]`, indent + 2));
} else {
lines.push(`${pad}- ${yamlScalar(item)}`);
}
}
return lines.filter(Boolean).join("\n");
}
if (value && typeof value === "object") {
for (const [key, val] of Object.entries(value)) {
const childPath = pathText ? `${pathText}.${key}` : key;
if (commentsMap[childPath]) {
for (const comment of commentsMap[childPath]) lines.push(`${pad}# ${comment}`);
}
if (val && typeof val === "object") {
lines.push(`${pad}${key}:`);
lines.push(toYaml(val, commentsMap, childPath, indent + 2));
} else {
lines.push(`${pad}${key}: ${yamlScalar(val)}`);
}
}
return lines.filter(Boolean).join("\n");
}
return `${pad}${yamlScalar(value)}`;
}
async function loadYamlFile(filePath) {
let text;
try {
text = await fs.readFile(filePath, "utf8");
} catch (error) {
throw new Error(`Failed to read ${filePath}: ${error.message}`);
}
try {
return parseYaml(text);
} catch (error) {
if (error instanceof YamlParseError) {
throw new Error(`Malformed YAML in ${filePath}: ${error.message}`);
}
throw error;
}
}
function sampleConfigFiles() {
return {
"config_1.yaml": `# Base app config
app:
name: merger-demo
env: \${APP_ENV:-dev}
database:
host: localhost
port: 5432
tags:
- core
- primary
features:
enabled:
- auth
- api
`,
"config_2.yaml": `# Override database and features
database:
port: 5433
user: \${DB_USER:-admin}
tags:
- analytics
features:
enabled:
- api
- billing
`,
"config_3.yaml": `# Production tuning
app:
env: prod
database:
host: db.internal
retries: 5
features:
enabled:
- auth
- billing
`,
};
}
async function run() {
const args = process.argv.slice(2);
const inputFiles = [];
let arrayStrategy = "replace";
let schemaPath = null;
let outputPath = "merged_config.yaml";
for (let i = 0; i < args.length; i += 1) {
const arg = args[i];
if (arg === "--array-strategy") {
i += 1;
arrayStrategy = args[i] ?? "";
} else if (arg === "--schema") {
i += 1;
schemaPath = args[i] ?? null;
} else if (arg === "--output") {
i += 1;
outputPath = args[i] ?? outputPath;
} else {
inputFiles.push(arg);
}
}
if (!["replace", "append", "unique"].includes(arrayStrategy)) {
console.error("Invalid --array-strategy. Use replace, append, or unique.");
process.exitCode = 1;
return;
}
let files = [...inputFiles];
if (files.length === 0) {
const samples = sampleConfigFiles();
for (const [name, content] of Object.entries(samples)) {
const abs = path.resolve(process.cwd(), name);
await fs.writeFile(abs, content, "utf8");
files.push(abs);
}
console.log(`No input files provided. Generated sample files: ${files.join(", ")}`);
}
if (files.length < 2) {
console.error("Provide at least two YAML files.");
process.exitCode = 1;
return;
}
const parsedFiles = [];
for (const file of files) {
const abs = path.resolve(process.cwd(), file);
const parsed = await loadYamlFile(abs);
parsedFiles.push({ file: abs, ...parsed });
}
let merged = deepClone(parsedFiles[0].data);
const sourceMap = {};
collectLeafPaths(merged, "", sourceMap);
for (const key of Object.keys(sourceMap)) sourceMap[key] = parsedFiles[0].file;
const mergedComments = { ...parsedFiles[0].comments };
const conflicts = [];
for (let i = 1; i < parsedFiles.length; i += 1) {
const next = parsedFiles[i];
merged = mergeValues(merged, next.data, "", arrayStrategy, conflicts, sourceMap, next.file);
for (const [key, value] of Object.entries(next.comments)) {
if (!Object.hasOwn(mergedComments, key)) mergedComments[key] = value;
}
}
merged = interpolateEnv(merged);
if (schemaPath) {
const schemaAbs = path.resolve(process.cwd(), schemaPath);
const parsedSchema = await loadYamlFile(schemaAbs);
const schemaErrors = [];
validateSchema(merged, parsedSchema.data, "", schemaErrors);
if (schemaErrors.length > 0) {
console.error("Schema validation failed:");
schemaErrors.forEach((error) => console.error(`- ${error}`));
process.exitCode = 1;
return;
}
}
if (conflicts.length > 0) {
console.log("Merge conflicts detected:");
for (const conflict of conflicts) {
const displayPath = conflict.path || "<root>";
console.log(
`- ${displayPath}: ${conflict.previousFile} disagrees with ${conflict.currentFile}`,
);
}
} else {
console.log("No merge conflicts detected.");
}
const yamlOutput = toYaml(merged, mergedComments);
console.log("\nMerged YAML:\n");
console.log(yamlOutput);
const outputAbs = path.resolve(process.cwd(), outputPath);
await fs.writeFile(outputAbs, `${yamlOutput}\n`, "utf8");
console.log(`\nSaved merged config: ${outputAbs}`);
}
run().catch((error) => {
console.error(error.message);
process.exitCode = 1;
});