Remember
Paste a dormant GitHub app; get it back running in a sandbox, or an honest refusal when it can’t be made safe.
- Context
- Daytona HackSprint Singapore, team of three with Kendrick and Moiz, a five-hour build
- My part
- Much of the build: the run workspace, the live Daytona flow and more
- Stack
- Next.js, TypeScript, Daytona, OpenAI, Nosana, Vitest
- Year
- 2026

The problem
Old software survives as a GitHub repository, a few screenshots and a dead deploy link. The code is still there, but running it again means rebuilding an environment from memory: which runtime, which package manager, which start command, which port, and which of its pinned dependencies no longer installs.
A language model can guess at those fixes, but it can’t be trusted to say whether a fix worked, and it certainly can’t be allowed to run whatever it suggests on a real machine. So the problem is really two: make repair cheap to try, and make success something the model has no say in.
How it works
A run starts from one public HTTPS GitHub URL, validated and canonicalised before any provider is called, and gets a stable run ID the browser polls every second. Everything after that happens in Daytona sandboxes, inside an eight-minute budget for the whole run.
- SeedA resource-limited sandbox (2 CPUs, 4 GiB of memory, 10 GiB of disk, a 15-minute lifetime) clones the repository, and a snapshot, S0, is taken before anything is installed. S0 is the pristine state every later attempt starts from.
- Inspect and detectEvidence is read from an allowlist of files, capped at 32 KiB each and 128 KiB in total. Plain code, not the model, works out the language, framework, package manager, install and start commands and likely ports, for Node and Python projects.
- BaselineA child fork installs and starts the project as detected, and the seed stays untouched. If it verifies, the run is done without any repair or model call.
- Repair raceIf the baseline fails, OpenAI gets the bounded evidence and the failure, and returns exactly three distinct strategies as structured actions. Each runs in its own fork of S0, in parallel.
- Verify and chooseA candidate counts only if its process is still alive and a signed preview URL answers HTTP 200 to 399. Among those that pass, the least invasive wins.
- Clean up and publishLosing forks, the seed and the snapshot are deleted, and the preview goes back to the browser. For GUI projects, Nosana can add a visual check afterwards, but it never decides success.
The repair race

What the model is allowed to say
The planner’s whole output is a diagnosis and three strategies. Each strategy is a list of one to eight actions from four types, and nothing else parses. Titles and hypotheses are length-capped, ids must be repair-a, b and c, and three strategies with the same title are rejected as not meaningfully distinct.
type RepairAction =
| { type: "run_command"; command: string; reason: string }
| { type: "write_file"; path: string; content: string;
reason: string }
| { type: "replace_text"; path: string; search: string;
replacement: string; reason: string }
| { type: "try_start"; command: string;
expectedPorts: number[]; reason: string };Before the model sees anything, logs and files are trimmed and anything shaped like a key, token or password is redacted. Before an action runs, it is bounded again: at most six per strategy, commands under 2,000 characters and 120 seconds, file writes under 64 KiB, and every path resolved inside the repository, rejecting traversal, absolute paths, drive letters and NUL bytes.
Choosing a winner
The model labels each strategy as environment, config, dependency or source. Remember doesn’t take its word for it. It derives the level from what the actions actually touch (a lockfile is a dependency change, a .nvmrc is environment, an unrecognised command is source) and keeps whichever is higher. Verified candidates are then sorted by that level, then fewest files changed, then fastest boot, then id.
const compareRepairs = (left, right) =>
INVASIVENESS_RANK[effectiveInvasiveness(left.strategy)] -
INVASIVENESS_RANK[effectiveInvasiveness(right.strategy)] ||
left.changedFiles.length - right.changedFiles.length ||
left.bootDurationMs - right.bootDurationMs ||
left.strategy.id.localeCompare(right.strategy.id);When nothing verifies

Who did what
Three of us over the five hours: Kendrick, Moiz and me. The commit history only shows Kendrick and me, nine commits from him and five from me, and it undercounts my part: much of what I wrote was committed from his laptop.
- MeThe workspace people watch a run in: repository submission, the sandbox provisioning panel, the orchestration diagram, the repair race and the preview and share dialogs. I wired it to the live Daytona flow, split the server into live, demo and unconfigured modes, and added tests on the Daytona provider and the runtime config. I also built the prepared Bonky Inu specimen, a revived game that opens instantly, so the demo never depended on a live run finishing in front of judges.
- KendrickThe first interface and run flow, the production integrations with Daytona, OpenAI and Nosana, the design system pass, and the README.
- MoizThe idea: he was part of the ideation.
Decisions
Snapshot before install, then fork for every attempt
Installing dependencies changes a repository in ways that are hard to undo. Taking S0 first means the baseline and all three repairs start from the same clean state, so they can run at once and a failed attempt can’t poison the next.
The model proposes; a running process decides
Success is a live process answering HTTP on a signed preview URL, checked before, during and after each probe. The model never grades its own repair, and a plan that parses but doesn’t boot simply loses.
Prefer the smallest fix, and measure its size ourselves
A repair that pins a Node version is safer than one that rewrites source, even if both boot. Because the invasiveness level is derived from the actions and can only go up from the model’s label, a strategy can’t call a source edit a config change to win.
Detection is code, not a prompt
Reading package.json, lockfiles and requirements is deterministic and cheap, and gets most dormant apps right. The baseline runs on that alone, so a project that still works never waits on a model.
Fail closed, and keep the evidence
If nothing verifies, or a provider key is missing, no preview is published and the API answers 503 instead of guessing. Every step is written to the run as an event, cleanup included, so a failure can be read afterwards instead of disappearing.
Results
3
Repair strategies per failed baseline, each in its own fork of the same pristine snapshot
61
Unit tests across 20 files, on verification, ranking, path policy, bounds and the provider adapter
8 min
Budget for a whole run, from sandbox to verified preview or refusal
A five-hour hackathon build. The tests are unit tests with a mocked provider; there are no live Daytona, OpenAI or Nosana tests in the repository and no recorded success rate across real repositories. On stage the prepared specimen carried the demo, so the live pipeline’s reliability on arbitrary repos is unmeasured.
Limits
- Node and Python web projects only; anything without a detectable install and start command is refused.
- Run state lives in local JSON files and the job runs in the web server’s process, so a restart loses runs in flight.
- The eight-minute deadline stops the run but doesn’t cancel provider calls already in progress.
- The interface says network egress is blocked, but the backend never enforces it.
- Nosana receives the preview URL rather than a captured screenshot, and its visual check was never tested against a live endpoint.

