← All tasks
javascriptcodex/javascript-t1 #2Not a task: already works

JSON Schema Validator (javascript, written by Codex)

envgap__codex__javascript-t1-2

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 #2 · 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: JSON Schema Validator

Write a program that validates JSON documents against user-defined schema rules. It should support type checking, required fields, nested object validation, array constraints, and pattern matching. It should collect all violations — not stop at the first — and produce a detailed validation report.

FUNCTIONAL REQUIREMENTS:
- Accept two command-line arguments: a JSON data file and a JSON schema file
- Support validation rules: type checking (string, number, boolean, array, object, null), required fields, min/max for numbers, minLength/maxLength for strings, enum values, regex pattern matching
- Validate nested objects and arrays recursively to any depth
- Validate array items (all items must conform to a specified type or sub-schema)
- Collect ALL validation errors across the entire document, reporting the full JSON path for each violation (e.g., $.users[2].email)
- Print the validation report to the console with one line per violation showing path, expected rule, and actual value
- Save the validation report as validation_report.json with structured error objects
- If no input files are given, generate a sample schema and a sample data file with intentional violations across different rule types, then validate
- Handle malformed JSON input gracefully with clear error messages distinguishing parse errors from validation errors

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

4 files, exactly as written, before any repair.

package-lock.json
{
  "name": "json-schema-validator",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "json-schema-validator",
      "version": "1.0.0"
    }
  }
}
package.json
{
  "name": "json-schema-validator",
  "version": "1.0.0",
  "description": "CLI JSON schema validator with recursive rule checking and full violation reporting.",
  "type": "module",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "validate": "node src/index.js"
  },
  "engines": {
    "node": ">=20.0.0"
  },
  "dependencies": {},
  "devDependencies": {}
}
README.md
# JSON Schema Validator (JavaScript)

CLI tool for validating JSON data against user-defined schema rules with full error collection.

## Features

- Accepts `data.json` and `schema.json` as CLI arguments.
- Supports rules:
  - `type`: `string`, `number`, `boolean`, `array`, `object`, `null`
  - `required` for objects
  - `min`/`max` (and `minimum`/`maximum`) for numbers
  - `minLength`/`maxLength` for strings
  - `enum`
  - `pattern` (regex)
  - `minItems`/`maxItems` for arrays
- Recursive validation for nested objects and arrays.
- Validates every array item with `items` schema.
- Collects all violations with full JSON paths (e.g., `$.users[2].email`).
- Prints one line per violation in console.
- Saves structured `validation_report.json`.
- If no input files are provided, generates sample schema/data with intentional violations and validates them.
- Distinguishes JSON parse errors from validation errors.

## Requirements

- Ubuntu 22.04
- Node.js 20+ (LTS)

## Dependencies (Pinned)

No external dependencies are used.

- Direct dependencies: none
- Transitive dependencies: none

`package-lock.json` is included and pinned (`lockfileVersion: 3`).

## Setup

```bash
cd TMLR/code_generation/codex_generated/p_02/javascript
npm install
```

## Run

Validate custom files:

```bash
npm run validate -- ./data.json ./schema.json
```

Generate sample files and validate:

```bash
npm start
```

## Output

- Console: one line per violation with path, expected rule, and actual value.
- File: `validation_report.json` with structured error objects.
- If no inputs: `sample_data.json` and `sample_schema.json` are generated.
src/index.js
#!/usr/bin/env node
import fs from "node:fs/promises";
import path from "node:path";

function jsonTypeOf(value) {
  if (value === null) return "null";
  if (Array.isArray(value)) return "array";
  return typeof value;
}

function escapeRegExpError(pattern, message) {
  return `invalid regex pattern "${pattern}": ${message}`;
}

function valuePreview(value) {
  if (value === undefined) return "undefined";
  if (typeof value === "string") return JSON.stringify(value);
  if (value === null || typeof value === "number" || typeof value === "boolean") {
    return String(value);
  }
  if (Array.isArray(value)) return `<array length=${value.length}>`;
  return "<object>";
}

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 aKeys = Object.keys(a);
    const bKeys = Object.keys(b);
    if (aKeys.length !== bKeys.length) return false;
    for (const key of aKeys) {
      if (!Object.hasOwn(b, key)) return false;
      if (!deepEqual(a[key], b[key])) return false;
    }
    return true;
  }
  return false;
}

function matchesType(value, expectedType) {
  const actualType = jsonTypeOf(value);
  if (expectedType === "number") return actualType === "number";
  if (expectedType === "string") return actualType === "string";
  if (expectedType === "boolean") return actualType === "boolean";
  if (expectedType === "array") return actualType === "array";
  if (expectedType === "object") return actualType === "object";
  if (expectedType === "null") return actualType === "null";
  return true;
}

function addError(errors, pathText, rule, expected, actualValue) {
  errors.push({
    path: pathText,
    rule,
    expected,
    actual: valuePreview(actualValue),
  });
}

function validateAgainstSchema(data, schema, pathText, errors) {
  if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
    return;
  }

  if (Object.hasOwn(schema, "type")) {
    const declaredType = schema.type;
    const allowedTypes = Array.isArray(declaredType) ? declaredType : [declaredType];
    const matches = allowedTypes.some((t) => matchesType(data, t));

    if (!matches) {
      addError(
        errors,
        pathText,
        "type",
        Array.isArray(declaredType) ? declaredType.join(" | ") : String(declaredType),
        data,
      );
      return;
    }
  }

  if (Array.isArray(schema.enum)) {
    const found = schema.enum.some((allowed) => deepEqual(allowed, data));
    if (!found) {
      addError(errors, pathText, "enum", JSON.stringify(schema.enum), data);
    }
  }

  const dataType = jsonTypeOf(data);

  if (dataType === "number") {
    if (typeof schema.min === "number" && data < schema.min) {
      addError(errors, pathText, "min", `>= ${schema.min}`, data);
    }
    if (typeof schema.max === "number" && data > schema.max) {
      addError(errors, pathText, "max", `<= ${schema.max}`, data);
    }
    if (typeof schema.minimum === "number" && data < schema.minimum) {
      addError(errors, pathText, "minimum", `>= ${schema.minimum}`, data);
    }
    if (typeof schema.maximum === "number" && data > schema.maximum) {
      addError(errors, pathText, "maximum", `<= ${schema.maximum}`, data);
    }
  }

  if (dataType === "string") {
    if (typeof schema.minLength === "number" && data.length < schema.minLength) {
      addError(errors, pathText, "minLength", `length >= ${schema.minLength}`, data);
    }
    if (typeof schema.maxLength === "number" && data.length > schema.maxLength) {
      addError(errors, pathText, "maxLength", `length <= ${schema.maxLength}`, data);
    }
    if (typeof schema.pattern === "string") {
      try {
        const regex = new RegExp(schema.pattern);
        if (!regex.test(data)) {
          addError(errors, pathText, "pattern", `match /${schema.pattern}/`, data);
        }
      } catch (error) {
        addError(
          errors,
          pathText,
          "pattern",
          escapeRegExpError(schema.pattern, error.message),
          data,
        );
      }
    }
  }

  if (dataType === "object") {
    if (Array.isArray(schema.required)) {
      for (const key of schema.required) {
        if (!Object.hasOwn(data, key)) {
          addError(
            errors,
            `${pathText}.${key}`,
            "required",
            "field must exist",
            undefined,
          );
        }
      }
    }

    if (schema.properties && typeof schema.properties === "object" && !Array.isArray(schema.properties)) {
      for (const [key, subschema] of Object.entries(schema.properties)) {
        if (Object.hasOwn(data, key)) {
          validateAgainstSchema(data[key], subschema, `${pathText}.${key}`, errors);
        }
      }
    }
  }

  if (dataType === "array") {
    if (typeof schema.minItems === "number" && data.length < schema.minItems) {
      addError(errors, pathText, "minItems", `items >= ${schema.minItems}`, data);
    }
    if (typeof schema.maxItems === "number" && data.length > schema.maxItems) {
      addError(errors, pathText, "maxItems", `items <= ${schema.maxItems}`, data);
    }

    if (schema.items && typeof schema.items === "object" && !Array.isArray(schema.items)) {
      data.forEach((item, index) => {
        validateAgainstSchema(item, schema.items, `${pathText}[${index}]`, errors);
      });
    }
  }
}

function generateSampleSchema() {
  return {
    type: "object",
    required: ["users", "status", "metadata", "note"],
    properties: {
      users: {
        type: "array",
        minItems: 1,
        items: {
          type: "object",
          required: ["name", "age", "email", "tags", "active"],
          properties: {
            name: { type: "string", minLength: 3, maxLength: 20 },
            age: { type: "number", min: 0, max: 120 },
            email: {
              type: "string",
              pattern: "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$",
            },
            tags: {
              type: "array",
              minItems: 1,
              items: { type: "string", minLength: 2, maxLength: 12 },
            },
            active: { type: "boolean" },
          },
        },
      },
      status: { type: "string", enum: ["new", "active", "disabled"] },
      metadata: {
        type: "object",
        required: ["version", "source"],
        properties: {
          version: { type: "string", pattern: "^\\d+\\.\\d+\\.\\d+$" },
          source: {
            type: "object",
            required: ["systemId"],
            properties: {
              systemId: { type: "string", minLength: 5 },
            },
          },
        },
      },
      note: { type: "null" },
    },
  };
}

function generateSampleData() {
  return {
    users: [
      {
        name: "Alice",
        age: 29,
        email: "alice@example.com",
        tags: ["owner", "ops"],
        active: true,
      },
      {
        name: "Al",
        age: -5,
        email: "invalid-email",
        tags: ["ok", 5, ""],
        active: "true",
      },
      {
        name: 123,
        age: 200,
        tags: [],
        active: false,
      },
    ],
    status: "archived",
    metadata: {
      version: "1.0",
      source: { systemId: 42 },
    },
    note: "should be null",
  };
}

async function loadJsonFile(filePath, label) {
  let text;
  try {
    text = await fs.readFile(filePath, "utf8");
  } catch (error) {
    throw new Error(`${label} file read error (${filePath}): ${error.message}`);
  }

  try {
    return JSON.parse(text);
  } catch (error) {
    throw new Error(`${label} JSON parse error (${filePath}): ${error.message}`);
  }
}

function printViolations(errors) {
  if (errors.length === 0) {
    console.log("Validation succeeded: no violations found.");
    return;
  }

  console.log(`Validation failed: ${errors.length} violation(s) found.`);
  for (const error of errors) {
    console.log(
      `${error.path} | expected: ${error.rule} -> ${error.expected} | actual: ${error.actual}`,
    );
  }
}

async function run() {
  const args = process.argv.slice(2);

  let dataPath;
  let schemaPath;

  if (args.length === 0) {
    dataPath = path.resolve(process.cwd(), "sample_data.json");
    schemaPath = path.resolve(process.cwd(), "sample_schema.json");

    await fs.writeFile(dataPath, `${JSON.stringify(generateSampleData(), null, 2)}\n`, "utf8");
    await fs.writeFile(schemaPath, `${JSON.stringify(generateSampleSchema(), null, 2)}\n`, "utf8");

    console.log(`No input files provided. Generated sample data: ${dataPath}`);
    console.log(`No input files provided. Generated sample schema: ${schemaPath}`);
  } else if (args.length === 2) {
    dataPath = path.resolve(process.cwd(), args[0]);
    schemaPath = path.resolve(process.cwd(), args[1]);
  } else {
    console.error("Usage: node src/index.js <data.json> <schema.json>");
    console.error("   or: node src/index.js");
    process.exitCode = 1;
    return;
  }

  let data;
  let schema;
  try {
    data = await loadJsonFile(dataPath, "Data");
    schema = await loadJsonFile(schemaPath, "Schema");
  } catch (error) {
    console.error(error.message);
    process.exitCode = 1;
    return;
  }

  const errors = [];
  validateAgainstSchema(data, schema, "$", errors);

  printViolations(errors);

  const report = {
    timestamp: new Date().toISOString(),
    dataFile: dataPath,
    schemaFile: schemaPath,
    valid: errors.length === 0,
    errorCount: errors.length,
    errors,
  };

  const reportPath = path.resolve(process.cwd(), "validation_report.json");
  await fs.writeFile(reportPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
  console.log(`Saved structured report: ${reportPath}`);
}

run().catch((error) => {
  console.error(`Unexpected failure: ${error.message}`);
  process.exitCode = 1;
});