Inspiration

In 2016, a developer deleted an 11-line package called left-pad and builds broke at Facebook, Netflix, and Spotify. That story always stuck with me. Ten years on, the problem is worse, not better — thousands of npm packages are abandoned or gone entirely, and production code still depends on them. I wanted to see if I could bring a dead package back without ever trusting its source code — and honestly, without trusting myself either. The tool had to prove its own claims.

What it does

You run necromancer resurrect left-pad and it goes through six phases:

  1. Exhume — grabs the tarball off npm and verifies registry integrity.
  2. Sandbox — locks the package in Docker. No network, lifecycle scripts stripped. If the package is malicious, it can't touch my machine.
  3. Probe — actually runs the thing and records what it does, with branch/line/function coverage so I know how much of it I exercised.
  4. Distill — turns those recordings into SOUL.md (a behavioral spec) and a vitest suite — one test per observed behavior.
  5. Resurrect — the model gets the soul and the tests, never the original source, and a judge loop feeds failures back for up to six rounds.
  6. Report — graveyard.html with the verdict, plus a provenance receipt (SHA-256 over every artifact).

The part I care most about: it doesn't overclaim. It never says "identical" — it says N of M observed behaviors passed, here's the coverage. Some tests (I call them Last Rites) are held out from the model entirely, so passing those actually means something — it's generalization, not memorization. And necromancer verify re-checks everything offline and prints what it could NOT re-verify.

How I built it

TypeScript on Node 20. Commander for the CLI, vitest for the generated suites, c8/V8 for coverage, Docker for the sandbox, and the OpenAI API / Codex CLI as the rebuild engine. The judge loop is deterministic — structured test failures go back to the model as feedback each round.

Challenges I ran into

  • Running untrusted npm code safely. Read-only mounts, no network, stripped scripts — and then vitest wouldn't run in the read-only container because Vite wants to write its temp bundle into node_modules. Fixed it by bind-mounting a writable directory over just that path.
  • Coverage across the container boundary. V8 records file URLs with container paths, so c8 couldn't match them to anything on the host. I rewrite the URLs before reporting.
  • Honesty is a feature you have to build on purpose. FAILED RECONSTRUCTION is a real state the tool can end in, the held-out tests exist, and verify lists what it didn't check — none of that happens by accident.

Accomplishments that I'm proud of

  • Seven real packages resurrected end to end — left-pad, is-odd, deep-extend, ms, object-assign, dedent, arr-diff — with the actual numbers (fidelity, coverage, dependency deltas) in the README, including the one run that didn't go perfectly.
  • 72 tests, clean typecheck, and a no-credentials replay mode so anyone can poke at the report in seconds without an API key.

What I learned

Characterization tests are a shockingly good contract for LLM code generation — when the spec is behavioral instead of prose, the judge loop usually converges in a few rounds. Also: "N of M behaviors passed" turns out to be way more convincing than "it works". Bounded claims build more trust than big ones.

What's next for Necromancer

Bigger packages (multi-module, native deps), a public cemetery of verified resurrections, and CI integration that flags dead dependencies and proposes a verified rebuild automatically.

Built With

Share this project:

Updates