Inspiration

AI continues and will continue to become increasingly smarter and cheaper, and thus finding complex vulnerabilities will become cheaper and faster. In this new age of cybersecurity, memory safety will become more important than ever before, and we will need tools to modernize and patch codebases as fast as new tools uncover vulnerabilities. Rust makes it a priority for your code to be memory safe. Moving to rust is an undeniably helpful step to take most legacy codebases, but, we recognize migrating languages is a tedious and long process, which is where Argo comes in.

What it does

Argo parses a legacy C codebase into a Blueprint-style graph and converts one node at a time into rust, interoperating the new rust code with the rest of the C codebase and running tests every step of the way.

  1. Parsing: Argo first parses the whole C codebase into an AST using a generic parser, then uses a custom parser to have more control over the result.
  2. Frontend: Loads result into spaceitmeDB and visualize nodes with runeUI.
  3. Migrate: Deploys agents into their own atomic copy of the codebase, they convert c code into rust, then they compile and test with the new rust node. Then the rust code gets commited into the main rust database.
  4. Iterate: We repeat the migration step until all of the C nodes have a rust equivalent.

How we built it

We split Argo into small crates that each do one job and talk through a single shared set of records.

  • C front end: It reads a real repository: the system preprocessor expands includes and macros, a generic parser gives us the AST, and our own lowering step with keyword templates turns that into a graph of nodes with explicit begin and end boundaries. The same front end can export any project into plain protocol records, so nothing about the UI or the database leaks into the parser.

  • One contract: Every other crate agrees on those records: the graph structure (nodes, pins, edges), provenance that points each range back into the original files, and the migration status on top. SpacetimeDB is only the transport; the records are what the rows mean.

  • The database: The database is a SpacetimeDB module with one row per chunk of C and one per claimed chunk's Rust. It only stores rows and enforces the rules — who owns the database, who holds a claim, how writes are fenced — and never reads files or calls a model. A small client crate translates both ways between those rows and the shared records, so the server and the UI agree on what a row means.

  • Loading a repo: The server starts a local SpacetimeDB, publishes the module, checks out the exact commit, parses it with the C front end, and pushes the chunks in.

  • Migrating: A worker connects, clones that same commit, claims a chunk, asks nemotron to translate it using the surrounding context, shape-checks the reply, and submits or fails it. Every write quotes the claim it came from, so a stale worker can never overwrite newer work.

  • Seeing it: A read-only RuneUI canvas subscribes to the same two tables, rebuilds the graph from the rows, and lets you pan, hover and open functions — but never write.

Challenges we ran into

  1. Separating our code into control flow blocks: How do we parse the C code into something compatible with the Blueprint style we are aiming towards? We solved this by first using a generic parser to get the AST, then a custom parser to fully control and define where we wanted each node to begin and end.

  2. Compiling combined new rust with old c codebase: How do we combine the C and rust databases and compile them together? We solved this by compiling the generated rust code using extern-C into the C_abi which we then compile with the rest of the C program.

  3. Refactoring: How do we handle preprocessor directives and previous context? Initially we built the parser to work after the compiler's preprocessor, we did this so it would only have to work with one file and so that all the preprocessor directives would be injected. The problem we found with this is that when we tried to migrate other C codebases, specifically github, we ran into conflicts with duplicate names. To solve this, we refactored to give each agent access to the full context of the codebase, but only allowed it to modify it's atomic rust database it's supposed to modify.

Accomplishments that we're proud of

  • Migrated a full CPU emulator into rust code without any human intervention.
  • Blueprint UI frontend is pleasing to look at and instantly comprehensible.
  • Everything works with cheap, fast, small AI models. //is fast true
  • Used spacetimeDB to manage our agents, allowing for effortless scalability with more agents.
  • Compiling rust code and C code together, allowing us to continously run tests at each step of the process

What we learned

  1. RuneUI: RuneUI allows agents to more directly interface with UI, making for a faster iteration process and an overall more polished frontend experience.
  2. Hill-Climbing: Spending time building a mental model of how things should work, and spending time specifying how subagents should work and interact with other subagents' work produces better quality code and doesn't accumulate technical debt.
  3. Balancing reading and writing code: When writing less and less code by hand, whether it is because of team members or agents, there needs to be a balance between reading and spenidng time understanding code and writing code.

What's next for Argo

  1. More languages: We have code that can be pretty easily adapted to migrate between any two languages with reasonable interoperability.
  2. Refactoring suggestions: Having the Blueprint graph can allow models to suggest logic changes and then implement them in the target language.
  3. Libraries: If there's no equivalent library in the target language, the program will still compile, but it will never fully be converted to the target language. In the future we can expand Argo to also convert libraries.

Built With

Share this project:

Updates

Submission history