A run of kodr phases all pointed at the same stubborn truth: generating files is the easy part. Getting the generated thing to install, run, and pass its own tests - on a local model, without me hand-fixing it - is where the real work hides.
Phase 65: a dependency install that isn’t a shell
Staged execution made kodr fail honestly when no verification ran. Correct, but it surfaced the next gap: a generated Node app usually can’t be tested until its dependencies are installed. So kodr run --install runs after applied writes and before verification. It is emphatically not a shell escape or a model tool for arbitrary package commands - the allowlist accepts only npm install and npm ci, and kodr picks npm ci when a lockfile exists, npm install otherwise. The result lands in install.json; if install fails, the run fails and verification is skipped, keeping the failure machine-visible while preserving the files for the next repair turn.
The payoff was reaching a useful failure under its own power. The Nemotron Postgres example: install passed with npm install, verification ran, and npm test died on ReferenceError: describe is not defined - the model had written Jest-style globals instead of native node:test imports. That’s exactly the right failure boundary. Kodr can now get there itself instead of needing a human to run the install step by hand.
Phase 71: plan, then execute (and the tool-call trap)
Phase 71 was an acceptance test for the whole inspection-plus-scratchpad arc: don’t hand-edit kodr, ask kodr to inspect itself with a local model, write a plan, carry it into a later turn, and produce a real multi-file patch. The first finding was a beauty. Local LM Studio requests that sent both native tools and strict structured output didn’t reliably call tools - Qwen and Nemotron both returned ordinary proposal JSON while claiming they’d inspected files. The tool schemas were in the request, so the registry wasn’t the problem. Removing response_format for local tool-call requests fixed it: Qwen called list_files, consumed the result, then returned the normal envelope.
So kodr now keeps strict response formats for remote providers and for local proposal turns without tools, but omits them for local native-tool loops - a pragmatic compatibility call for small OpenAI-compatible servers. The acceptance artifact landed across two runs: one called inspect_symbols and produced a plan for adding index.totalReferences; the second injected that plan as scratchpad context, called read_file, and produced a two-file dry-run patch. Plan-then-execute, no frontier model in sight.
Phase 72: a repair loop with brakes
The repair attempts kept exposing the same problem: a small repair prompt, but the model call would run past the timeout and leave only partial artifacts. That’s not a repair loop, that’s a hang. Phase 72 adds --heal: after an applied run fails verification, kodr starts a bounded repair loop with deliberately narrow context - tests.json, the failing path from the stack trace, and nearby source like the sibling implementation file.
Each repair turn writes artifacts under repairs/turn-N/ (prompt, context, response, writes, snapshot diff, tests, errors). The loop stops when verification passes, when the budget is spent, when two turns make no workspace progress, when a repair edits a sibling file instead of the failing path, or when a repair call times out. Every exit is bounded and inspectable. A later linkrot test even caught a lovely edge case: Node stack traces carry absolute paths, and the relative-path scanner could match the src/project/... suffix inside /Users/.../src/project/..., producing phantom empty context files. The repair context now normalizes against the workspace and drops non-existent suffix guesses. Kodr still won’t hand-fix the sample - it now has the machinery to feed the real failure back to the model and let it try, on a leash.
Links:
- Phase docs: 65-dependency-install-workflow, 71-self-dev-plan-then-execute, 72-multi-turn-self-healing-loop
- Kodr blog: 65, 72
- The install step: src/dependency-installer.mjs and the repair loop: src/healing.mjs