-
-
Nemotron draws the map. Code moves it. A checker proves nothing else changed.
-
What ac does: immich's real files, sliced by type before, by feature after 63 certified commits.
-
A generic agent (opencode on Nemotron 3 Super) on the same task: its tests pass, its certificate fails.
-
The same agent on Nemotron 3 Ultra: 374 files moved, no import rewritten, $2.55 billed when its cap stopped it.
-
The plan: Nemotron 3 Ultra's placements for immich, each with its rationale; $0.1998 for the whole plan.
-
A step's certificate: every check passes, and its digest is the one in the commit's trailer.
-
The ladder: 63 commits on immich, each certified; every rung passes with tsc, the 2,337 tests and the OpenAPI spec.
-
A seeded wrong move: the certificate fails it, the commit is rolled back, the file pinned, the step passes without it.
-
Before and after: 367 of 563 files moved into 38 feature modules; the largest import cycle grows from 12 of 14 folders to 41 of 70.
-
Findings, recorded and never fixed: a base class injects 56 members into 51 classes.
-
Boundary rules: a new forbidden import fails the check; 161 of today's violations are frozen.
-
We attacked the checker: 831 mutants, all 831 rejected by the static checks before a test runs.
-
Benchmarks with the losses: the planner vs the best deterministic method on 5 entries, MoJoFM and TurboMQ; boxed: rejected plans.
-
Preliminary: Nemotron 3.5 Super VL with room to think vs Ultra on MoJoFM, 3 runs per entry; on immich (2024 key), no measurable difference.
-
The test build, no key and no cost: npx ac demo --replay rebuilds every commit, and every tree equals the recording.
The problem
Many TypeScript backends are sliced by technical type, so every feature is spread over controllers/, services/,
repositories/ and dtos/. Moving to feature modules is mechanical, but it touches hundreds of files at once, so it
gets done by hand or it stalls halfway. Unleash started moving to feature folders in October 2023 (PR #4994); of the
controller that PR left behind, a reviewer wrote "we can move it any time", and three years later it is still in
src/lib/routes/, with src/lib/features/ beside src/lib/services/. n8n's packages/cli has modules/ beside
controllers/ and services/. Even a reorganization maintainers want is hand work: in March 2024, immich's
maintainers reorganized their server in 17 pull requests over two days.
Moving the files isn't the hard part; trusting the diff is, and a green test run isn't enough. We gave this job to a generic coding agent on Nemotron 3 Super: all 2,337 of immich's test cases still passed, yet 66 test files never loaded and tsc reported 1,680 errors.
ac is for maintainers who want feature modules without doing the move by hand, and for anyone reviewing a large
move made by an agent or a person: ac certify checks a diff it didn't make.
What it does
ac restructures a TypeScript backend sliced by technical type into feature modules (modules/<feature>/,
platform/<concern>/, shared/), one commit per step, without changing behavior, and a separate checker certifies
every commit.
ac run is the agent, and it runs unattended: it plans with Nemotron, writes each step's commit with a compiler-API
executor, runs the target's own tests (a baseline first, then tsc, the unit suite and immich's OpenAPI spec on every
rung and on the whole PR) and certifies every commit. When a certificate fails, it rolls the commit back, pins the file
the certificate names and runs the step again without it; it does that at most twice, then pins the whole step and
logs a finding. --resume continues after a crash. Nemotron makes the calls a deterministic tool can't; it writes no
code, so every changed byte is written by deterministic code and checked by the certificate.
- Plan. Deterministic code reads the repository (imports, DI wiring, tables, routes) and draws a draft map.
Nemotron 3 Ultra edits the draft and then reviews the result; a validator ranks the edits and drops any review
change that adds a warning or joins a group without evidence (an import edge, a table, a route). The plan is a
plan.yamlyou can read and edit. - Move. An executor on the TypeScript compiler API moves one group of files per commit and rewrites only the import specifiers that point at them. Every other byte stays identical.
- Certify.
ac certify, a separate program that never imports the executor, checks every commit: the move set is complete; only specifier bytes changed; every import resolves to the same file under TypeScript, the test runner and the runtime; no config or path string still names an old path; the test inventory and the emit layout are the same. On each rung of the ladder and on the whole PR (not on each move step), it also runs the target's own oracles (tsc, the unit suite, immich's OpenAPI spec), which must agree with the base commit. It certifies any diff: ours, another agent's, a person's. Each certificate also lists what it can't see: consumers outside the repository, files read at runtime, environment-dependent resolution such asNODE_PATH, stack-trace text, and CI triggers or CODEOWNERS keyed by path. - Enforce. The last rung adds dependency-cruiser boundary rules with today's violations frozen in a baseline, so a new forbidden import fails the check.
- Make the bugs obvious, then resist. Whatever the new layout reveals is written down as a finding and never fixed: on immich, an import cycle through 41 of 70 folders (42 of 73 in R4's count, which splits nested folders), and a base class that injects 56 members into 51 services.
- What you get. A branch of small commits you can review or bisect, each with its certificate's verdict and
digest in a trailer;
plan.mdto read andplan.yamlto edit; the findings log; an HTML report; and, for a repository it can't certify, a refusal with the reasons before any branch or commit exists.
On immich
On immich's server, unattended: 63 commits, each certified, in 1 h 55 m. 367 of 563 files moved into 38 feature
modules, 20 platform folders and shared/. tsc reports 0 errors, 105/105 test files and 2,337/2,337 cases are equal
to base, and the OpenAPI spec is byte-identical. From a fresh clone, with immich's dependencies installed, the replay
reproduces the digests of three sampled step certificates and of the whole-PR certificate, byte for byte.
No step failed in that run, so to show the loop recovering we seeded a wrong move: ac run --inject-fault 2:M2
replaces step 2's commit with one that leaves one import unrewritten. The certificate fails it (K3a, K3b, K3c), the
commit is rolled back, album.dto.ts is pinned, the step passes without it, and the R1 rung then passes with the
oracles (13 m 37 s; runs/immich-fault on the replay site).
The same task, given to a generic agent
opencode 1.18.34 on Nemotron 3 Super, the same task with a fixed prompt and no human input. Its single-shot run copied
211 files and left the originals: 2,337/2,337 cases pass, but 66 of 171 test files never load, and tsc reports 1,680
errors. Its persistent run moved 297 files, copied 562 more into a src.backup/ tree, and left 768 tsc errors, with 5
of 105 test files loading. Is it just the better model? On Nemotron 3 Ultra, the model ac plans with (opencode at
the model's default effort, with its own $2 and $12 caps): the single-shot run ran ls on the same two folders 38
times in 48 tool calls and changed nothing ($0.55 billed; its $2 cap, counted at the full input price, stopped it at $1.86); the persistent run
moved 374 files, rewrote not one import, and hit its $12 cap after 9 min 26 s ($2.55 billed: 90% of its input came from the prompt cache, which our caps counted at the full
price). ac certify fails every branch that
changed something (Ultra's single-shot run changed nothing, so there was nothing to certify), and says exactly why; it
passes ours.
Try it
- In two minutes, on the replay site: the A/B page (https://corupta.github.io/architecture-cleaner/ab/) puts the
generic agent's branches and ours side by side under the same checker;
immich-fault(https://corupta.github.io/architecture-cleaner/runs/immich-fault/) shows the loop rolling back a seeded bad commit;immich-ladder(https://corupta.github.io/architecture-cleaner/runs/immich-ladder/) has all 63 certificates and the 45 findings. Nothing on the site calls a model. - The test build:
npx ac demo --replayin a fresh clone replays the plan from the cassette, executes all 62 move steps and R4's rules commit again, checks that each commit's tree equals the recording, and certifies a sample again. No key, $0, about 11 minutes. With immich checked out and its dependencies and SDK builds installed (the README's full reproduction, under Node 24.21.0),--env <checkout> --oraclesreproduces the digests of the three sampled certificates and of the whole-PR certificate byte for byte (about 20 minutes).
How we use Nemotron on Nebius Token Factory, and what it cost
Nemotron plans and narrates; it never edits code. A model's mistake can only be a bad plan or a wrong sentence in the findings log (one is on record, below), and neither can change code: a bad plan meets the validator, and every commit made from it meets the certificate.
- Nemotron 3 Ultra (
medium_effort) edits the deterministic draft: three candidates in onen=3call, under a strictjson_schema, ranked by the validator. A second call reviews the winning plan like a staff engineer, and the gate drops any suggestion that adds a warning or joins a group without evidence. immich's whole plan: 2 calls, $0.1998. - Nemotron 3.5 Lightning (thinking off) narrates the findings log, one or two sentences per finding, under a lexical guard that drops any narration naming something its evidence doesn't contain: $0.0010.
- Why edit a draft instead of asking for the map? In our first gate, asked to list every file's group, Nemotron 3 Super and Ultra dropped 10–15% of hoppscotch's 189 files and 26–45% of kaneo's 456, and the literature finds classic clustering ahead of LLM decompositions. So the draft is deterministic and complete, and Nemotron supplies the judgment on top of it: names, contested files, feature or platform. Our benchmarks below show where that wins and where it doesn't.
- The same model, two jobs. In a generic agent loop, Nemotron 3 Ultra read 11.6 million input tokens in nine
minutes and left every import of the files it moved dangling (1,638). In
acit makes two planning calls for $0.20, code does the moves, and the checker proves them. - Every call
acmakes goes through one gateway over Token Factory's OpenAI-compatible API: strict schemas, reasoning set throughchat_template_kwargs, hard budget caps checked before each call, and a cassette of every request and response. That's why the replay needs no key, costs nothing and never calls Token Factory: the plan, every commit's tree and the findings log come out identical to the recording. - Where Token Factory sped the work up: one key and one OpenAI-compatible client for every Nemotron tier, so each
model comparison was a model id (Super VL's longest answers also needed streaming, past the 10-minute limit for
non-streamed calls); strict
json_schemaon every Nemotron model we used, so every finished answer parses against its schema on the first try, with no repair loop; andn=3with the prompt billed once (details in the README).
All of it, from the gate probes to both baseline arms and the Super VL experiments, came to $24.40 on Token Factory's bill, of our $50 cap, and our ledger reconciles with that bill to the cent.
Benchmarks, losses included
ac scramble flattens a feature-modular repository into type folders, in one commit our checker checks (three
scrambles pass; kaneo's fails closed), so the original folders are the ground truth. Against the best deterministic
method (MoJoFM, 0–100), the planner's map wins on hoppscotch (87.59 against 81.38 in the recorded run; three fresh
Ultra runs of the same request scored 81.38 to 84.83, mean 83.22) and on hoppscotch with opaque file names (83.45
against 81.38), and loses on kaneo (50.65 against 66.90) and on immich (MoJoFM 88.37 against 93.02). It also scores
higher on ghostfolio (86.81 against 84.72), but our own validator rejects that plan, so counting only accepted plans
it wins on 2 of 5. immich isn't a scramble, so its MoJoFM counts only the 47 of its 269 movable files that the
feature folders of its March 2024 domain layer still label once git history is followed to the pinned commit: mostly
services and DTOs named after their feature, which a grouping by file name nearly reproduces. On TurboMQ, which scores
how cohesive the modules are in the import graph and needs no answer key, Louvain (which optimizes modularity, a close
cousin of TurboMQ) scores highest on four of the five; the planner is highest only on ghostfolio (19.93 against 19.88
for the seeds draft by file names), with the plan our validator rejects.
Preliminary: Nemotron 3.5 Super VL, with thinking on and 256k tokens to reason, beat Ultra on all four scrambles on
MoJoFM (hoppscotch 91.26 against Ultra's 83.22 mean; pooled over the four, p ≈ 0.0007), and its mean TurboMQ was
higher on all four too, by 0.07 to 1.13, though none of the experiment's runs, of either model, tops the best
deterministic method on TurboMQ. It took 2.5 times the cost (at the full input price; 2.7 times as billed) and 6.4
times the time. On immich the two are under one MoJo operation apart over its 2024 key (88.37 against the 89.15 mean
of Ultra's three fresh runs; an operation, one file moved or two groups joined, is 2.33 points there), and score 10.26
against 10.20 on TurboMQ. The limits: three runs per entry, possible recall of these public projects, and Ultra
compared only at medium_effort. The demo keeps Ultra: on immich there is no measurable difference, and the recorded
run reproduces byte for byte.
What's new, and what isn't
Moving TypeScript files with their imports rewritten is a commodity (ts-morph, IDE refactorings), and so is an LLM
deciding while a deterministic engine moves: the closest tool we found, @demlik/structure-sweep (first published on
npm on 2026-09-24), asks an LLM for each file's feature and rewrites the imports with ts-morph. The ideas under our
checker are old too: translation validation (Pnueli, Siegel and Singerman 1998; Necula 2000) and refactorings that
preserve name binding (Schäfer et al.). What we didn't find in our search (2026-10-01/02): a checker, separate from the
tool that made the change, that certifies each step of a module move by translation validation (the move set
complete, only specifier bytes changed, every import resolving to the same file under the compiler, the test runner
and the runtime) and certifies any diff, ours or another agent's; and findings recorded and never fixed, as a
contract.
Our own misses
The checker exists to catch mistakes, ours included, so we list ours. Our executor's first immich step left coverage globs naming the moved files, and the certifier caught it. An early full run passed, but under load vitest had exited 0 after reporting 54 of 105 test files, and the baseline had quarantined 61 of them; a run that stops short is now replaced. Our own benchmark scrambles failed our certifier on false positives, each fixed with a proof, while kaneo's still fails closed. One of those fixes introduced a false pass, and our review caught it before it shipped. Four adversarial reviews confirmed 79 defects in our own code, each fixed with a regression test. And one narration our guard let through stays in the findings log as recorded, labeled as the model's words.
How we built it
- A mutation harness that attacks the checker: 831 mutants over 7 fixtures, 2 synthetic repositories, openhab-cloud, two immich steps and 53 hunt cases, all 831 killed by the static checks alone, before a single test case runs.
- A static replay site, rendered from the committed run artifacts, byte-identical across builds, on GitHub Pages.
- Built by corupta with Claude Code (over Paseo, with Claude Opus 5.5 at the ultracode effort setting) doing most of
the implementation; corupta set the direction, made the decisions and reviewed. Every session's report is in
docs/sessions/. Every run, the oracles and the video were made on an NVIDIA Jetson AGX Orin 64 GB (JetPack 6.2), on its CPU (the models ran on Token Factory). - Every number in the README, the storyboard, the site and the video is listed with its source and how it was checked (numbers.md): 2,489 at the freeze, and a section for each later addition (S10, S11, and both benchmark metrics on every entry).
What's next: Clean Architecture
The hackathon version stops at feature modules with enforced boundaries. The frozen baseline of violations is the work list for the next part: for each violation, a port and an adapter, proposed as a report first, then applied one port per step. Inverting a dependency changes call sites and wiring, so a static certificate can't cover it; each step will need a behavioral oracle (golden-master or differential runs, the target's tests, contract diffs such as immich's OpenAPI spec), on the same ladder, under the same never-fix rule.
Built With
- claude-code
- dependency-cruiser
- ffmpeg
- git
- github
- github-actions
- graphology
- nebius-token-factory
- nemotron-3-super
- nemotron-3-ultra
- nemotron-3.5-lightning
- nemotron-3.5-super-vl
- node.js
- nvidia-jetson-agx-orin
- nvidia-nemotron
- openai-sdk
- paseo
- playwright
- typescript
- undici
- vhs
- vitest
- yaml
- zod


Log in or sign up for Devpost to join the conversation.