How it works
Eclipse Atesor™ takes a repository URL and runs a fixed sequence: a cache check, a sandbox start, a graph of agents, and the output files. Scripts do each step that a script can do. Atesor makes an LLM call only where a step needs judgment. Two examples are a build plan for an unusual project and a fix for a new error.
This page gives the concept. Architecture overview gives the detail.
What happens when you start a port?
Section titled “What happens when you start a port?”You start a port with a command such as python3 main.py --repo https://github.com/madler/zlib. Then main.py does these steps:
- Check the input. Atesor accepts only a plain
httporhttpsURL without shell metacharacters. It takes the package name from the URL, for examplezlib. - Look in the recipe cache. If the cache has a recipe for this package, this sandbox and this URL, Atesor writes the recipe file and stops. This step needs no API key and no Docker. The
--forceflag skips the cache. The--optimizeflag also skips this step, because performance mode needs a real build tree. - Check the API key. Atesor reads
LLM_PROVIDERand stops if the key for that provider is not set. - Start the sandbox. Atesor checks that the host can run riscv64 code through binfmt_misc. It builds the sandbox image if the image does not exist, starts the container, and checks that the container reports
riscv64. - Run the agent graph. A LangGraph state machine runs the nodes in the next section. One
AgentStateobject carries all data of the run from node to node. - Write the outputs. Atesor writes the state snapshot, the build report, the recipe and the patches. With
--package, it also writes a zip file.
How does the graph go from source code to recipe?
Section titled “How does the graph go from source code to recipe?”The graph is in src/graph.py. The picture shows the usual path of a run, from the command to the output files. A blue LLM tag marks the nodes that can call an LLM.
init_nodeclones the repository inside the sandbox. It then runs a scripted analysis: build system detection, dependency extraction, and a scan for architecture-specific code.- With
--optimize, if the recipe cache has a verified recipe of the package,init_nodesends the run tocached_recipe_node. This node makes the cached build plan the plan of the run and applies the cached patches. The analyst and the scout make no LLM call. If the cached recipe has no command, the run goes toanalyst_node.--forcemakes Atesor plan again.
- With
analyst_nodereads excerpts of the real build files and docs. It writes a package analysis: the purpose, the build system, the dependencies, the RISC-V risks and the expected artifacts. It also setsneeds_custom_planwhen a standard recipe will not work.heuristic_plan_nodewrites a default build plan for CMake, Make, autotools and Go. It sends the work toscout_nodein these cases:- The analyst set
needs_custom_plan. - The confidence in the build system is less than 0.5.
- The scan found one critical, or three or more high, architecture-specific findings.
- The build system has no default plan, for example Meson or Cargo.
- The analyst set
scout_nodeasks the LLM for a build plan, based on the analysis and the build files. When it plans again after a failure, it also sees the error.supervisor_nodechooses the next node with rules only. ASUCCESSstatus always goes tofinish_node. A failure with aDEPENDENCYorMISSING_TOOLSerror goes back to the scout for a new plan. A known plan error, such as a missing./configurescript, also goes back to the scout, one time for each error type. Other failures go back into the build-fix loop.- Inside
build_fix_subgraph,build_noderuns the phases of the plan. When a command fails, it first tries its recovery heuristics.verify_nodereads the ELF header of each build output.fix_nodeasks the LLM for a fix, applies it, and sends the run back tobuild_node. finish_noderuns the package’s own tests one time. It then asks the LLM to write the recipe. When the port is verified, it saves the recipe in the recipe cache and adds few-shot examples.optimize_noderuns afterfinish_nodeonly with--optimize. The next section describes it.escalate_nodewrites a report for a person when the run cannot finish.
What happens after a verified port with --optimize?
Section titled “What happens after a verified port with --optimize?”Performance mode is an optional stage after the port. It tries faster builds of a verified port for the machine that runs Atesor. Each build that it tries is a tune: the same build with other compiler or toolchain settings. The verified build always stays the result of the port. A faster tune is an extra output.
- Probe the machine. Atesor reads the ISA extensions, the core, the CPUs and the compilers of the machine.
- Build a baseline. Atesor builds the verified build again in a copy. It compares each tune with this copy.
- Select a workload. The workload is a run that does the same fixed work each time, for example the benchmarks of a Go module. For programs and libraries, the optimizer agent (an LLM) proposes a program run or writes a small harness.
- Build the tunes. Examples are
-marchwith the probed extensions,-O3, LTO, clang and PGO (profile-guided optimization). - Check each tune. Each tune must make riscv64 artifacts with its settings. The package tests must still pass, and the workload output must be the same as the baseline output.
- Measure each tune. Only on the native target, Atesor times each tune against the baseline. A faster tune must win again in a second check. Under QEMU, Atesor checks the tunes but makes no speed claim.
For a port that is not verified, performance mode stops with the status skipped. It never changes the exit code of the port. Performance mode tells how to use it. Performance mode internals gives the design.
What does “deterministic first” mean?
Section titled “What does “deterministic first” mean?”Deterministic first is the first design rule of Atesor: do not spend an LLM call on work that a script can do. A deterministic step gives the same result for the same input. The rule shows in each part of the pipeline:
- Analysis. The clone, the build system detection, the dependency extraction and the architecture scan are scripts in
src/scripted_ops.py. - Planning. For a known build system, the default plan comes from code. The LLM scout runs only when the default plan cannot work.
- Routing. The supervisor uses rules. It makes no LLM call.
- Repair. The builder tries its recovery heuristics before the fixer gets the error. For example, it installs the distro package for a missing header, or it builds again on one thread after a suspected out-of-memory kill under QEMU.
- Verification. A script reads the ELF headers. No LLM decides if a binary is riscv64 code.
- Performance mode. Scripts probe the machine, build the tunes, check them and time them. The LLM only proposes a workload. Statistics, not an LLM, decide if a tune is faster.
- LLM answers. Each answer must pass a schema check. When an answer is not valid after the retries, a deterministic fallback takes its place where one exists.
Where does Atesor use an LLM?
Section titled “Where does Atesor use an LLM?”An LLM call happens only at these points:
| Step | When | Fallback if the LLM fails |
|---|---|---|
| Analyst | Once at the start of the run, but not in a run from a cached recipe | An analysis from the scripted detection |
| Scout | No default plan, or a new plan after a dependency, tool or plan error | A fallback build plan |
| Fixer | After a build or verification failure | None. The run fails or escalates. |
| Summarizer | Once at the end of a successful run | A short recipe from the build steps |
| Artifact curator | Once at the end, to rank the build outputs | Rule-based ranking |
| Optimizer | Only with --optimize, after a verified port: a program run or a harness for the workload |
The next workload source. The package tests are the last source. |
Before the fixer proposes a fix, it can ask for one round of read-only commands, such as cat or readelf. The round has a limit of 4 commands.
Atesor writes each call to the LLM call log, with its tokens and its cost. The cost cap stops a port at $1.00 of LLM cost. The supervisor applies the cap, so the cap does not stop the optimizer, which runs after the port. See Cost control.
How does a run end?
Section titled “How does a run end?”A run stops at the first of these conditions:
- The build succeeds. Atesor goes to
finish_node, also when the success comes on the last attempt. - The run reaches the attempt limit, 5 by default (
--max-attempts). - The last three errors have the same signature (an error loop).
- The last error is a permission error or a disk space error.
- The LLM cost is more than $1.00.
Every stop except a success goes to escalate_node, and the command exits with code 1. A success exits with code 0 when verification proves riscv64 output, or with code 3 when it does not. With --optimize, performance mode runs after a success and does not change the exit code.
Next steps
Section titled “Next steps”- Concepts: the terms on this page.
- Your first port: see the flow on a real package.
- Architecture overview: the nodes, the routing functions and the state.