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

Dijkstra Shortest Path Finder (javascript, written by Codex)

envgap__codex__javascript-t1-43

Written by a coding agent; not on GitHubWritten 2026-03-03

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 #43 · 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: Dijkstra Shortest Path Finder

Write a program that finds the shortest path between nodes in a weighted graph using Dijkstra's algorithm, supporting multiple graph input formats, path visualization, and batch queries.

FUNCTIONAL REQUIREMENTS:
- Accept a graph definition file as a command-line argument (support adjacency list in JSON and edge list in CSV format)
- Accept source and destination nodes via --source and --destination flags
- Implement Dijkstra's algorithm with a priority queue (min-heap) for optimal performance
- Support both directed and undirected graphs via --directed flag (default: undirected)
- Report for the shortest path: total distance/weight, the complete node sequence, and the number of edges
- Support finding shortest paths from one source to all other nodes via --all flag (single-source shortest path)
- Support negative edge weight detection: warn if negative weights are found (Dijkstra doesn't handle them correctly) and suggest using Bellman-Ford instead
- Display the path in multiple formats via --format flag: text (default, showing node sequence with edge weights), json (structured output), and dot (Graphviz DOT format for visualization)
- Support batch queries: read multiple source-destination pairs from a file via --queries flag and compute shortest paths for all pairs
- Print graph statistics: total nodes, total edges, average degree, connected components count, and graph density
- Save results as JSON with --output flag (default: shortest_path.json)
- If no input is given, generate a sample weighted graph with 15 nodes and 25 edges, find shortest paths between several pairs of nodes, and demonstrate the all-pairs output
- Handle errors: disconnected nodes (no path exists), self-loops, duplicate edges, invalid node references, and malformed graph files

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": "dijkstra-shortest-path-finder",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "dijkstra-shortest-path-finder",
      "version": "1.0.0",
      "license": "MIT"
    }
  }
}
package.json
{
  "name": "dijkstra-shortest-path-finder",
  "version": "1.0.0",
  "description": "Shortest path finder using Dijkstra",
  "type": "module",
  "main": "src/index.js",
  "scripts": { "start": "node src/index.js" },
  "engines": { "node": ">=20.0.0" },
  "license": "MIT"
}
README.md
# Dijkstra Shortest Path Finder (JavaScript)

## Requirements
- Ubuntu 22.04
- Node.js 20+

## Install
```bash
npm install
```

## Run
```bash
node src/index.js graph.csv --source A --destination D
node src/index.js graph.json --source A --all --directed
node src/index.js graph.csv --queries pairs.txt --format json --output shortest_path.json
```

If no input is provided, a sample weighted graph is generated and demonstrated.
src/index.js
import fs from "node:fs";
import path from "node:path";
import process from "node:process";

class MinHeap {
  constructor() { this.a = []; }
  push(item) { this.a.push(item); this.up(this.a.length - 1); }
  up(i) {
    while (i > 0) {
      const p = Math.floor((i - 1) / 2);
      if (this.a[p].dist <= this.a[i].dist) break;
      [this.a[p], this.a[i]] = [this.a[i], this.a[p]];
      i = p;
    }
  }
  pop() {
    if (!this.a.length) return null;
    const top = this.a[0];
    const last = this.a.pop();
    if (this.a.length) { this.a[0] = last; this.down(0); }
    return top;
  }
  down(i) {
    const n = this.a.length;
    while (true) {
      let m = i;
      const l = 2 * i + 1;
      const r = 2 * i + 2;
      if (l < n && this.a[l].dist < this.a[m].dist) m = l;
      if (r < n && this.a[r].dist < this.a[m].dist) m = r;
      if (m === i) break;
      [this.a[m], this.a[i]] = [this.a[i], this.a[m]];
      i = m;
    }
  }
  get size() { return this.a.length; }
}

function parseArgs(argv) {
  const cfg = {
    file: null,
    source: null,
    destination: null,
    directed: false,
    all: false,
    format: "text",
    queries: null,
    output: "shortest_path.json"
  };
  const pos = [];
  for (let i = 0; i < argv.length; i += 1) {
    const a = argv[i];
    if (!a.startsWith("--")) { pos.push(a); continue; }
    if (a === "--source") cfg.source = argv[++i];
    else if (a === "--destination") cfg.destination = argv[++i];
    else if (a === "--directed") cfg.directed = true;
    else if (a === "--all") cfg.all = true;
    else if (a === "--format") cfg.format = argv[++i];
    else if (a === "--queries") cfg.queries = argv[++i];
    else if (a === "--output") cfg.output = argv[++i];
    else throw new Error(`Unknown option: ${a}`);
  }
  if (pos.length > 0) cfg.file = pos[0];
  return cfg;
}

function loadGraph(file, directed) {
  const graph = new Map();
  const addEdge = (u, v, w) => {
    if (!graph.has(u)) graph.set(u, []);
    graph.get(u).push({ to: v, w });
  };

  if (file.toLowerCase().endsWith(".json")) {
    const obj = JSON.parse(fs.readFileSync(file, "utf8"));
    for (const [u, nbrs] of Object.entries(obj)) {
      for (const [v, w] of Object.entries(nbrs)) {
        const wt = Number(w);
        addEdge(u, v, wt);
        if (!directed) addEdge(v, u, wt);
      }
    }
  } else {
    const lines = fs.readFileSync(file, "utf8").trim().split(/\r?\n/);
    for (let i = 1; i < lines.length; i += 1) {
      const [u, v, wStr] = lines[i].split(",").map((x) => x.trim());
      const w = Number(wStr);
      addEdge(u, v, w);
      if (!directed) addEdge(v, u, w);
    }
  }

  return graph;
}

function graphStats(graph, directed) {
  const nodes = [...graph.keys()];
  let edges = 0;
  let degreeSum = 0;
  let negatives = 0;
  for (const [u, nbrs] of graph.entries()) {
    edges += nbrs.length;
    degreeSum += nbrs.length;
    negatives += nbrs.filter((e) => e.w < 0).length;
    for (const e of nbrs) if (!graph.has(e.to)) graph.set(e.to, []);
  }
  if (!directed) edges /= 2;

  const visited = new Set();
  let comps = 0;
  for (const n of graph.keys()) {
    if (visited.has(n)) continue;
    comps += 1;
    const stack = [n];
    visited.add(n);
    while (stack.length) {
      const cur = stack.pop();
      for (const e of graph.get(cur) || []) {
        if (!visited.has(e.to)) {
          visited.add(e.to);
          stack.push(e.to);
        }
      }
    }
  }

  const N = graph.size;
  const density = N > 1 ? (directed ? edges / (N * (N - 1)) : (2 * edges) / (N * (N - 1))) : 0;

  return {
    totalNodes: N,
    totalEdges: edges,
    averageDegree: N ? degreeSum / N : 0,
    connectedComponents: comps,
    density,
    negativeEdges: negatives
  };
}

function dijkstra(graph, src) {
  const dist = new Map();
  const prev = new Map();
  for (const n of graph.keys()) dist.set(n, Infinity);
  dist.set(src, 0);

  const pq = new MinHeap();
  pq.push({ node: src, dist: 0 });

  while (pq.size) {
    const { node, dist: d } = pq.pop();
    if (d !== dist.get(node)) continue;
    for (const e of graph.get(node) || []) {
      const nd = d + e.w;
      if (nd < dist.get(e.to)) {
        dist.set(e.to, nd);
        prev.set(e.to, node);
        pq.push({ node: e.to, dist: nd });
      }
    }
  }
  return { dist, prev };
}

function buildPath(prev, src, dst) {
  if (src === dst) return [src];
  const path = [];
  let cur = dst;
  while (cur !== undefined && cur !== src) {
    path.push(cur);
    cur = prev.get(cur);
  }
  if (cur !== src) return null;
  path.push(src);
  return path.reverse();
}

function queryOne(graph, src, dst) {
  const { dist, prev } = dijkstra(graph, src);
  const d = dist.get(dst);
  if (!Number.isFinite(d)) return { source: src, destination: dst, exists: false, message: "No path" };
  const path = buildPath(prev, src, dst);
  return {
    source: src,
    destination: dst,
    exists: true,
    totalDistance: d,
    nodeSequence: path,
    edges: Math.max(0, path.length - 1)
  };
}

function formatOutput(result, format) {
  if (format === "json") return JSON.stringify(result, null, 2);
  if (format === "dot") {
    const lines = ["digraph G {"];
    if (result.nodeSequence) {
      for (let i = 0; i < result.nodeSequence.length - 1; i += 1) {
        lines.push(`  \"${result.nodeSequence[i]}\" -> \"${result.nodeSequence[i + 1]}\";`);
      }
    }
    lines.push("}");
    return lines.join("\n");
  }
  if (!result.exists) return `No path from ${result.source} to ${result.destination}`;
  return `Distance: ${result.totalDistance}\nPath: ${result.nodeSequence.join(" -> ")}\nEdges: ${result.edges}`;
}

function parseQueries(file) {
  return fs.readFileSync(file, "utf8").split(/\r?\n/).map((l) => l.trim()).filter(Boolean).map((l) => {
    const [s, d] = l.split(",").map((x) => x.trim());
    return { source: s, destination: d };
  });
}

function makeSample(file) {
  const nodes = Array.from({ length: 15 }, (_, i) => `N${i + 1}`);
  const edges = [];
  const add = (u, v, w) => edges.push(`${u},${v},${w}`);
  for (let i = 0; i < 14; i += 1) add(nodes[i], nodes[i + 1], 1 + (i % 5));
  add("N1", "N5", 4); add("N2", "N8", 3); add("N3", "N10", 6); add("N4", "N12", 2);
  add("N6", "N11", 5); add("N7", "N13", 2); add("N8", "N14", 7); add("N9", "N15", 1);
  add("N5", "N9", 3); add("N10", "N14", 2); add("N11", "N15", 4);
  const csv = `source,destination,weight\n${edges.join("\n")}\n`;
  fs.writeFileSync(file, csv, "utf8");
}

function main() {
  try {
    const cfg = parseArgs(process.argv.slice(2));
    if (!cfg.file) {
      cfg.file = path.resolve("sample_graph.csv");
      makeSample(cfg.file);
      cfg.source = "N1";
      cfg.destination = "N15";
    }

    const graph = loadGraph(cfg.file, cfg.directed);
    const stats = graphStats(graph, cfg.directed);
    if (stats.negativeEdges > 0) process.stderr.write("Warning: negative edges detected; Dijkstra may be invalid. Use Bellman-Ford.\n");

    const results = [];
    if (cfg.queries) {
      const pairs = parseQueries(cfg.queries);
      for (const q of pairs) results.push(queryOne(graph, q.source, q.destination));
    } else if (cfg.all && cfg.source) {
      const { dist, prev } = dijkstra(graph, cfg.source);
      for (const n of graph.keys()) {
        const d = dist.get(n);
        const p = Number.isFinite(d) ? buildPath(prev, cfg.source, n) : null;
        results.push({ source: cfg.source, destination: n, exists: Number.isFinite(d), totalDistance: d, nodeSequence: p, edges: p ? p.length - 1 : 0 });
      }
    } else {
      if (!cfg.source || !cfg.destination) throw new Error("--source and --destination are required unless --all or --queries is used");
      results.push(queryOne(graph, cfg.source, cfg.destination));
    }

    for (const r of results) {
      console.log(formatOutput(r, cfg.format));
      if (cfg.format !== "json") console.log("");
    }

    const report = { config: cfg, graphStats: stats, results };
    fs.writeFileSync(cfg.output, JSON.stringify(report, null, 2), "utf8");
  } catch (err) {
    console.error(`Error: ${err.message}`);
    process.exit(1);
  }
}

main();