Skip to main content
Glama
README.md
# parallelmap

A test suite or a job pipeline takes as long as it takes —
unless some of it can run side by side. parallelmap takes
the workload graph — every job's name, its duration, and
the names of the jobs that must finish before it starts —
and maps out what can safely parallelize:

- **Levels** — the jobs grouped by depth. Level 0 holds
  the jobs with no dependencies; every other job sits at
  one level above its deepest dependency. Everything in a
  level can run together, once the levels below it are
  done.
- **Sequential vs parallel time** — the sum of all
  durations against the makespan with unbounded workers
  (the largest earliest finish), and the speedup between
  them. All reported seconds are rounded to 3 decimals,
  and the speedup is computed from those rounded values.
- **Critical path** — the chain that sets the parallel
  time, in execution order. Ties — for the finishing job
  and for each step back along the chain — go to the
  smallest job name, so the path is fully deterministic.
- **Worker schedule** — pass a worker count and a greedy
  list schedule assigns the jobs in (level, name) priority
  order, each to the worker that frees up first (lowest
  index on a tie), starting no earlier than its
  dependencies' finishes under that schedule. The schedule
  reports each job's worker, start, and finish, sorted by
  (start, worker, job), plus the resulting makespan.
  Without a worker count there is no schedule and the
  makespan is the unbounded parallel time.
- **Cycle detection** — when the jobs cannot be scheduled
  at all, parallelmap does not error out or guess. It
  reports `ok: false` with the exact cycle: a depth-first
  search over names and dependencies in ascending order
  returns the first cycle it finds as the list of jobs
  forming it (a job that depends on itself reports as a
  one-name cycle), with the time fields null.

The graph must be closed: a `depends_on` entry naming a
job that was not supplied is a validation error, as are a
missing or duplicate job name, a missing, negative, or
non-finite duration, and a worker count that is not an
integer of at least 1. Errors name the offending path
(for example `jobs[1].depends_on[0] references unknown
job "build"`) and no partial map is produced. The
computation is pure and local: no network, no
configuration, no files read beyond the input you pass in.

## CLI

```bash
npx @reneturcios/parallelmap map --input args.json
npx @reneturcios/parallelmap --version
```

`args.json` contains the full arguments object:

```json
{
  "jobs": [
    { "name": "compile", "duration_seconds": 30 },
    { "name": "unit", "duration_seconds": 45, "depends_on": ["compile"] },
    { "name": "lint", "duration_seconds": 10, "depends_on": ["compile"] },
    { "name": "package", "duration_seconds": 5, "depends_on": ["unit", "lint"] }
  ],
  "workers": 2
}
```

`depends_on` defaults to `[]` per job and `workers` is
optional overall. The map is printed as JSON:

```json
{
  "ok": true,
  "levels": [["compile"], ["lint", "unit"], ["package"]],
  "sequential_seconds": 90,
  "parallel_seconds": 80,
  "speedup": 1.125,
  "critical_path": ["compile", "unit", "package"],
  "cycle": [],
  "schedule": [
    { "job": "compile", "worker": 0, "start": 0, "finish": 30 },
    { "job": "unit", "worker": 0, "start": 30, "finish": 75 },
    { "job": "lint", "worker": 1, "start": 30, "finish": 40 },
    { "job": "package", "worker": 1, "start": 75, "finish": 80 }
  ],
  "makespan_seconds": 80
}
```

Exit codes: `0` on success, `1` on a runtime error
(including an unreadable or invalid input file, or
invalid input), `2` on a usage error.

## MCP server

```bash
npx -y @reneturcios/parallelmap mcp
```

MCP client config:

```json
{
  "mcpServers": {
    "parallelmap": {
      "command": "npx",
      "args": ["-y", "@reneturcios/parallelmap", "mcp"]
    }
  }
}
```

### Tools

- `map_parallelization` — Map which jobs or tests can
  safely run in parallel.
  - `jobs` (array, required, at least one) — jobs as
    `{name, duration_seconds, depends_on?}` objects;
    `name` is unique across the set and each `depends_on`
    entry names another supplied job
  - `workers` (integer, optional, at least 1) — when
    given, a greedy schedule onto that many workers is
    included

  Returns `{ok, levels, sequential_seconds,
  parallel_seconds, speedup, critical_path, cycle,
  schedule, makespan_seconds}` — or, on a cycle, `{ok:
  false, cycle, levels: [], sequential_seconds,
  parallel_seconds: null, speedup: null, critical_path:
  [], schedule: [], makespan_seconds: null}`.

## Docker

```bash
docker run -i ghcr.io/cloudcorprecords/parallelmap
docker run --rm -v "$PWD:/w" ghcr.io/cloudcorprecords/parallelmap map --input /w/args.json
```

## Development

```bash
npm install
npm run build   # tsc -> dist/
npm test        # tsx --test test/*.test.ts (pure local, no network)
npm run dev     # run the CLI from source
```

## License

MIT — Copyright (c) 2026 Rene Turcios