Een open-source spec voor Codex-orkestratie: Symphony
Door Alex Kotliarskyi, Victor Zhu en Zach Brock
Zes maanden geleden nam ons team, terwijl we aan een interne productiviteitstool werkten, een beslissing die toen controversieel was: we zouden onze repo bouwen zonder door mensen geschreven code. Elke regel in onze projectrepository moest door Codex worden gegenereerd.
Om dat te laten werken, hebben we onze engineeringworkflow vanaf de basis opnieuw ontworpen. We bouwden een agent-vriendelijke repository, investeerden zwaar in geautomatiseerde tests en guardrails, en behandelden Codex als een volwaardige teamgenoot. We documenteerden die reis in onze eerdere blogpost over harness engineering.
Het werkte, maar we liepen vervolgens tegen een nieuwe bottleneck aan: contextwisseling.
Om dit nieuwe probleem op te lossen, bouwden we een systeem genaamd Symphony. Symphony(opent in een nieuw venster) is een agent-orkestrator die een projectmanagementtool zoals Linear verandert in een control plane voor programmeeragents. Elke open taak krijgt een agent, agents draaien continu en mensen beoordelen de resultaten.
In deze post leggen we uit hoe we Symphony hebben gemaakt, wat bij sommige teams leidde tot een stijging van 500% in gemergede pull requests, en hoe je het kunt gebruiken om van je eigen issuetracker een altijd actieve agent-orkestrator te maken.
De grenzen van interactieve programmeeragents
Zelfs nu programmeeragents steeds makkelijker te gebruiken zijn, blijven het interactieve tools, of je ze nu via webapps of de CLI gebruikt.
Naarmate er bij OpenAI meer agent-ondersteund werk kwam, liepen we tegen een nieuwe uitdaging aan. Elke engineer opende een paar Codex-sessies, wees taken toe, beoordeelde de output, stuurde de agent bij en herhaalde dat proces. In de praktijk konden de meeste mensen zonder al te veel moeite drie tot vijf sessies tegelijk aan voordat het voortdurende schakelen tussen contexten te veel werd. Daarboven nam de productiviteit af. We vergaten welke sessie waarmee bezig was, sprongen tussen terminals om agents weer op koers te brengen en moesten problemen oplossen bij langlopende taken die halverwege vastliepen.
De agents waren snel, maar we hadden een systeembottleneck: menselijke aandacht. We hadden in feite een team van extreem capabele junior engineers opgebouwd en vervolgens onze menselijke engineers de taak gegeven hen te micromanagen. Dat is onmogelijk op te schalen.
Een verschuiving in perspectief
We beseften dat we op het verkeerde optimaliseerden. We richtten ons systeem op programmeersessies en gemergede PR's, terwijl PR's en sessies eigenlijk slechts tussenstappen zijn. Softwareworkflows zijn grotendeels ingericht rond concrete opleveringen: issues, taken, tickets en mijlpalen.
Dus vroegen we ons af wat er zou gebeuren als we agents niet langer direct zouden aansturen en ze in plaats daarvan werk uit onze taaktracker zouden laten halen.
Dat idee werd Symphony, een geschreven spec die als supervisor functioneert om agentic werk te coördineren.
Hoe we van onze issuetracker een agent-orkestrator maakten
Symphony begon met een eenvoudig concept: elke open taak moet door een agent worden opgepakt en afgerond. In plaats van Codex-sessies in meerdere tabbladen te beheren, maakten we van onze issuetracker de control plane.
In deze opzet wordt elk open Linear-issue gekoppeld aan een specifieke agent-werkruimte. Symphony houdt het takenbord voortdurend in de gaten en zorgt ervoor dat voor elke actieve taak een agent blijft draaien totdat die is afgerond. Als een agent crasht of vastloopt, start Symphony die opnieuw. Als er nieuw werk verschijnt, pakt Symphony dat op en begint het te organiseren.
We hebben onze workflow opgebouwd rond ticketstatussen, waarbij we taakbeheerder Linear als een statusmachine gebruikten.
In de praktijk ontkoppelt Symphony werk van sessies en van pull requests. Sommige issues leveren meerdere PR's op in verschillende repo's; andere zijn puur onderzoek of analyse en raken de codebase nooit.
Zodra werk op deze manier abstract wordt gemaakt, kunnen tickets veel grotere eenheden werk vertegenwoordigen.
We gebruiken Symphony regelmatig om complexe features en infrastructuurmigraties te coördineren. We kunnen bijvoorbeeld een taak indienen waarin we de agent vragen de codebase, Slack of Notion te analyseren en een implementatieplan op te stellen. Zodra we tevreden zijn met het plan, genereert de agent een taakstructuur waarin het werk in fasen wordt opgesplitst en afhankelijkheden tussen taken worden vastgelegd.
Agents beginnen alleen aan taken die niet geblokkeerd zijn, dus de uitvoering ontvouwt zich voor deze DAG (een reeks uitvoeringsstappen) op natuurlijke en optimale wijze parallel. In het onderstaande voorbeeld markeerden we de React-upgrade als geblokkeerd door een migratie naar Vite. Zoals verwacht begonnen agents pas met het upgraden van React nadat de migratie naar Vite was voltooid.
Agents kunnen ook zelf werk creëren. Tijdens implementatie of review merken ze vaak verbeteringen op die buiten de scope van de huidige taak vallen: een performanceprobleem, een kans op refactoring of een betere architectuur. Als dat gebeurt, dienen ze simpelweg een nieuw issue in dat we later kunnen beoordelen en inplannen. Veel van deze vervolgtaken worden ook door agents opgepakt. Terwijl wij toezicht houden op dit proces, blijven agents georganiseerd en houden ze het werk in beweging.
Deze manier van werken verlaagt de mentale drempel om aan onduidelijk werk te beginnen aanzienlijk. Als de agent iets verkeerd doet, levert dat nog steeds nuttige informatie op, terwijl het ons bijna niets kost. We kunnen heel eenvoudig tickets indienen zodat de agent prototypes maakt en verschillende richtingen verkent, en alles wat niet bevalt weer verwerpen.
Omdat de orkestrator op devboxes draait en nooit slaapt, kunnen we overal taken toevoegen en weten dat een agent ze oppakt. Zo voerde een engineer in ons team drie belangrijke wijzigingen door vanuit de Linear-app op zijn telefoon, vanuit een vakantiehuisje met slechte wifi.
Meer verkenning door op deze manier te werken
Toen we de effecten van werken met Symphony zagen, was de meest opvallende verandering de output. Bij sommige teams binnen OpenAI zagen we het aantal gemergede PR's in de eerste drie weken met een factor zes toenemen. Buiten OpenAI wees Linear-oprichter Karri Saarinen op een piek in aangemaakte werkruimtes(opent in een nieuw venster) toen we Symphony uitbrachten. De grotere verschuiving zit echter in hoe teams over werk denken.
Wanneer onze engineers niet langer tijd besteden aan het begeleiden van Codex-sessies, veranderen de economische verhoudingen van codewijzigingen volledig. De kosten van elke wijziging dalen, omdat we niet langer menselijke inspanning steken in het aansturen van de implementatie zelf.
Dat veranderde ons gedrag. Het is heel eenvoudig geworden om speculatieve taken in Symphony op te starten. Probeer een idee uit, verken een refactor, test een hypothese en ga alleen verder met resultaten die veelbelovend lijken.
Het vergroot ook de groep mensen die werk kan initiëren. Onze productmanager en designer kunnen nu featureverzoeken rechtstreeks in Symphony indienen. Ze hoeven niet de repo uit te checken of een Codex-sessie te beheren. Ze beschrijven de feature en krijgen een reviewpakket terug dat een videowalkthrough bevat van de feature die in het echte product werkt.
Symphony blinkt ook uit in grote monorepo's (zoals die we bij OpenAI hebben), waar de laatste stap om een PR te mergen traag en kwetsbaar kan zijn. Het systeem bewaakt CI, rebaset wanneer nodig, lost conflicten op, probeert flaky checks opnieuw en loodst wijzigingen door de pijplijn. Tegen de tijd dat een ticket Merging bereikt, hebben we veel vertrouwen dat de wijziging zonder handmatig ingrijpen in de hoofdbranch terechtkomt.
Vooruitgang brengt nieuwe problemen met zich mee
Werken op dit niveau brengt trade-offs met zich mee. Toen we overstapten van het interactief aansturen van agents naar het toewijzen van werk op ticketniveau, verloren we de mogelijkheid om ze onderweg voortdurend bij te sturen en waar nodig van koers te laten veranderen. Soms leverde de agent iets op dat de plank volledig missloeg. Dat was nuttig: zulke missers brachten zwakke plekken in het systeem aan het licht en hielpen ons het robuuster te maken.
In plaats van het resultaat handmatig te patchen, voegden we guardrails en skills toe zodat de agents de volgende keer beter werk konden leveren. Na verloop van tijd leidde dit ertoe dat we nieuwe mogelijkheden aan onze harness toevoegden, zoals end-to-endtests uitvoeren, de app aansturen via Chrome DevTools en QA-smoketests beheren. We verbeterden onze documentatie aanzienlijk en maakten duidelijker wat we onder goed werk verstaan.
Niet elke taak past bij hoe Symphony werkt. Sommige problemen vereisen nog steeds softwareontwikkelaars die rechtstreeks werken met interactieve Codex-sessies, vooral ambigue problemen of werk dat sterk oordeel en expertise vereist. In de praktijk zijn dit meestal de interessantste en prettigste taken voor onze engineers om tijd aan te besteden.
Het verschil is dat Symphony het grootste deel van routinematig implementatiewerk aankan. Daardoor kunnen engineers zich op één moeilijk probleem tegelijk richten in plaats van voortdurend te schakelen tussen kleinere taken.
We leerden ook dat het niet goed werkt om agents te behandelen als rigide stappen in een statusmachine. Modellen worden slimmer en kunnen grotere problemen oplossen dan de kaders die wij ze opleggen. In de vroege versies maakten alle GitHub-integraties bijvoorbeeld deel uit van de harness eromheen: Codex werd alleen ingezet om codewijzigingen door te voeren, terwijl de rest van het proces, zoals wijzigingen indienen en tests uitvoeren, in code was vastgelegd. In die vroege vorm van agent-ondersteund werk vroegen we Codex dus alleen om de taak te implementeren. Die aanpak bleek te beperkend. Codex kan prima meerdere PR's aanmaken, reviewfeedback lezen en verwerken. Daarom gaven we het meer tools: de gh-CLI, skills om CI-logs te lezen, enzovoort. Nu kunnen we Codex ook vragen om meer te doen, zoals oude PR's sluiten of rapporten ophalen over afgerond versus stopgezet werk. Zulke taken vallen ver buiten de oorspronkelijke focus op feature-implementatie.
Daarom zijn we uiteindelijk overgestapt op het geven van doelstellingen aan agents in plaats van strikte overgangen, net zoals een goede manager een doel toewijst aan iemand in zijn team. De kracht van modellen komt voort uit hun vermogen tot redenering, dus geef ze tools en context en laat ze hun werk doen.
Symphony gebruiken om Symphony te bouwen
Wanneer je de Symphony-repository opent, is het eerste wat je opvalt dat Symphony technisch gezien gewoon een SPEC.md-bestand is: een definitie van het probleem en de beoogde oplossing. In plaats van een complex supervisiesysteem te bouwen, definieerden we het probleem en de beoogde oplossingen en gaven we agents sturing op hoog niveau.
De referentie-implementatie is geschreven in Elixir, want als code in feite gratis is, kun je eindelijk talen kiezen om waar ze goed in zijn, zoals Elixirs sterke ondersteuning voor concurrency. Maar het kernidee past in een eenvoudig Markdown-document. We moedigen je aan om je favoriete programmeeragent op de spec los te laten en een eigen versie te laten implementeren.
De eerste versie van Symphony was gewoon een Codex-sessie die in tmux draaide, Linear voortdurend monitorde en subagents startte voor nieuwe taken. Het werkte, maar was niet bijzonder betrouwbaar. De tweede versie draaide binnen onze hoofdrepo, die vanaf het begin was ingericht voor agents. We hadden de agent-harness al gebouwd om agents de juiste skills en context te geven om in deze repo kwalitatief goed werk te leveren, dus Symphony bracht alles samen.
Toen de basisfunctionaliteit er eenmaal was, gebruikten we Symphony om Symphony te bouwen.
Toen we intern demonstreerden hoe het systeem taken beheerde en de bijbehorende proof-of-work-video toevoegde, was de reactie overweldigend positief: ons Symphony-projectkanaal groeide en teams in de hele organisatie begonnen het spontaan te gebruiken. Sterke interne adoptie is bij OpenAI een voorwaarde om extern te lanceren. Op basis van het gebruik dat we binnen OpenAI zagen, werd duidelijk dat we Symphony ook buiten het bedrijf moesten delen.
Daarom werkten we het idee uit tot een zelfstandige SPEC.md en vroegen we Codex om het te implementeren. Voor de referentie-implementatie kozen we Elixir, een relatief niche programmeertaal met uitstekende bouwstenen voor het orkestreren en aansturen van gelijktijdige processen. Codex bouwde de Elixir-implementatie in één keer en daarna bleven we zowel de spec als de implementatie verder verfijnen. Om de spec nog scherper te krijgen, vroegen we Codex zelfs om die ook in verschillende andere talen te implementeren: TypeScript, Go, Rust, Java en Python. Die resultaten gebruikten we om onduidelijkheden op te sporen en het systeem te vereenvoudigen. Dat lukte in elke taal.
Tijdens het bouwen van Codex hebben we veel onnodige complexiteit verwijderd, zoals afhankelijkheden van specifieke repositories of van Linear MCP. Symphony is niet langer afhankelijk van onze interne repositories of workflows. De kern van de aanpak werd eenvoudig:
Garandeer voor elke open taak dat er een agent draait in zijn eigen werkruimte.
Naast ondersteuning bij het actieve werk is de ontwikkelworkflow nu ook iets wat agents kennen en volgen. Die workflow, zoals werken aan een issue, een repo checken, die op In progress zetten zodat de PM weet dat eraan gewerkt wordt, de PR toevoegen, die naar de status Review verplaatsen en video's toevoegen, is nu vastgelegd in een eenvoudig WORKFLOW.md-bestand. Dit hele proces werd al door mensen gevolgd, maar was nooit gedocumenteerd. In plaats van te vertrouwen op deze impliciete reeks stappen, leggen we die nu vast en zorgt Symphony ervoor dat agents ze volgen. Zo kunnen we agents bouwen die met ons samenwerken. Als we besluiten dat agents ook zelfreflectie aan afgerond werk moeten toevoegen, voegen we dat toe aan de WORKFLOW.md, en Symphony zorgt ervoor dat agents ook die stap volgen.
We konden Codex ook gebruiken in app server mode(opent in een nieuw venster), een ingebouwde headless modus voor Codex. Deze modus stelde ons in staat Codex te draaien en er programmatisch mee te communiceren via een goed gedocumenteerde JSON-RPC API voor dingen zoals het starten van een thread of reageren op turns. Het is een veel handigere en schaalbaardere manier dan proberen met Codex te interageren via de CLI of live tmux-sessies.
Codex App Server paste perfect bij onze use case: we profiteren van de harness die Codex biedt en hebben tegelijk de flexibiliteit en integratiepunten die we nodig hebben. Om bijvoorbeeld te voorkomen dat we het Linear-access-token blootstellen aan subagents, gebruiken we dynamic tool calls(opent in een nieuw venster) om de ruwe functie linear_graphql beschikbaar te maken. Daarmee kunnen willekeurige verzoeken aan Linear worden uitgevoerd, zonder afhankelijk te zijn van MCP of het access-token bloot te stellen aan containers.
Wat hierna volgt
Symphony is een bewust minimale orkestratielaag. We maken het open source om te laten zien wat Codex App Server kan wanneer het wordt gecombineerd met verschillende workflowtools, zoals Linear. Daarom zijn we niet van plan Symphony als zelfstandig product te onderhouden. Zie het als een referentie-implementatie. Net zoals veel developers hun coding agents de post over harness engineering gaven om hun repositories op te zetten, hopen we dat jij je favoriete coding agent de Symphony-spec(opent in een nieuw venster) en repository(opent in een nieuw venster) geeft om je eigen versie te bouwen, afgestemd op jouw omgeving.
De kracht komt van Codex en de app server. Symphony was een manier om Codex met Linear te verbinden, twee tools die we al gebruikten, om het probleem van werkbeheer op te lossen. Naarmate programmeeragents beter worden in redeneren en het volgen van instructies, vermoeden we dat de bottleneck bij andere bedrijven ook verschuift van code schrijven naar het beheren van agent-ondersteund werk. Het mooie is dat de drempel om met dit soort programmeeragentsystemen te experimenteren nu verrassend laag is. Met Codex kun je eenvoudig zelf aan de slag.
Shout-outs aan de community
We zijn verheugd te zien dat de engineeringcommunity Symphony gebruikt in de weken sinds de release, met meer dan 15K GitHub-sterren(opent in een nieuw venster) op 23 april.