The starter repository
Alpha-Z-Pte-Ltd/frontieror-challenge-starter is a working setup to build on. It downloads the problems, runs an example agent that writes a solver for each one, tests the solvers the way the sandbox runs them, and submits them.
You need uv, macOS or Linux (WSL on Windows), and an account with a team. The example agent calls a model through OpenRouter, so it needs an OpenRouter key. Docker is needed only for --docker.
Quick start
git clone https://github.com/Alpha-Z-Pte-Ltd/frontieror-challenge-starter.git
cd frontieror-challenge-starter
uv run frontieror init # sign in and download the problems
uv run frontieror find-solvers # let the agent write solvers/<slug>/solve.py
uv run frontieror test # run them on the public instances
uv run frontieror submit --wait # submit and wait for the score
init opens your browser to approve the sign-in, then saves an API token in .env. See Authentication. Run it again when a new round opens.
Commands
| Command | What it does |
|---|---|
init | Signs in (or uses --token), asks for an OpenRouter key if none is set, downloads the open round's problems, public instances and published checkers to data/, and prepares the Python environment. |
find-solvers [SLUG…] | Runs the agent for each problem without a solver. --force replaces existing solvers; --jobs N works on several problems at once; --yes runs without asking when data/ is from an earlier round. |
test [SLUG…] | Runs each solver on the public instances, checks the output format and, with the problem's published checker, feasibility. Outputs and logs go to runs/<slug>/test/. |
submit | Packs every solvers/<slug>/ folder and submits it in python-3.14, with the extra packages from frontieror.toml. It shows when the results should arrive; --wait follows the progress and shows the score; --yes skips the questions; --acknowledge-missing submits even when some problems have no solver. |
Add --docker to find-solvers or test to run solvers in the sandbox's image with its limits.
Which files are yours
| Path | |
|---|---|
agent/main.py | The agent: find_solver(problem, tools, settings) and its model call through OpenRouter. Change it freely. |
agent/prompts.py | The prompts, from FrontierOR's one-shot prompt adapted to the sandbox. |
solvers/ | The solvers, one folder per problem. This is what gets submitted. |
frontieror.toml | Extra packages and the agent's settings: model and max_attempts. |
toolkit/ | The frontieror command. Leave it unchanged so updates apply cleanly. |
The agent entry point
find-solvers calls the function named by [agent] entry in frontieror.toml (agent:find_solver by default) once per problem:
def find_solver(problem: Problem, tools: Tools, settings: dict) -> None:
...
problemhas the problem'sslug,name,objective,description,instance_schema,solution_schema, its publicinstancesandchecker, the path of its published checker orNone.tools.test(code)runscode, which definessolve(instance, time_limit_s) -> dict, on the public instances (or the ones named ininstances) and returns a report with each run's status, message, objective, time and log. The toolkit appends the part that reads the instance and writes the solution.tools.save(code)writessolvers/<slug>/solve.py, with the same part appended.tools.log(message)prints a progress line;tools.log_file(name, text)writes a file toruns/<slug>/.tools.environmentlists the Python version and the packages available, for the prompt.settingsis the[agent]table offrontieror.toml.
The example agent asks the model for a solver, tests it and feeds the failures back, for up to max_attempts replies. It saves the attempt that passes the most instances. The replies and each attempt's runs are kept in runs/<slug>/.
Testing locally
test runs natively by default: fast, but without the sandbox's memory limit or isolation. test --docker runs in the same image with the same limits as the sandbox. On Apple Silicon it runs under emulation, so it checks correctness, not speed.
Both check the run and the solution's format, then run the problem's checker when it is published: natively, or with --docker in the python-3.12 image as in the evaluation. A solution the checker rejects shows as infeasible, with the violations. Scores come only from a submission.
A minimal solve.py
A complete solver for barnhart2000 that rejects every commodity, which is always feasible:
import argparse
import json
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--problem", required=True)
parser.add_argument("--instance", required=True)
parser.add_argument("--output", required=True)
parser.add_argument("--time-limit", type=float, default=60)
args = parser.parse_args()
with open(args.instance) as f:
instance = json.load(f)
commodities = instance["commodities"]["commodity_list"]
objective = sum(c["artificial_arc_cost"] * c["demand"] for c in commodities)
solution = {
"objective_value": objective,
"commodities": [
{"commodity_id": c["commodity_id"], "rejected": True, "path_arcs": []}
for c in commodities
],
}
with open(args.output, "w") as f:
json.dump(solution, f)
if __name__ == "__main__":
main()
Zip the barnhart2000 folder and it is a valid submission: the solution is feasible, though far from optimal. The contract it follows is on Submission format.