Open-source spec para sa Codex orchestration: Symphony
Nina Alex Kotliarskyi, Victor Zhu, at Zach Brock
Six months ago, habang ginagawa ang isang internal productivity tool, gumawa ang team namin ng controversial (noong panahong iyon) na desisyon: ibi-build namin ang repo namin nang walang human-written code. Codex ang dapat mag-generate sa bawat linya sa project repository namin.
Para mangyari iyon, ni-redesign namin ang engineering workflow namin mula sa pinakapundasyon pataas. Nag-build kami ng isang agent-friendly na repository, nag-invest nang husto sa mga automated test at guardrail, at itinuring na full-fledged teammate ang Codex. Dinocument namin ang journey na iyon sa nauna naming blog post tungkol sa harness engineering.
At nangyari nga iyon, pero nagka-bottleneck na naman kami: context switching.
Para malutas ang bagong problemang ito, nag-build kami ng isang system na tinatawag na Symphony. Ang Symphony(magbubukas sa bagong window) ay isang agent orchestrator. Ginagawa nitong control plane ang isang project-management board, gaya ng Linear, para sa mga coding agent. Bawat nakabukas na task ay nagkakaroon ng agent, tuloy-tuloy na gumagana ang mga agent, at nire-review naman ng mga tao ang mga resulta.
Ipinapaliwanag ng post na ito kung paano namin ginawa ang Symphony—na nagbunga ng 500% na pagtaas sa mga landed pull request sa ilang team—at kung paano ito gagamitin para gawing isang always-on na agent orchestrator ang sarili mong issue tracker.
Limit ng mga interactive coding agent
Kahit nagiging madali nang gamitin ang mga coding agent—ina-access man sa web apps o sa CLI—mga interactive tool pa rin ang mga ito.
Habang mas dumarami ang trabaho ng agent sa OpenAI, nakakakita kami ng bagong uri ng problema. Bawat engineer ay nagbubukas ng ilang Codex session, nag-a-assign ng mga task, nire-review ang output, ini-steer ang agent, at inuulit ito. Kadalasan na, tatlo hanggang limang session ang sabay-sabay na mama-manage ng mga tao bago sila magkaproblema sa context switching. Kapag lumampas na roon, bumababa na ang productivity. Nalilimutan na namin kung ano nga ba ang ginagawa ng mga session na iyon, nagpapalipat-lipat kami sa mga terminal para maibalik sa track ang mga agent, at dini-debug ang mga task na matagal nang gumagana tapos biglang tumigil sa kalagitnaan.
Mabibilis ang agent, pero nagka-system bottleneck kami: human attention. Nakabuo kami ng team ng mga junior engineer na talagang napaka-capable, tapos ini-assign namin ang mga human engineer namin para i-micromanage ang mga agent. Pero hindi magiging efficient iyon.
Binago ang perspective
Naisip namin na mali ang ino-optimize namin. Ino-orient namin ang system naman sa mga coding session at mini-merge ang mga PR, samantalang ang mga PR at session ay part lang ng process at hindi ng outcome. Malaking bahagi ng mga software workflow ay naka-organize sa mga deliverable: issues, tasks, tickets, milestones.
Kaya tinanong namin ang sarili namin kung ano ang mangyayari kapag itinigil namin ang direktang pag-supervise sa mga agent at hayaan naming sila ang humugot ng trabaho mula sa task tracker namin.
Ang idea na iyon ang naging Symphony, isang written spec na nagfa-function bilang supervisor para i-orchestrate ang trabaho ng agent.
Ginawang agent orchestrator ang issue tracker namin
Nag-umpisa ang Symphony sa isang simpleng concept: ang kahit anong task na nakabukas ay dapat makuha at matapos ng isang agent. Sa halip na i-manage ang mga Codex session sa maraming tab, ginawa naming control plane ang issue tracker namin.
Sa setup na ito, ang bawat nakabukas na Linear issue ay inia-assign sa isang dedicated na agent workspace. Tuloy-tuloy na binabantayan ng Symphony ang task board at tinitiyak na ang bawat active na task ay may agent na gumagana sa loop hanggang sa matapos ito. Kapag nag-crash o tumigil sa pag-respond ang agent, nire-restart ito ng Symphony. Kapag may lumitaw na bagong trabaho, kinukuha ito ng Symphony at sinisimulang i-organize ang trabaho.
Binuo namin ang workflow namin batay sa mga status ng ticket, gamit ang task manager Linear bilang state machine.
Inihihiwalay ng Symphony ang task mula sa mga session at mga pull request. Ang ilang issue ay nagpo-produce ng maraming PR sa iba’t ibang repo; ang iba naman ay pure investigation o analysis nang hindi ginagalaw ang codebase.
Kapag na-abstract na ang trabaho sa ganitong paraan, puwede nang mag-represent sa mas malalaking unit ng trabaho ang mga ticket.
Regular naming ginagamit ang Symphony para mag-orchestrate ng mga complex feature at infrastructure migration. Halimbawa, puwedeng mag-file kami ng task na nagre-request sa agent na i-analyze ang codebase, Slack, o Notion at gumawa ng implementation plan. Kapag okay na para sa amin ang plan, magge-generate ang agent ng structure ng task, hahati-hatiin nito sa ilang stage ang trabaho at ide-define ang mga dependency sa pagitan ng mga task.
Sa mga task lang na hindi naka-block nagsisimulang magtrabaho ang mga agent, kaya natural na nangyayari ang execution, efficient, at sabay-sabay para sa DAG na ito (isang series ng mga execution step). Sa halimbawa sa ibaba, minarkahan naming na-block sa migration papuntang Vite ang upgrade ng React. Gaya ng inaasahan, React lang ang sinimulang i-upgrade ng mga agent pagkatapos ng migration sa Vite.
Nakakagawa rin ng trabaho ang mga agent. Sa panahon ng implementation o review, madalas may napapansin silang improvements na wala sa scope ng kasalukuyang task: issue sa performance, refactoring opportunity, o mas mahusay na architecture. Kapag nangyayari iyon, nagfa-file lang sila ng bagong issue na mai-evaluate at mai-schedule namin sa ibang pagkakataon—marami sa mga follow-up task na ito ang nakukuha na rin ng mga agent. Habang ino-oversee namin ang process na ito, nananatiling organize ang mga agent at patuloy na ginagawa ang trabaho.
Malaki ang naitutulong ng ganitong paraan ng pagtatrabaho para mabawasan ang mental effort sa mga ambigous na trabaho. Kapag nagkakamali ang agent, nakakatulong pa rin ang impormasyong iyon, at halos wala kaming gastos dito. Napakamura para sa amin na mag-file ng mga ticket para mag-prototype at mag-explore ang agent, at itapon ang mga exploration na hindi namin gusto.
Dahil sa mga devbox gumagana ang orchestrator at tuloy-tuloy ito, makakapagdagdag kami ng mga task kahit nasaan kami at alam naming may agent na kukuha nito. Halimbawa, isang engineer sa team namin ang may ginawang tatlong mahahalagang changes mula sa Linear app gamit ang phone niya habang nasa isang magandang cabin at mahina ang wifi.
Dumami ang exploration dahil sa ganitong pagtatrabaho
Nang inoobserbahan ang mga epekto ng pagtatrabaho gamit ang Symphony, output ang pinakakitang-kitang pagbabago. Sa ilang team sa OpenAI, nakita naming tumaas nang 6X ang bilang ng mga landed PR sa unang tatlong linggo. Sa labas ng OpenAI, ini-highlight ng founder ng Linear na si Karri Saarinen ang biglang pagdami ng mga nagawang workspace(magbubukas sa bagong window) nang i-release namin ang Symphony. Pero, ang mas malalim na pagbabago ay ang naging pananaw ng mga team sa trabaho.
Noong hindi na gumugugol ng oras sa pag-supervise sa mga session ng Codex ang mga engineer namin, ang laki rin ng pinagbago ng gastos sa code. Bumaba ang iniisip na halaga ng bawat pagbabago dahil hindi na kami nag-i-invest sa human effort para sa mismong implementation.
Binago niyan ang behavior namin. Napakadali na lang magsimula ng mga speculative o trial task sa Symphony. I-try ang isang idea, mag-explore ng refactor, mag-test ng hypothesis, at panatilihin ang mga parang promising na resulta lang.
Dahil din dito, lumawak kung sino ang puwedeng mag-initiate ng task. Ang product manager at designer namin ay direkta nang nakakapag-file ngayon ng mga feature request sa Symphony. Hindi na nila kailangang mag-check out mula sa repo o mag-manage ng Codex session. Idini-discribe lang nila ang feature at nakakakuha na sila ng review packet na may video walkthrough ng feature na gumagana sa tunay na product.
Effective din ang Symphony sa malalaking monorepo (gaya ng mayroon kami sa OpenAI) kung saan mabagal at hindi stable ang final step ng pag-merge ng PR. Binabantayan ng system ang CI, nagre-rebase kapag kailangan, nagre-resolve ng mga conflict, nire-retry ang mga flaky check, at sa pangkalahatan, gina-guide ang changes sa pipeline. Kapag nakarating na ang ticket sa Merging, mataas na ang kumpiyansa naming makakarating ang change sa main branch nang hindi kailangang i-supervise ng tao.
Kasabay ng pag-unlad, lumitaw ang bago at iba't ibang problema
May kapalit ang pag-o-operate sa ganitong level. Nang mula sa interactive na pag-guide sa mga agent lumipat kami sa pag-a-assign sa kanila ng trabaho sa ticket level, hindi na namin sila ma-adjust habang nagtatrabaho sila at hindi na namin maitama ang direksiyon nila kapag kailangan. Minsan, talagang mali ang nagagawa ng agent. Nakakatulong iyon, naipapakita kasi ng mga kamaliang iyon ang mga gap sa system kaya natutulungan kami na mas lalong ma-improve iyon.
Sa halip na mano-manong ayusin ang resulta, nagdagdag kami ng guardrails at skills para magtagumpay ang mga agent sa susunod. Sa paglipas ng panahon, may mga idinagdag kaming bagong capability sa harness namin, gaya ng pag-run ng end-to-end test, pag-drive sa app gamit ang Chrome DevTools, at pag-manage sa mga QA smoke test. In-improve namin nang husto ang documentation namin at nilinaw kung ano talaga ang ibig sabihin ng "good."
Hindi lahat ng task ay puwede sa Symphony. May ilang problema na kailangan pa ring direktang trabahuhin ng mga engineer sa mga interactive na session ng Codex, partikular na ang mga ambiguous na problema o trabaho na nangangailangan ng mahusay na judgment at expertise. Kadalasan na, ang mga task na ito ang pinaka-interesting at pinakanakaka-enjoy gawin para sa mga engineer namin.
Ang kaibahan lang, kaya nang i-handle ng Symphony ang karamihan sa regular na implementation work. Dahil diyan, nakakapagpokus na ang mga engineer sa isang mahirap na problema imbes na madalas na nagpapalipat-lipat sa maliliit na task.
Natutuhan din namin na hindi effective na ituring na rigid na mga node sa isang state machine ang mga agent. Patalino na nang patalino ang mga model at kaya na nilang lumutas ng mas malalaking problema kumpara sa orihinal na pagkakadisenyo namin sa kanila. Halimbawa, sa mga unang version, lahat ng GitHub integration ay kasama sa outer harness—halimbawa, sa mga unang version, changes lang sa code ang inaasahang gagawin ng Codex, at ang lahat ng iba pang process (pagsa-submit ng changes, pagra-run ng mga test) ay ide-define sa code. Sa unang mga version ng task ng agent, ipinapa-implement lang sa Codex ang task. Masyado nitong nililimitahan ang Codex. Kayang-kaya ng Codex na gumawa ng maraming PR at magbasa ng review feedback at i-address ang mga iyon. Kaya binigyan namin ito ng mga tool—gh CLI, mga skill para magbasa ng mga CI log, at iba pa—at ngayon marami na tayong puwedeng ipagawa sa Codex gaya ng isara ang mga lumang PR o humila ng mga report tungkol sa mga completed vs. abandoned na trabaho. Hindi kasama ang mga task na ito sa initial na feature implementation.
Kaya unti-unti naming sinimulang bigyan ng mga objective ang mga agent imbes na magsagawa ng mahihigpit na transition, gaya ito ng isang magaling na manager na nag-a-assign ng goal sa isang direct report sa team niya. Ang kalakasan ng mga model ay mula sa kapasidad nila na mangatwiran, kaya bigyan mo sila ng mga tool at context at hayaan mo silang magtrabaho.
Ginamit ang Symphony para i-build ang Symphony
Kapag binuksan mo ang repository ng Symphony, mapapansin mo agad na technically, ang Symphony ay SPEC.md file lang—isang definition ng problema at ng intended solution. Sa halip na mag-build ng kumplikadong supervision system, ini-specify namin ang problema at ang mga intended solution, kaya mas malawak na guidance ang naibibigay namin sa mga agent.
Ang reference implementation ay nakasulat sa Elixir—dahil kapag halos libre na ang code, puwede ka nang pumili ng mga (programming) language batay sa lakas ng mga ito, gaya ng concurrency ng Elixir—pero ang pangunahing idea ay maihaharap sa isang simpleng Markdown document. Ini-encourage ka naming i-guide ang paborito mong coding agent sa spec at ipa-implement dito ang sarili nitong version.
Ang unang version ng Symphony ay isang Codex session lang na gumagana sa tmux, regular nitong chini-check ang Linear at gumagawa ng mga sub-agent para sa mga bagong task. Gumagana naman ito, pero hindi ito gaanong reliable. Ang ikalawang version ay nasa main project repository namin, na ginawa para sa mga agent. Na-build na namin ang agent harness para maibigay sa mga agent ang mga skill at context para makagawa sila ng de-kalidad na trabaho sa repo na ito, kaya ikino-connect na lang ng Symphony ang lahat ng iyon.
Nang existing na ang basic functionality nito, ginamit namin ang Symphony para i-build ang Symphony.
Nang i-demo namin sa loob ng kumpanya ang mga system managing task at in-attach ang proof-of-work video nito, napaka-positive nang naging reaction: lumaki ang Symphony project channel namin, at inumpisahan itong gamitin ng mga team sa buong organization. Mahalagang mapatunayan na fit ang product internally bago ito mai-launch sa labas ng OpenAI. Batay sa nakita naming usage sa OpenAI, naging malinaw na dapat naming i-share sa labas ng kumpanya ang Symphony.
Kaya in-extract namin ang idea at ginawang standalone na SPEC.md, tapos ipina-implement namin ito sa Codex. Para sa reference implementation, Elixir ang pinili namin, isang specialized na (programming) language na may mahusay na built-in support para sa pag-orchestrate at pag-supervise ng concurrent o sabay-sabay na process. Nagawang i-build ng Codex ang Elixir implementation nang one shot, at mula roon, patuloy na naming ini-improve ang spec at implementation. Para ma-polish ang spec, ipina-implement pa nga namin ito sa Codex sa iba pang programming language—TypeScript, Go, Rust, Java, Python—at ginamit ang mga resulta para i-identify ang ambiguities at i-simplify ang system. Nagtagumpay ito sa bawat programming language.
Sa buong proseso ng pag-build sa Codex, marami kaming inalis na di-kailangang complexity, gaya ng dependencies sa mga specific repository o Linear MCP. Hindi na nakadepende ang Symphony sa mga internal repository o workflow namin. Naging simple na ang core approach:
Sa bawat task na nakabukas, makakasigurong may agent na gumagana sa sarili nitong workspace.
Bukod sa tumutulong sa active na task, alam at sinusunod na ngayon ng agent ang development workflow. Ang development workflow—ayusin ang isang issue, mag-check out sa repo, gawing in progress ito para malaman ng PM na ginagawa na ito, idagdag ang PR, ilipat ito sa Review staus, mag-attach ng mga video, at iba pa—ay nasa isang simpleng WORKFLOW.md file na ngayon. Lahat ng ito ay proseso na sinusunod ng mga tao, pero hindi nai-document kahit kailan. Imbes na mag-rely sa di-nakasulat na set na ito ng mga step, idinocument namin ito, at sinisiguro ng Symphony na sinusunod ito ng mga agent. Dahil dito, nakapag-build kami ng mga agent na nakakatrabaho namin. Kung mapagdesisyunan namin na dapat mag-attach din ang mga agent ng self-reflection para matapos ang trabaho, idaragdag namin iyon sa WORKFLOW.md, at iga-guide ng Symphony ang mga agent sa step na iyon.
Nagagamit din namin ang Codex sa app server mode(magbubukas sa bagong window), isang built-in na headless mode para sa Codex. Sa mode na ito, nagagawa naming i-run ang Codex at makipag-usap dito gamit ang isang well documented na JSON-RPC API para sa mga action na gaya ng umpisahan ang isang thread o mag-react sa mga turn. Mas madali at mas flexible na paraan ito kaysa sa subukang makipag-interact sa Codex gamit ang CLI o live na mga tmux session.
Bagay na bagay ang Codex App Server sa use case namin: sinasamantala namin ang harness o tool na inilalaan ng Codex, habang may mga paraan din kami para mai-adjust at mai-connect ang mga bagay-bagay. Halimbawa, para hindi ma-expose ang Linear access token sa mga subagent, gumagamit kami ng dynamic tool calls(magbubukas sa bagong window) para i-expose ang raw na linear_graphql function na nag-e-execute ng mga arbitrary request sa Linear, nang hindi nagre-rely sa MCP o ini-expose ang access token sa mga container.
Ano'ng susunod
Ang Symphony ay sinadyang gawing isang simpleng orchestration layer. Ginawa namin itong open source para maipakita ang kapasididad ng Codex App Server kapag ginamit kasama ng iba’t ibang workflow tool, gaya ng Linear. Dahil dito, wala kaming planong panatilihing standalone product ang Symphony. Ituring ito na isang reference implementation. Katulad ito ng ginagawa ng maraming developer na igina-guide ang mga coding agent nila sa harness engineering para i-structure ang mga repository nila, sana i-guide mo rin ang paborito mong coding agent sa spec(magbubukas sa bagong window) at repository(magbubukas sa bagong window) ng Symphony para makapag-build ka ng sarili mong mga version na ibinagay sa mga environment mo.
Sa Codex at sa app server nito nagmumula ang power. Ang Symphony ang isang paraan para mai-connect ang Codex sa Linear, dalawang bagay na ginagamit na namin, para malutas ang problema sa pag-manage sa trabaho. Habang lalong gumagaling ang mga coding agent sa reasoning o pangangatwiran at pagsunod sa mga instruction, hinala namin ang magiging bottleneck rin sa ibang kumpanya ay ang pag-shift mula pagsusulat ng code papunta sa pag-manage sa trabaho ng agent. Ang nakaka-excite na part, kataka-takang mababa ang mga barrier o hadlang ngayon sa pag-e-experiment sa mga coding agent system na ito. Puwedeng-puwede ka nang mag-build gamit ang Codex.
Mga community shoutout
Ang saya-saya namin nang makita naming ginamit ng engineering community ang Symphony noong sumunod na mga linggo mula nang i-release ito, nakakuha ito ng mahigit 15K GitHub stars(magbubukas sa bagong window) hanggang noong Abril 23.