مرکزی مواد پر جائیں
OpenAI

۲۷ اپریل، ۲۰۲۶

انجینئرنگ

Codex آرکیسٹریشن کے لیے اوپن سورس اسپیک: Symphony

از Alex Kotliarskyi، Victor Zhu، اور Zach Brock

لوڈ ہو رہا ہے…

چھ ماہ قبل، جب ہم ایک اندرونی پیداواری ٹول پر کام کر رہے تھے، ہماری ٹیم نے ایک متنازعہ فیصلہ کیا: ہم اپنی ریپوزٹری کو بغیر کسی انسان کے لکھے گئے کوڈ کے بنائیں گے۔ ہمارے پروجیکٹ ریپوزٹری کی ہر لائن Codex کے ذریعے تیار کی جانی تھی۔

اسے ممکن بنانے کے لیے ہم نے اپنے انجینئرنگ ورک فلو کو بنیادی سطح سے دوبارہ ڈیزائن کیا۔ ہم نے ایک ایجنٹ کے لیے موزوں ریپوزٹری تیار کی، خودکار ٹیسٹس اور گارڈ ریلز میں بھرپور سرمایہ کاری کی، اور Codex کو ایک مکمل ٹیم ممبر کے طور پر لیا۔ ہم نے اس سفر کو اپنی پچھلی ہارنس انجینئرنگ پر بلاگ پوسٹ میں دستاویز بند کیا۔

اور یہ کام کر گیا، لیکن پھر ہمیں اگلی رکاوٹ کا سامنا ہوا: کانٹیکسٹ سوئچنگ۔

اس نئے مسئلے کو حل کرنے کے لیے، ہم نے Symphony نامی ایک سسٹم بنایا ہے۔ Symphony(نئی ونڈو میں کھلتا ہے) ایک ایجنٹ آرکیسٹریٹر ہے جو Linear جیسے پروجیکٹ مینجمنٹ بورڈ کو کوڈنگ ایجنٹس کے لیے کنٹرول پلین میں تبدیل کرتا ہے۔ ہر کھلے ٹاسک کے لیے ایک ایجنٹ مقرر ہوتا ہے، ایجنٹس مسلسل کام کرتے ہیں، اور انسان نتائج کا جائزہ لیتے ہیں۔

یہ پوسٹ بتاتی ہے کہ ہم نے Symphony کیسے تیار کیا—جس کے نتیجے میں کچھ ٹیموں میں مرج ہونے والی پل ریکویسٹس میں 500% اضافہ ہوا—اور یہ بھی کہ آپ اسے استعمال کر کے اپنے ایشو ٹریکر کو ایک ہمیشہ فعال ایجنٹ آرکیسٹریٹر میں کیسے تبدیل کر سکتے ہیں۔

انٹرایکٹو کوڈنگ ایجنٹس کی حد

اگرچہ ان کا استعمال آسان ہوتا جا رہا ہے، کوڈنگ ایجنٹس—چاہے وہ ویب ایپس کے ذریعے استعمال ہوں یا CLI کے ذریعے—اب بھی انٹرایکٹو ٹولز ہی ہیں۔

جیسے جیسے OpenAI میں ایجنٹک کام کا پیمانہ بڑھا، ہمیں ایک نئی قسم کا بوجھ محسوس ہوا۔ ہر انجینئر چند Codex سیشنز کھولتا، ٹاسکس اسائن کرتا، آؤٹ پٹ کا جائزہ لیتا، ایجنٹ کی رہنمائی کرتا، اور یہ عمل دہراتا۔ عملی طور پر زیادہ تر لوگ ایک وقت میں تین سے پانچ سیشنز کو آرام سے مینج کر سکتے تھے، اس کے بعد کانٹیکسٹ سوئچنگ مشکل ہو جاتی تھی۔ اس سے آگے پروڈکٹیوٹی کم ہونے لگتی تھی۔ ہم بھول جاتے تھے کہ کون سا سیشن کیا کر رہا ہے، ایجنٹس کو درست راستے پر لانے کے لیے مختلف ٹرمینلز کے درمیان آنا جانا کرتے، اور طویل چلنے والے ٹاسکس کو ڈی بگ کرتے جو درمیان میں رک جاتے تھے۔

ایجنٹس تیز تھے، لیکن ہمارے سسٹم میں ایک رکاوٹ تھی: انسانی توجہ۔ ہم نے دراصل نہایت قابل جونیئر انجینئرز کی ایک ٹیم بنا لی تھی، اور پھر اپنے انسانی انجینئرز کو ان کی باریک بینی سے نگرانی کرنے پر لگا دیا تھا۔ یہ طریقہ قابلِ توسیع نہیں تھا۔

نقطۂ نظر میں تبدیلی

ہمیں احساس ہوا کہ ہم غلط چیز کو بہتر بنانے کی کوشش کر رہے تھے۔ ہم اپنے سسٹم کو کوڈنگ سیشنز اور مرج ہونے والی PRs کے گرد ترتیب دے رہے تھے، حالانکہ PRs اور سیشنز دراصل ایک مقصد کے حصول کا ذریعہ ہیں۔ سافٹ ویئر ورک فلوز زیادہ تر ڈیلیور ایبلز کے گرد منظم ہوتے ہیں: ایشوز، ٹاسکس، ٹکٹس، مائل اسٹونز۔

لہٰذا ہم نے خود سے سوال کیا کہ کیا ہوگا اگر ہم ایجنٹس کی براہِ راست نگرانی کرنا چھوڑ دیں اور اس کے بجائے انہیں اپنے ٹاسک ٹریکر سے کام لینے دیں۔

یہی خیال Symphony بن گیا، ایک تحریری وضاحت جو ایجنٹک کام کو منظم کرنے کے لیے ایک نگران کے طور پر کام کرتی ہے۔

اپنے ایشو ٹریکر کو ایک ایجنٹ آرکیسٹریٹر میں تبدیل کرنا

Symphony کی شروعات ایک سادہ تصور سے ہوئی: ہر کھلا ٹاسک ایک ایجنٹ کے ذریعے اٹھایا جائے اور مکمل کیا جائے۔ متعدد ٹیبز میں Codex سیشنز کو منیج کرنے کے بجائے، ہم نے اپنے ایشو ٹریکر کو کنٹرول پلین بنا دیا۔

اس سیٹ اپ میں ہر اوپن Linear ایشو ایک مخصوص ایجنٹ ورک اسپیس سے منسلک ہوتا ہے۔ Symphony مسلسل ٹاسک بورڈ پر نظر رکھتا ہے اور یہ یقینی بناتا ہے کہ ہر ایکٹیو ٹاسک کے لیے ایک ایجنٹ اس وقت تک مسلسل کام کرتا رہے جب تک وہ مکمل نہ ہو جائے۔ اگر کوئی ایجنٹ کریش ہو جائے یا رک جائے تو Symphony اسے ری اسٹارٹ کر دیتا ہے۔ اگر نیا کام سامنے آتا ہے تو Symphony اسے اٹھاتا ہے اور کام کو منظم کرنا شروع کر دیتا ہے۔

ہم نے اپنا ورک فلو ٹکٹ اسٹیٹس کی بنیاد پر تیار کیا، اور ٹاسک مینیجر Linear کو ایک اسٹیٹ مشین کے طور پر استعمال کیا۔

کوڈنگ ایجنٹس ہمارے ساتھ کام کرنے کے لیے Linear اسٹیٹسز کو ایک اسٹیٹ مشین کے طور پر استعمال کرتے ہیں۔

عملی طور پر، Symphony کام کو سیشنز اور pull request سے الگ کر دیتا ہے۔ کچھ ایشوز مختلف ریپوز میں متعدد PRs پیدا کرتے ہیں، جبکہ کچھ صرف تحقیق یا تجزیے پر مبنی ہوتے ہیں اور کبھی بھی کوڈ بیس کو نہیں چھیڑتے۔

جب کام کو اس انداز میں مجرد بنایا جاتا ہے، تو ٹکٹس زیادہ بڑے نوعیت کے کام کی نمائندگی کر سکتے ہیں۔

ہم باقاعدگی سے Symphony کو پیچیدہ فیچرز اور انفراسٹرکچر مائیگریشنز کو منظم کرنے کے لیے استعمال کرتے ہیں۔ مثال کے طور پر، ہم ایک ٹاسک بنا سکتے ہیں جس میں ایجنٹ سے کہا جائے کہ وہ کوڈ بیس، Slack، یا Notion کا تجزیہ کرے اور ایک نفاذی منصوبہ تیار کرے۔ جب ہم اس منصوبے سے مطمئن ہو جاتے ہیں، تو ایجنٹ ٹاسکس کا ایک شجرہ تیار کرتا ہے، کام کو مراحل میں تقسیم کرتا ہے اور ٹاسکس کے درمیان انحصارات کی وضاحت کرتا ہے۔

ایجنٹس صرف اُن ٹاسکس پر کام شروع کرتے ہیں جو بلاک نہیں ہوتے، اس لیے اس DAG میں عمل درآمد قدرتی طور پر اور مؤثر انداز میں متوازی طور پر آگے بڑھتا ہے۔ نیچے دی گئی مثال میں، ہم نے React اپگریڈ کو Vite کی مائیگریشن پر منحصر قرار دیا تھا۔ توقع کے مطابق، ایجنٹس نے React کو اپگریڈ کرنا اسی وقت شروع کیا جب Vite کی مائیگریشن مکمل ہو گئی۔

ایجنٹس خود بھی کام تخلیق کر سکتے ہیں۔ نفاذ یا جائزے کے دوران، وہ اکثر ایسی بہتریوں کی نشاندہی کرتے ہیں جو موجودہ ٹاسک کے دائرے سے باہر ہوتی ہیں: کارکردگی کا مسئلہ، ریفیکٹرنگ کا موقع، یا بہتر آرکیٹیکچر۔ جب ایسا ہوتا ہے، تو وہ ایک نیا ایشو بنا دیتے ہیں جسے ہم بعد میں جانچ اور شیڈول کر سکتے ہیں—ان میں سے بہت سے فالو اپ ٹاسکس بھی ایجنٹس ہی اٹھا لیتے ہیں۔ جبکہ ہم اس عمل کی نگرانی کرتے ہیں، ایجنٹس منظم رہتے ہیں اور کام کو آگے بڑھاتے رہتے ہیں۔

اس طریقۂ کار سے غیر واضح کام شروع کرنے کی ذہنی لاگت نمایاں طور پر کم ہو جاتی ہے۔ اگر ایجنٹ کچھ غلط بھی کرے، تو وہ بھی مفید معلومات فراہم کرتا ہے، اور ہمارے لیے اس کی لاگت تقریباً صفر ہوتی ہے۔ ہم بہت کم خرچ پر ٹکٹس بنا سکتے ہیں تاکہ ایجنٹ تجربہ کرے اور دریافت کرے، اور جو تجربات ہمیں پسند نہ آئیں انہیں آسانی سے رد کر سکتے ہیں۔

چونکہ آرکیسٹریٹر devboxes پر چلتا ہے اور کبھی بند نہیں ہوتا، اس لیے ہم کہیں سے بھی ٹاسکس شامل کر سکتے ہیں اور یقین رکھ سکتے ہیں کہ کوئی ایجنٹ انہیں اٹھا لے گا۔ مثال کے طور پر، ہماری ٹیم کے ایک انجینئر نے ایک آرام دہ کیبن سے کمزور wifi کے باوجود اپنے فون پر Linear ایپ کے ذریعے تین اہم تبدیلیاں کیں۔

اس طریقے سے کام کرنے کے نتیجے میں تحقیق اور دریافت میں اضافہ

جب ہم نے Symphony کے اثرات کا مشاہدہ کیا تو سب سے نمایاں تبدیلی آؤٹ پٹ میں تھی۔ OpenAI کی کچھ ٹیموں میں پہلے تین ہفتوں کے دوران مرج ہونے والی PRs کی تعداد میں 6X اضافہ دیکھا گیا۔ OpenAI سے باہر، Linear کے بانی Karri Saarinen نے Symphony کے ریلیز کے ساتھ ورک اسپیسز کی تخلیق میں اضافے(نئی ونڈو میں کھلتا ہے) کو نمایاں کیا۔ تاہم، اس سے بھی گہری تبدیلی یہ ہے کہ ٹیمیں کام کے بارے میں کیسے سوچتی ہیں۔

جب ہمارے انجینئرز Codex سیشنز کی نگرانی میں وقت صرف نہیں کرتے، تو کوڈ میں تبدیلیوں کی معاشیات مکمل طور پر بدل جاتی ہے۔ ہر تبدیلی کی محسوس ہونے والی لاگت کم ہو جاتی ہے کیونکہ ہم اب خود نفاذ کے عمل میں انسانی محنت شامل نہیں کر رہے ہوتے۔

اس نے ہمارے رویے کو بدل دیا۔ اب Symphony میں تجرباتی ٹاسکس شروع کرنا بہت آسان ہو گیا ہے۔ کوئی آئیڈیا آزمائیں، کسی ریفیکٹرنگ کو ایکسپلور کریں، کوئی مفروضہ ٹیسٹ کریں، اور صرف وہ نتائج رکھیں جو امید افزا لگیں۔

اس نے یہ بھی ممکن بنایا کہ اب کام شروع کرنے کی صلاحیت مزید لوگوں تک پھیل گئی ہے۔ ہمارے پروڈکٹ مینیجر اور ڈیزائنر اب براہِ راست Symphony میں فیچر ریکویسٹس درج کر سکتے ہیں۔ انہیں ریپوزٹری چیک آؤٹ کرنے یا Codex سیشن منیج کرنے کی ضرورت نہیں ہوتی۔ وہ فیچر کی وضاحت کرتے ہیں اور بدلے میں انہیں ایک ریویو پیکج ملتا ہے جس میں اصل پروڈکٹ کے اندر فیچر کے کام کرنے کی ویڈیو واک تھرو شامل ہوتا ہے۔

Symphony بڑے مونوریپوز میں بھی بہترین کارکردگی دکھاتا ہے، جیسے کہ OpenAI میں ہمارا ریپو، جہاں PR کو مرج کرنے کا آخری مرحلہ سست اور نازک ہوتا ہے۔ یہ سسٹم CI پر نظر رکھتا ہے، ضرورت پڑنے پر ری بیس کرتا ہے، تنازعات کو حل کرتا ہے، غیر مستحکم چیکس کو دوبارہ چلاتا ہے، اور عمومی طور پر تبدیلیوں کو پائپ لائن کے ذریعے آگے بڑھاتا ہے۔ جب تک کوئی ٹکٹ مرج کرنے کے مرحلے تک پہنچتا ہے، ہمیں پورا اعتماد ہوتا ہے کہ یہ تبدیلی انسانی نگرانی کے بغیر مین برانچ میں شامل ہو جائے گی۔

Symphony کا پہلے کا اور بعد کا تقابلی گرڈ

Symphony نافذ کرنے کے بعد، ہم زیادہ کام ایجنٹس کو سونپتے ہیں اور زیادہ مشکل اور جستجو پر مبنی کاموں پر توجہ دیتے ہیں۔

ترقی کے ساتھ نئے اور مختلف مسائل بھی سامنے آتے ہیں

اس سطح پر کام کرنے کے ساتھ کچھ سمجھوتے بھی آتے ہیں۔ جب ہم نے ایجنٹس کو انٹرایکٹو طور پر گائیڈ کرنے کے بجائے انہیں ٹکٹ کی سطح پر کام سونپنا شروع کیا، تو ہم نے دورانِ عمل مسلسل رہنمائی کرنے اور ضرورت پڑنے پر راستہ درست کرنے کی صلاحیت کھو دی۔ بعض اوقات ایجنٹ نے ایسا نتیجہ پیش کیا جو مکمل طور پر ہدف سے ہٹا ہوا تھا۔ یہ بھی مفید ثابت ہوا—ایسی ناکامیاں سسٹم میں موجود خلا کو ظاہر کرتی تھیں اور ہمیں اسے مزید مضبوط بنانے میں مدد دیتی تھیں۔

نتائج کو مینوئل طور پر درست کرنے کے بجائے، ہم نے گارڈ ریلز اور مہارتیں شامل کیں تاکہ ایجنٹس اگلی بار کامیاب ہو سکیں۔ وقت کے ساتھ ساتھ وہ ہمیں اپنے ہارنس میں نئی صلاحیتیں شامل کرنے کی طرف لے گیا، جیسے اینڈ ٹو اینڈ ٹیسٹس چلانا، Chrome DevTools کے ذریعے ایپ کو چلانا، اور QA اسموک ٹیسٹس کا انتظام کرنا۔ ہم نے اپنی دستاویزات میں نمایاں بہتری کی اور واضح کیا کہ ایک اچھا نتیجہ کیسا ہونا چاہیے۔

ہر ٹاسک Symphony کے اندازِ کار کے مطابق نہیں ہوتا ہے۔ کچھ مسائل اب بھی ایسے ہوتے ہیں جن کے لیے انجینئرز کو براہِ راست انٹرایکٹو Codex سیشنز کے ساتھ کام کرنا پڑتا ہے، خاص طور پر مبہم مسائل یا وہ کام جن میں مضبوط فیصلہ سازی اور مہارت درکار ہو۔ عملی طور پر یہی وہ ٹاسکس ہوتے ہیں جو ہمارے انجینئرز کے لیے سب سے زیادہ دلچسپ اور لطف اندوز ہونے کے قابل ہوتے ہیں۔

فرق یہ ہے کہ Symphony معمول کے نفاذی کام کا بڑے حصے کو ہینڈل کر سکتا ہے۔ اس سے انجینئرز کو یہ موقع ملتا ہے کہ وہ ایک وقت میں ایک مشکل مسئلے پر توجہ دیں، بجائے اس کے کہ وہ مسلسل چھوٹے ٹاسکس کے درمیان کانٹیکسٹ سوئچنگ کرتے رہیں۔

ہم نے یہ بھی سیکھا کہ ایجنٹس کو اسٹیٹ مشین میں سخت اور محدود نوڈز کے طور پر دیکھنا مؤثر نہیں ہوتا۔ ماڈلز زیادہ ذہین ہوتے جا رہے ہیں اور اس دائرے سے کہیں بڑے مسائل حل کر سکتے ہیں جس میں ہم انہیں محدود کرنے کی کوشش کرتے ہیں۔ مثال کے طور پر، ابتدائی ورژنز میں تمام GitHub انٹیگریشنز بیرونی ہارنس کا حصہ تھیں—یعنی Codex سے صرف کوڈ میں تبدیلیاں کرنے کی توقع کی جاتی تھی، جبکہ باقی عمل جیسے تبدیلیاں جمع کروانا اور ٹیسٹس چلانا کوڈ میں متعین کیا جاتا تھا۔ ابتدائی مرحلے میں ہم صرف Codex سے ٹاسک کو نافذ کرنے کو کہتے تھے، لیکن یہ طریقہ بہت محدود ثابت ہوا۔ Codex متعدد PRs بنانے، ریویو فیڈبیک پڑھنے اور اسے حل کرنے کی مکمل صلاحیت رکھتا ہے۔ چنانچہ ہم نے اسے ٹولز فراہم کیے—gh CLI، CI لاگز پڑھنے کی مہارتیں وغیرہ—اور اب ہم Codex سے مزید کام بھی لے سکتے ہیں، جیسے پرانے PRs بند کرنا یا مکمل اور ادھورے کام کی رپورٹس نکالنا۔ اس طرح کے کام ابتدائی فیچر نفاذ کے دائرے سے کہیں باہر تھے۔

لہٰذا ہم نے بالآخر ایجنٹس کو سخت مراحل کے بجائے مقاصد دینا شروع کیے، بالکل ویسے ہی جیسے ایک اچھا مینیجر اپنی ٹیم کے رکن کو کوئی ہدف سونپتا ہے۔ ماڈلز کی اصل طاقت ان کی استدلال کی صلاحیت میں ہوتی ہے، اس لیے انہیں ٹولز اور سیاق و سباق فراہم کریں اور انہیں کام کرنے دیں۔

Symphony بنانے کے لیے Symphony کا استعمال

جب آپ Symphony کی ریپوزٹری کھولتے ہیں تو سب سے پہلے آپ یہ دیکھتے ہیں کہ Symphony دراصل صرف ایک SPEC.md فائل ہے—مسئلے اور مطلوبہ حل کی ایک وضاحت۔ ایک پیچیدہ نگرانی کا سسٹم بنانے کے بجائے، ہم نے مسئلے اور مطلوبہ حل کی تعریف کی، اور ایجنٹس کو اعلیٰ سطح کی رہنمائی فراہم کی۔

Markdown

1
# Symphony Service Specification
2

3
Status: Draft v1 (language-agnostic)
4

5
Purpose: Define a service that orchestrates coding agents to get project work done.
6

7
## 1. Problem Statement
8

9
Symphony is a long-running automation service that continuously reads work from an issue tracker
10
(Linear in this specification version), creates an isolated workspace for each issue, and runs a
11
coding agent session for that issue inside the workspace.
12

13
The service solves four operational problems:
14

15
- It turns issue execution into a repeatable daemon workflow instead of manual scripts.
16
- It isolates agent execution in per-issue workspaces so agent commands run only inside per-issue
17
workspace directories.
18
- It keeps the workflow policy in-repo (`WORKFLOW.md`) so teams version the agent prompt and runtime
19
settings with their code.
20
- It provides enough observability to operate and debug multiple concurrent agent runs.
21

22
Implementations are expected to document their trust and safety posture explicitly. This
23
specification does not require a single approval, sandbox, or operator-confirmation policy; some
24
implementations may target trusted environments with a high-trust configuration, while others may
25
require stricter approvals or sandboxing.
26

27
Important boundary:
28

29
- Symphony is a scheduler/runner and tracker reader.
30
- Ticket writes (state transitions, comments, PR links) are typically performed by the coding agent
31
using tools available in the workflow/runtime environment.
32
- A successful run may end at a workflow-defined handoff state (for example `Human Review`), not
33
necessarily `Done`.
34

35
## 2. Goals and Non-Goals
36

37
### 2.1 Goals
38

39
- Poll the issue tracker on a fixed cadence and dispatch work with bounded concurrency.
40
- Maintain a single authoritative orchestrator state for dispatch, retries, and reconciliation.
41
- Create deterministic per-issue workspaces and preserve them across runs.
42
- Stop active runs when issue state changes make them ineligible.
43
- Recover from transient failures with exponential backoff.
44
- Load runtime behavior from a repository-owned `WORKFLOW.md` contract.
45
- Expose operator-visible observability (at minimum structured logs).
46
- Support restart recovery without requiring a persistent database.
47

48
### 2.2 Non-Goals
49

50
- Rich web UI or multi-tenant control plane.
51
- Prescribing a specific dashboard or terminal UI implementation.
52
- General-purpose workflow engine or distributed job scheduler.
53
- Built-in business logic for how to edit tickets, PRs, or comments. (That logic lives in the
54
workflow prompt and agent tooling.)
55
- Mandating strong sandbox controls beyond what the coding agent and host OS provide.
56
- Mandating a single default approval, sandbox, or operator-confirmation posture for all
57
implementations.
58

59
## 3. System Overview
60

61
### 3.1 Main Components
62

63
1. `Workflow Loader`
64
- Reads `WORKFLOW.md`.
65
- Parses YAML front matter and prompt body.
66
- Returns `{config, prompt_template}`.
67

68
2. `Config Layer`
69
- Exposes typed getters for workflow config values.
70
- Applies defaults and environment variable indirection.
71
- Performs validation used by the orchestrator before dispatch.
72

73
3. `Issue Tracker Client`
74
- Fetches candidate issues in active states.
75
- Fetches current states for specific issue IDs (reconciliation).
76
- Fetches terminal-state issues during startup cleanup.
77
- Normalizes tracker payloads into a stable issue model.
78

79
4. `Orchestrator`
80
- Owns the poll tick.
81
- Owns the in-memory runtime state.
82
- Decides which issues to dispatch, retry, stop, or release.
83
- Tracks session metrics and retry queue state.
84

85
5. `Workspace Manager`
86
- Maps issue identifiers to workspace paths.
87
- Ensures per-issue workspace directories exist.
88
- Runs workspace lifecycle hooks.
89
- Cleans workspaces for terminal issues.
90

91
6. `Agent Runner`
92
- Creates workspace.
93
- Builds prompt from issue + workflow template.
94
- Launches the coding agent app-server client.
95
- Streams agent updates back to the orchestrator.
96

97
7. `Status Surface` (optional)
98
- Presents human-readable runtime status (for example terminal output, dashboard, or other
99
operator-facing view).
100
101
8. `Logging`
102
- Emits structured runtime logs to one or more configured sinks.
103

104
### 3.2 Abstraction Levels
105

106
Symphony is easiest to port when kept in these layers:
107

108
1. `Policy Layer` (repo-defined)
109
- `WORKFLOW.md` prompt body.
110
- Team-specific rules for ticket handling, validation, and handoff.
111

112
2. `Configuration Layer` (typed getters)
113
- Parses front matter into typed runtime settings.
114
- Handles defaults, environment tokens, and path normalization.
115

116
3. `Coordination Layer` (orchestrator)
117
- Polling loop, issue eligibility, concurrency, retries, reconciliation.
118

119
4. `Execution Layer` (workspace + agent subprocess)
120
- Filesystem lifecycle, workspace preparation, coding-agent protocol.
121

122
5. `Integration Layer` (Linear adapter)
123
- API calls and normalization for tracker data.
124

125
6. `Observability Layer` (logs + optional status surface)
126
- Operator visibility into orchestrator and agent behavior.
127

128
### 3.3 External Dependencies
129

130
- Issue tracker API (Linear for `tracker.kind: linear` in this specification version).
131
- Local filesystem for workspaces and logs.
132
- Optional workspace population tooling (for example Git CLI, if used).
133
- Coding-agent executable that supports JSON-RPC-like app-server mode over stdio.
134
- Host environment authentication for the issue tracker and coding agent.
135

136
## 4. Core Domain Model
137

138
### 4.1 Entities
139

140
#### 4.1.1 Issue
141

142
Normalized issue record used by orchestration, prompt rendering, and observability output.
143

144
Fields:
145

146
- `id` (string)
147
- Stable tracker-internal ID.
148
- `identifier` (string)
149
- Human-readable ticket key (example: `ABC-123`).
150
- `title` (string)
151
- `description` (string or null)
152
- `priority` (integer or null)
153
- Lower numbers are higher priority in dispatch sorting.
154
- `state` (string)
155
- Current tracker state name.
156
- `branch_name` (string or null)
157
- Tracker-provided branch metadata if available.
158
- `url` (string or null)
159
- `labels` (list of strings)
160
- Normalized to lowercase.
161
- `blocked_by` (list of blocker refs)
162
- Each blocker ref contains:
163
- `id` (string or null)
164
- `identifier` (string or null)
165
- `state` (string or null)
166
- `created_at` (timestamp or null)
167
- `updated_at` (timestamp or null)
168

169
#### 4.1.2 Workflow Definition
170

171
Parsed `WORKFLOW.md` payload:
172

173
- `config` (map)
174
- YAML front matter root object.
175
- `prompt_template` (string)
176
- Markdown body after front matter, trimmed.
177

178
#### 4.1.3 Service Config (Typed View)
179

180
Typed runtime values derived from `WorkflowDefinition.config` plus environment resolution.
181

182
Examples:
183

184
- poll interval
185
- workspace root
186
- active and terminal issue states
187
- concurrency limits
188
- coding-agent executable/args/timeouts
189
- workspace hooks
190

191
#### 4.1.4 Workspace
192

193
Filesystem workspace assigned to one issue identifier.
194

195
Fields (logical):
196

197
- `path` (workspace path; current runtime typically uses absolute paths, but relative roots are
198
possible if configured without path separators)
199
- `workspace_key` (sanitized issue identifier)
200
- `created_now` (boolean, used to gate `after_create` hook)
201

202
#### 4.1.5 Run Attempt
203

204
One execution attempt for one issue.
205

206
Fields (logical):
207

208
- `issue_id`
209
- `issue_identifier`
210
- `attempt` (integer or null, `null` for first run, `>=1` for retries/continuation)
211
- `workspace_path`
212
- `started_at`
213
- `status`
214
- `error` (optional)
215

216
#### 4.1.6 Live Session (Agent Session Metadata)
217

218
State tracked while a coding-agent subprocess is running.
219

220
Fields:
221

222
- `session_id` (string, `<thread_id>-<turn_id>`)
223
- `thread_id` (string)
224
- `turn_id` (string)
225
- `codex_app_server_pid` (string or null)
226
- `last_codex_event` (string/enum or null)
227
- `last_codex_timestamp` (timestamp or null)
228
- `last_codex_message` (summarized payload)
229
- `codex_input_tokens` (integer)
230
- `codex_output_tokens` (integer)
231
- `codex_total_tokens` (integer)
232
- `last_reported_input_tokens` (integer)
233
- `last_reported_output_tokens` (integer)
234
- `last_reported_total_tokens` (integer)
235
- `turn_count` (integer)
236
- Number of coding-agent turns started within the current worker lifetime.
237

238
#### 4.1.7 Retry Entry
239

240
Scheduled retry state for an issue.
241

242
Fields:
243

244
- `issue_id`
245
- `identifier` (best-effort human ID for status surfaces/logs)
246
- `attempt` (integer, 1-based for retry queue)
247
- `due_at_ms` (monotonic clock timestamp)
248
- `timer_handle` (runtime-specific timer reference)
249
- `error` (string or null)
250

251
#### 4.1.8 Orchestrator Runtime State
252

253
Single authoritative in-memory state owned by the orchestrator.
254

255
Fields:
256

257
- `poll_interval_ms` (current effective poll interval)
258
- `max_concurrent_agents` (current effective global concurrency limit)
259
- `running` (map `issue_id -> running entry`)
260
- `claimed` (set of issue IDs reserved/running/retrying)
261
- `retry_attempts` (map `issue_id -> RetryEntry`)
262
- `completed` (set of issue IDs; bookkeeping only, not dispatch gating)
263
- `codex_totals` (aggregate tokens + runtime seconds)
264
- `codex_rate_limits` (latest rate-limit snapshot from agent events)
265

266
### 4.2 Stable Identifiers and Normalization Rules
267

268
- `Issue ID`
269
- Use for tracker lookups and internal map keys.
270
- `Issue Identifier`
271
- Use for human-readable logs and workspace naming.
272
- `Workspace Key`
273
- Derive from `issue.identifier` by replacing any character not in `[A-Za-z0-9._-]` with `_`.
274
- Use the sanitized value for the workspace directory name.
275
- `Normalized Issue State`
276
- Compare states after `lowercase`.
277
- `Session ID`
278
- Compose from coding-agent `thread_id` and `turn_id` as `<thread_id>-<turn_id>`.
279

280
## 5. Workflow Specification (Repository Contract)
281

282
### 5.1 File Discovery and Path Resolution
283

284
Workflow file path precedence:
285

286
1. Explicit application/runtime setting (set by CLI startup path).
287
2. Default: `WORKFLOW.md` in the current process working directory.
288

289
Loader behavior:
290

291
- If the file cannot be read, return `missing_workflow_file` error.
292
- The workflow file is expected to be repository-owned and version-controlled.
293

294
### 5.2 File Format
295

296
`WORKFLOW.md` is a Markdown file with optional YAML front matter.
297

298
Design note:
299

300
- `WORKFLOW.md` should be self-contained enough to describe and run different workflows (prompt,
301
runtime settings, hooks, and tracker selection/config) without requiring out-of-band
302
service-specific configuration.
303

304
Parsing rules:
305

306
- If file starts with `---`, parse lines until the next `---` as YAML front matter.
307
- Remaining lines become the prompt body.
308
- If front matter is absent, treat the entire file as prompt body and use an empty config map.
309
- YAML front matter must decode to a map/object; non-map YAML is an error.
310
- Prompt body is trimmed before use.
311

312
Returned workflow object:
313

314
- `config`: front matter root object (not nested under a `config` key).
315
- `prompt_template`: trimmed Markdown body.
316

317
### 5.3 Front Matter Schema
318

319
Top-level keys:
320

321
- `tracker`
322
- `polling`
323
- `workspace`
324
- `hooks`
325
- `agent`
326
- `codex`
327

328
Unknown keys should be ignored for forward compatibility.
329

330
Note:
331

332
- The workflow front matter is extensible. Optional extensions may define additional top-level keys
333
(for example `server`) without changing the core schema above.
334
- Extensions should document their field schema, defaults, validation rules, and whether changes
335
apply dynamically or require restart.
336
- Common extension: `server.port` (integer) enables the optional HTTP server described in Section
337
13.7.
338

339
#### 5.3.1 `tracker` (object)
340

341
Fields:
342

343
- `kind` (string)
344
- Required for dispatch.
345
- Current supported value: `linear`
346
- `endpoint` (string)
347
- Default for `tracker.kind == "linear"`: `https://api.linear.app/graphql`
348
- `api_key` (string)
349
- May be a literal token or `$VAR_NAME`.
350
- Canonical environment variable for `tracker.kind == "linear"`: `LINEAR_API_KEY`.
351
- If `$VAR_NAME` resolves to an empty string, treat the key as missing.
352
- `project_slug` (string)
353
- Required for dispatch when `tracker.kind == "linear"`.
354
- `active_states` (list of strings)
355
- Default: `Todo`, `In Progress`
356
- `terminal_states` (list of strings)
357
- Default: `Closed`, `Cancelled`, `Canceled`, `Duplicate`, `Done`
358

359
#### 5.3.2 `polling` (object)
360

361
Fields:
362

363
- `interval_ms` (integer or string integer)
364
- Default: `30000`
365
- Changes should be re-applied at runtime and affect future tick scheduling without restart.
366

367
#### 5.3.3 `workspace` (object)
368

369
Fields:
370

371
- `root` (path string or `$VAR`)
372
- Default: `<system-temp>/symphony_workspaces`
373
- `~` and strings containing path separators are expanded.
374
- Bare strings without path separators are preserved as-is (relative roots are allowed but
375
discouraged).
376
377
#### 5.3.4 `hooks` (object)
378

379
Fields:
380

381
- `after_create` (multiline shell script string, optional)
382
- Runs only when a workspace directory is newly created.
383
- Failure aborts workspace creation.
384
- `before_run` (multiline shell script string, optional)
385
- Runs before each agent attempt after workspace preparation and before launching the coding
386
agent.
387
- Failure aborts the current attempt.
388
- `after_run` (multiline shell script string, optional)
389
- Runs after each agent attempt (success, failure, timeout, or cancellation) once the workspace
390
exists.
391
- Failure is logged but ignored.
392
- `before_remove` (multiline shell script string, optional)
393
- Runs before workspace deletion if the directory exists.
394
- Failure is logged but ignored; cleanup still proceeds.
395
- `timeout_ms` (integer, optional)
396
- Default: `60000`
397
- Applies to all workspace hooks.
398
- Non-positive values should be treated as invalid and fall back to the default.
399
- Changes should be re-applied at runtime for future hook executions.
400
401
#### 5.3.5 `agent` (object)
402

403
Fields:
404

405
- `max_concurrent_agents` (integer or string integer)
406
- Default: `10`
407
- Changes should be re-applied at runtime and affect subsequent dispatch decisions.
408
- `max_retry_backoff_ms` (integer or string integer)
409
- Default: `300000` (5 minutes)
410
- Changes should be re-applied at runtime and affect future retry scheduling.
411
- `max_concurrent_agents_by_state` (map `state_name -> positive integer`)
412
- Default: empty map.
413
- State keys are normalized (`lowercase`) for lookup.
414
- Invalid entries (non-positive or non-numeric) are ignored.
415

416
#### 5.3.6 `codex` (object)
417

418
Fields:
419

420
For Codex-owned config values such as `approval_policy`, `thread_sandbox`, and
421
`turn_sandbox_policy`, supported values are defined by the targeted Codex app-server version.
422
Implementors should treat them as pass-through Codex config values rather than relying on a
423
hand-maintained enum in this spec. To inspect the installed Codex schema, run
424
`codex app-server generate-json-schema --out <dir>` and inspect the relevant definitions referenced
425
by `v2/ThreadStartParams.json` and `v2/TurnStartParams.json`. Implementations may validate these
426
fields locally if they want stricter startup checks.
427

428
- `command` (string shell command)
429
- Default: `codex app-server`
430
- The runtime launches this command via `bash -lc` in the workspace directory.
431
- The launched process must speak a compatible app-server protocol over stdio.
432
- `approval_policy` (Codex `AskForApproval` value)
433
- Default: implementation-defined.
434
- `thread_sandbox` (Codex `SandboxMode` value)
435
- Default: implementation-defined.
436
- `turn_sandbox_policy` (Codex `SandboxPolicy` value)
437
- Default: implementation-defined.
438
- `turn_timeout_ms` (integer)
439
- Default: `3600000` (1 hour)
440
- `read_timeout_ms` (integer)
441
- Default: `5000`
442
- `stall_timeout_ms` (integer)
443
- Default: `300000` (5 minutes)
444
- If `<= 0`, stall detection is disabled.
445

446
### 5.4 Prompt Template Contract
447

448
The Markdown body of `WORKFLOW.md` is the per-issue prompt template.
449

450
Rendering requirements:
451

452
- Use a strict template engine (Liquid-compatible semantics are sufficient).
453
- Unknown variables must fail rendering.
454
- Unknown filters must fail rendering.
455

456
Template input variables:
457

458
- `issue` (object)
459
- Includes all normalized issue fields, including labels and blockers.
460
- `attempt` (integer or null)
461
- `null`/absent on first attempt.
462
- Integer on retry or continuation run.
463

464
Fallback prompt behavior:
465

466
- If the workflow prompt body is empty, the runtime may use a minimal default prompt
467
(`You are working on an issue from Linear.`).
468
- Workflow file read/parse failures are configuration/validation errors and should not silently fall
469
back to a prompt.
470

471
### 5.5 Workflow Validation and Error Surface
472

473
Error classes:
474

475
- `missing_workflow_file`
476
- `workflow_parse_error`
477
- `workflow_front_matter_not_a_map`
478
- `template_parse_error` (during prompt rendering)
479
- `template_render_error` (unknown variable/filter, invalid interpolation)
480

481
Dispatch gating behavior:
482

483
- Workflow file read/YAML errors block new dispatches until fixed.
484
- Template errors fail only the affected run attempt.
485

486
## 6. Configuration Specification
487

488
### 6.1 Source Precedence and Resolution Semantics
489

490
Configuration precedence:
491

492
1. Workflow file path selection (runtime setting -> cwd default).
493
2. YAML front matter values.
494
3. Environment indirection via `$VAR_NAME` inside selected YAML values.
495
4. Built-in defaults.
496

497
Value coercion semantics:
498

499
- Path/command fields support:
500
- `~` home expansion
501
- `$VAR` expansion for env-backed path values
502
- Apply expansion only to values intended to be local filesystem paths; do not rewrite URIs or
503
arbitrary shell command strings.
504
505
### 6.2 Dynamic Reload Semantics
506

507
Dynamic reload is required:
508

509
- The software should watch `WORKFLOW.md` for changes.
510
- On change, it should re-read and re-apply workflow config and prompt template without restart.
511
- The software should attempt to adjust live behavior to the new config (for example polling
512
cadence, concurrency limits, active/terminal states, codex settings, workspace paths/hooks, and
513
prompt content for future runs).
514
- Reloaded config applies to future dispatch, retry scheduling, reconciliation decisions, hook
515
execution, and agent launches.
516
- Implementations are not required to restart in-flight agent sessions automatically when config
517
changes.
518
- Extensions that manage their own listeners/resources (for example an HTTP server port change) may
519
require restart unless the implementation explicitly supports live rebind.
520
- Implementations should also re-validate/reload defensively during runtime operations (for example
521
before dispatch) in case filesystem watch events are missed.
522
- Invalid reloads should not crash the service; keep operating with the last known good effective
523
configuration and emit an operator-visible error.
524

525
### 6.3 Dispatch Preflight Validation
526

527
This validation is a scheduler preflight run before attempting to dispatch new work. It validates
528
the workflow/config needed to poll and launch workers, not a full audit of all possible workflow
529
behavior.
530

531
Startup validation:
532

533
- Validate configuration before starting the scheduling loop.
534
- If startup validation fails, fail startup and emit an operator-visible error.
535

536
Per-tick dispatch validation:
537

538
- Re-validate before each dispatch cycle.
539
- If validation fails, skip dispatch for that tick, keep reconciliation active, and emit an
540
operator-visible error.
541

542
Validation checks:
543

544
- Workflow file can be loaded and parsed.
545
- `tracker.kind` is present and supported.
546
- `tracker.api_key` is present after `$` resolution.
547
- `tracker.project_slug` is present when required by the selected tracker kind.
548
- `codex.command` is present and non-empty.
549

550
### 6.4 Config Fields Summary (Cheat Sheet)
551

552
This section is intentionally redundant so a coding agent can implement the config layer quickly.
553

554
- `tracker.kind`: string, required, currently `linear`
555
- `tracker.endpoint`: string, default `https://api.linear.app/graphql` when `tracker.kind=linear`
556
- `tracker.api_key`: string or `$VAR`, canonical env `LINEAR_API_KEY` when `tracker.kind=linear`
557
- `tracker.project_slug`: string, required when `tracker.kind=linear`
558
- `tracker.active_states`: list of strings, default `["Todo", "In Progress"]`
559
- `tracker.terminal_states`: list of strings, default `["Closed", "Cancelled", "Canceled", "Duplicate", "Done"]`
560
- `polling.interval_ms`: integer, default `30000`
561
- `workspace.root`: path, default `<system-temp>/symphony_workspaces`
562
- `worker.ssh_hosts` (extension): list of SSH host strings, optional; when omitted, work runs
563
locally
564
- `worker.max_concurrent_agents_per_host` (extension): positive integer, optional; shared per-host
565
cap applied across configured SSH hosts
566
- `hooks.after_create`: shell script or null
567
- `hooks.before_run`: shell script or null
568
- `hooks.after_run`: shell script or null
569
- `hooks.before_remove`: shell script or null
570
- `hooks.timeout_ms`: integer, default `60000`
571
- `agent.max_concurrent_agents`: integer, default `10`
572
- `agent.max_turns`: integer, default `20`
573
- `agent.max_retry_backoff_ms`: integer, default `300000` (5m)
574
- `agent.max_concurrent_agents_by_state`: map of positive integers, default `{}`
575
- `codex.command`: shell command string, default `codex app-server`
576
- `codex.approval_policy`: Codex `AskForApproval` value, default implementation-defined
577
- `codex.thread_sandbox`: Codex `SandboxMode` value, default implementation-defined
578
- `codex.turn_sandbox_policy`: Codex `SandboxPolicy` value, default implementation-defined
579
- `codex.turn_timeout_ms`: integer, default `3600000`
580
- `codex.read_timeout_ms`: integer, default `5000`
581
- `codex.stall_timeout_ms`: integer, default `300000`
582
- `server.port` (extension): integer, optional; enables the optional HTTP server, `0` may be used
583
for ephemeral local bind, and CLI `--port` overrides it
584

585
## 7. Orchestration State Machine
586

587
The orchestrator is the only component that mutates scheduling state. All worker outcomes are
588
reported back to it and converted into explicit state transitions.
589

590
### 7.1 Issue Orchestration States
591

592
This is not the same as tracker states (`Todo`, `In Progress`, etc.). This is the service's internal
593
claim state.
594

595
1. `Unclaimed`
596
- Issue is not running and has no retry scheduled.
597

598
2. `Claimed`
599
- Orchestrator has reserved the issue to prevent duplicate dispatch.
600
- In practice, claimed issues are either `Running` or `RetryQueued`.
601

602
3. `Running`
603
- Worker task exists and the issue is tracked in `running` map.
604

605
4. `RetryQueued`
606
- Worker is not running, but a retry timer exists in `retry_attempts`.
607

608
5. `Released`
609
- Claim removed because issue is terminal, non-active, missing, or retry path completed without
610
re-dispatch.
611
612
Important nuance:
613

614
- A successful worker exit does not mean the issue is done forever.
615
- The worker may continue through multiple back-to-back coding-agent turns before it exits.
616
- After each normal turn completion, the worker re-checks the tracker issue state.
617
- If the issue is still in an active state, the worker should start another turn on the same live
618
coding-agent thread in the same workspace, up to `agent.max_turns`.
619
- The first turn should use the full rendered task prompt.
620
- Continuation turns should send only continuation guidance to the existing thread, not resend the
621
original task prompt that is already present in thread history.
622
- Once the worker exits normally, the orchestrator still schedules a short continuation retry
623
(about 1 second) so it can re-check whether the issue remains active and needs another worker
624
session.
625

626
### 7.2 Run Attempt Lifecycle
627

628
A run attempt transitions through these phases:
629

630
1. `PreparingWorkspace`
631
2. `BuildingPrompt`
632
3. `LaunchingAgentProcess`
633
4. `InitializingSession`
634
5. `StreamingTurn`
635
6. `Finishing`
636
7. `Succeeded`
637
8. `Failed`
638
9. `TimedOut`
639
10. `Stalled`
640
11. `CanceledByReconciliation`
641

642
Distinct terminal reasons are important because retry logic and logs differ.
643

644
### 7.3 Transition Triggers
645

646
- `Poll Tick`
647
- Reconcile active runs.
648
- Validate config.
649
- Fetch candidate issues.
650
- Dispatch until slots are exhausted.
651

652
- `Worker Exit (normal)`
653
- Remove running entry.
654
- Update aggregate runtime totals.
655
- Schedule continuation retry (attempt `1`) after the worker exhausts or finishes its in-process
656
turn loop.
657
658
- `Worker Exit (abnormal)`
659
- Remove running entry.
660
- Update aggregate runtime totals.
661
- Schedule exponential-backoff retry.
662

663
- `Codex Update Event`
664
- Update live session fields, token counters, and rate limits.
665

666
- `Retry Timer Fired`
667
- Re-fetch active candidates and attempt re-dispatch, or release claim if no longer eligible.
668

669
- `Reconciliation State Refresh`
670
- Stop runs whose issue states are terminal or no longer active.
671

672
- `Stall Timeout`
673
- Kill worker and schedule retry.
674

675
### 7.4 Idempotency and Recovery Rules
676

677
- The orchestrator serializes state mutations through one authority to avoid duplicate dispatch.
678
- `claimed` and `running` checks are required before launching any worker.
679
- Reconciliation runs before dispatch on every tick.
680
- Restart recovery is tracker-driven and filesystem-driven (no durable orchestrator DB required).
681
- Startup terminal cleanup removes stale workspaces for issues already in terminal states.
682

683
## 8. Polling, Scheduling, and Reconciliation
684

685
### 8.1 Poll Loop
686

687
At startup, the service validates config, performs startup cleanup, schedules an immediate tick, and
688
then repeats every `polling.interval_ms`.
689

690
The effective poll interval should be updated when workflow config changes are re-applied.
691

692
Tick sequence:
693

694
1. Reconcile running issues.
695
2. Run dispatch preflight validation.
696
3. Fetch candidate issues from tracker using active states.
697
4. Sort issues by dispatch priority.
698
5. Dispatch eligible issues while slots remain.
699
6. Notify observability/status consumers of state changes.
700

701
If per-tick validation fails, dispatch is skipped for that tick, but reconciliation still happens
702
first.
703

704
### 8.2 Candidate Selection Rules
705

706
An issue is dispatch-eligible only if all are true:
707

708
- It has `id`, `identifier`, `title`, and `state`.
709
- Its state is in `active_states` and not in `terminal_states`.
710
- It is not already in `running`.
711
- It is not already in `claimed`.
712
- Global concurrency slots are available.
713
- Per-state concurrency slots are available.
714
- Blocker rule for `Todo` state passes:
715
- If the issue state is `Todo`, do not dispatch when any blocker is non-terminal.
716

717
Sorting order (stable intent):
718

719
1. `priority` ascending (1..4 are preferred; null/unknown sorts last)
720
2. `created_at` oldest first
721
3. `identifier` lexicographic tie-breaker
722

723
### 8.3 Concurrency Control
724

725
Global limit:
726

727
- `available_slots = max(max_concurrent_agents - running_count, 0)`
728

729
Per-state limit:
730

731
- `max_concurrent_agents_by_state[state]` if present (state key normalized)
732
- otherwise fallback to global limit
733

734
The runtime counts issues by their current tracked state in the `running` map.
735

736
Optional SSH host limit:
737

738
- When `worker.max_concurrent_agents_per_host` is set, each configured SSH host may run at most
739
that many concurrent agents at once.
740
- Hosts at that cap are skipped for new dispatch until capacity frees up.
741

742
### 8.4 Retry and Backoff
743

744
Retry entry creation:
745

746
- Cancel any existing retry timer for the same issue.
747
- Store `attempt`, `identifier`, `error`, `due_at_ms`, and new timer handle.
748

749
Backoff formula:
750

751
- Normal continuation retries after a clean worker exit use a short fixed delay of `1000` ms.
752
- Failure-driven retries use `delay = min(10000 * 2^(attempt - 1), agent.max_retry_backoff_ms)`.
753
- Power is capped by the configured max retry backoff (default `300000` / 5m).
754

755
Retry handling behavior:
756

757
1. Fetch active candidate issues (not all issues).
758
2. Find the specific issue by `issue_id`.
759
3. If not found, release claim.
760
4. If found and still candidate-eligible:
761
- Dispatch if slots are available.
762
- Otherwise requeue with error `no available orchestrator slots`.
763
5. If found but no longer active, release claim.
764

765
Note:
766

767
- Terminal-state workspace cleanup is handled by startup cleanup and active-run reconciliation
768
(including terminal transitions for currently running issues).
769
- Retry handling mainly operates on active candidates and releases claims when the issue is absent,
770
rather than performing terminal cleanup itself.
771

772
### 8.5 Active Run Reconciliation
773

774
Reconciliation runs every tick and has two parts.
775

776
Part A: Stall detection
777

778
- For each running issue, compute `elapsed_ms` since:
779
- `last_codex_timestamp` if any event has been seen, else
780
- `started_at`
781
- If `elapsed_ms > codex.stall_timeout_ms`, terminate the worker and queue a retry.
782
- If `stall_timeout_ms <= 0`, skip stall detection entirely.
783

784
Part B: Tracker state refresh
785

786
- Fetch current issue states for all running issue IDs.
787
- For each running issue:
788
- If tracker state is terminal: terminate worker and clean workspace.
789
- If tracker state is still active: update the in-memory issue snapshot.
790
- If tracker state is neither active nor terminal: terminate worker without workspace cleanup.
791
- If state refresh fails, keep workers running and try again on the next tick.
792

793
### 8.6 Startup Terminal Workspace Cleanup
794

795
When the service starts:
796

797
1. Query tracker for issues in terminal states.
798
2. For each returned issue identifier, remove the corresponding workspace directory.
799
3. If the terminal-issues fetch fails, log a warning and continue startup.
800

801
This prevents stale terminal workspaces from accumulating after restarts.
802

803
## 9. Workspace Management and Safety
804

805
### 9.1 Workspace Layout
806

807
Workspace root:
808

809
- `workspace.root` (normalized path; the current config layer expands path-like values and preserves
810
bare relative names)
811

812
Per-issue workspace path:
813

814
- `<workspace.root>/<sanitized_issue_identifier>`
815

816
Workspace persistence:
817

818
- Workspaces are reused across runs for the same issue.
819
- Successful runs do not auto-delete workspaces.
820

821
### 9.2 Workspace Creation and Reuse
822

823
Input: `issue.identifier`
824

825
Algorithm summary:
826

827
1. Sanitize identifier to `workspace_key`.
828
2. Compute workspace path under workspace root.
829
3. Ensure the workspace path exists as a directory.
830
4. Mark `created_now=true` only if the directory was created during this call; otherwise
831
`created_now=false`.
832
5. If `created_now=true`, run `after_create` hook if configured.
833

834
Notes:
835

836
- This section does not assume any specific repository/VCS workflow.
837
- Workspace preparation beyond directory creation (for example dependency bootstrap, checkout/sync,
838
code generation) is implementation-defined and is typically handled via hooks.
839

840
### 9.3 Optional Workspace Population (Implementation-Defined)
841

842
The spec does not require any built-in VCS or repository bootstrap behavior.
843

844
Implementations may populate or synchronize the workspace using implementation-defined logic and/or
845
hooks (for example `after_create` and/or `before_run`).
846

847
Failure handling:
848

849
- Workspace population/synchronization failures return an error for the current attempt.
850
- If failure happens while creating a brand-new workspace, implementations may remove the partially
851
prepared directory.
852
- Reused workspaces should not be destructively reset on population failure unless that policy is
853
explicitly chosen and documented.
854

855
### 9.4 Workspace Hooks
856

857
Supported hooks:
858

859
- `hooks.after_create`
860
- `hooks.before_run`
861
- `hooks.after_run`
862
- `hooks.before_remove`
863

864
Execution contract:
865

866
- Execute in a local shell context appropriate to the host OS, with the workspace directory as
867
`cwd`.
868
- On POSIX systems, `sh -lc <script>` (or a stricter equivalent such as `bash -lc <script>`) is a
869
conforming default.
870
- Hook timeout uses `hooks.timeout_ms`; default: `60000 ms`.
871
- Log hook start, failures, and timeouts.
872

873
Failure semantics:
874

875
- `after_create` failure or timeout is fatal to workspace creation.
876
- `before_run` failure or timeout is fatal to the current run attempt.
877
- `after_run` failure or timeout is logged and ignored.
878
- `before_remove` failure or timeout is logged and ignored.
879

880
### 9.5 Safety Invariants
881

882
This is the most important portability constraint.
883

884
Invariant 1: Run the coding agent only in the per-issue workspace path.
885

886
- Before launching the coding-agent subprocess, validate:
887
- `cwd == workspace_path`
888

889
Invariant 2: Workspace path must stay inside workspace root.
890

891
- Normalize both paths to absolute.
892
- Require `workspace_path` to have `workspace_root` as a prefix directory.
893
- Reject any path outside the workspace root.
894

895
Invariant 3: Workspace key is sanitized.
896

897
- Only `[A-Za-z0-9._-]` allowed in workspace directory names.
898
- Replace all other characters with `_`.
899

900
## 10. Agent Runner Protocol (Coding Agent Integration)
901

902
This section defines the language-neutral contract for integrating a coding agent app-server.
903

904
Compatibility profile:
905

906
- The normative contract is message ordering, required behaviors, and the logical fields that must
907
be extracted (for example session IDs, completion state, approval handling, and usage/rate-limit
908
telemetry).
909
- Exact JSON field names may vary slightly across compatible app-server versions.
910
- Implementations should tolerate equivalent payload shapes when they carry the same logical
911
meaning, especially for nested IDs, approval requests, user-input-required signals, and
912
token/rate-limit metadata.
913

914
### 10.1 Launch Contract
915

916
Subprocess launch parameters:
917

918
- Command: `codex.command`
919
- Invocation: `bash -lc <codex.command>`
920
- Working directory: workspace path
921
- Stdout/stderr: separate streams
922
- Framing: line-delimited protocol messages on stdout (JSON-RPC-like JSON per line)
923

924
Notes:
925

926
- The default command is `codex app-server`.
927
- Approval policy, cwd, and prompt are expressed in the protocol messages in Section 10.2.
928

929
Recommended additional process settings:
930

931
- Max line size: 10 MB (for safe buffering)
932

933
### 10.2 Session Startup Handshake
934

935
Reference: https://developers.openai.com/codex/app-server/
936

937
The client must send these protocol messages in order:
938

939
Illustrative startup transcript (equivalent payload shapes are acceptable if they preserve the same
940
semantics):
941

942
```json
943
{"id":1,"method":"initialize","params":{"clientInfo":{"name":"symphony","version":"1.0"},"capabilities":{}}}
944
{"method":"initialized","params":{}}
945
{"id":2,"method":"thread/start","params":{"approvalPolicy":"<implementation-defined>","sandbox":"<implementation-defined>","cwd":"/abs/workspace"}}
946
{"id":3,"method":"turn/start","params":{"threadId":"<thread-id>","input":[{"type":"text","text":"<rendered prompt-or-continuation-guidance>"}],"cwd":"/abs/workspace","title":"ABC-123: Example","approvalPolicy":"<implementation-defined>","sandboxPolicy":{"type":"<implementation-defined>"}}}
947
```
948

949
1. `initialize` request
950
- Params include:
951
- `clientInfo` object (for example `{name, version}`)
952
- `capabilities` object (may be empty)
953
- If the targeted Codex app-server requires capability negotiation for dynamic tools, include the
954
necessary capability flag(s) here.
955
- Wait for response (`read_timeout_ms`)
956
2. `initialized` notification
957
3. `thread/start` request
958
- Params include:
959
- `approvalPolicy` = implementation-defined session approval policy value
960
- `sandbox` = implementation-defined session sandbox value
961
- `cwd` = absolute workspace path
962
- If optional client-side tools are implemented, include their advertised tool specs using the
963
protocol mechanism supported by the targeted Codex app-server version.
964
4. `turn/start` request
965
- Params include:
966
- `threadId`
967
- `input` = single text item containing rendered prompt for the first turn, or continuation
968
guidance for later turns on the same thread
969
- `cwd`
970
- `title` = `<issue.identifier>: <issue.title>`
971
- `approvalPolicy` = implementation-defined turn approval policy value
972
- `sandboxPolicy` = implementation-defined object-form sandbox policy payload when required by
973
the targeted app-server version
974
975
Session identifiers:
976

977
- Read `thread_id` from `thread/start` result `result.thread.id`
978
- Read `turn_id` from each `turn/start` result `result.turn.id`
979
- Emit `session_id = "<thread_id>-<turn_id>"`
980
- Reuse the same `thread_id` for all continuation turns inside one worker run
981

982
### 10.3 Streaming Turn Processing
983

984
The client reads line-delimited messages until the turn terminates.
985

986
Completion conditions:
987

988
- `turn/completed` -> success
989
- `turn/failed` -> failure
990
- `turn/cancelled` -> failure
991
- turn timeout (`turn_timeout_ms`) -> failure
992
- subprocess exit -> failure
993

994
Continuation processing:
995

996
- If the worker decides to continue after a successful turn, it should issue another `turn/start`
997
on the same live `threadId`.
998
- The app-server subprocess should remain alive across those continuation turns and be stopped only
999
when the worker run is ending.
1000

1001
Line handling requirements:
1002

1003
- Read protocol messages from stdout only.
1004
- Buffer partial stdout lines until newline arrives.
1005
- Attempt JSON parse on complete stdout lines.
1006
- Stderr is not part of the protocol stream:
1007
- ignore it or log it as diagnostics
1008
- do not attempt protocol JSON parsing on stderr
1009

1010
### 10.4 Emitted Runtime Events (Upstream to Orchestrator)
1011

1012
The app-server client emits structured events to the orchestrator callback. Each event should
1013
include:
1014

1015
- `event` (enum/string)
1016
- `timestamp` (UTC timestamp)
1017
- `codex_app_server_pid` (if available)
1018
- optional `usage` map (token counts)
1019
- payload fields as needed
1020

1021
Important emitted events may include:
1022

1023
- `session_started`
1024
- `startup_failed`
1025
- `turn_completed`
1026
- `turn_failed`
1027
- `turn_cancelled`
1028
- `turn_ended_with_error`
1029
- `turn_input_required`
1030
- `approval_auto_approved`
1031
- `unsupported_tool_call`
1032
- `notification`
1033
- `other_message`
1034
- `malformed`
1035

1036
### 10.5 Approval, Tool Calls, and User Input Policy
1037

1038
Approval, sandbox, and user-input behavior is implementation-defined.
1039

1040
Policy requirements:
1041

1042
- Each implementation should document its chosen approval, sandbox, and operator-confirmation
1043
posture.
1044
- Approval requests and user-input-required events must not leave a run stalled indefinitely. An
1045
implementation should either satisfy them, surface them to an operator, auto-resolve them, or
1046
fail the run according to its documented policy.
1047

1048
Example high-trust behavior:
1049

1050
- Auto-approve command execution approvals for the session.
1051
- Auto-approve file-change approvals for the session.
1052
- Treat user-input-required turns as hard failure.
1053

1054
Unsupported dynamic tool calls:
1055

1056
- Supported dynamic tool calls that are explicitly implemented and advertised by the runtime should
1057
be handled according to their extension contract.
1058
- If the agent requests a dynamic tool call (`item/tool/call`) that is not supported, return a tool
1059
failure response and continue the session.
1060
- This prevents the session from stalling on unsupported tool execution paths.
1061

1062
Optional client-side tool extension:
1063

1064
- An implementation may expose a limited set of client-side tools to the app-server session.
1065
- Current optional standardized tool: `linear_graphql`.
1066
- If implemented, supported tools should be advertised to the app-server session during startup
1067
using the protocol mechanism supported by the targeted Codex app-server version.
1068
- Unsupported tool names should still return a failure result and continue the session.
1069

1070
`linear_graphql` extension contract:
1071

1072
- Purpose: execute a raw GraphQL query or mutation against Linear using Symphony's configured
1073
tracker auth for the current session.
1074
- Availability: only meaningful when `tracker.kind == "linear"` and valid Linear auth is configured.
1075
- Preferred input shape:
1076

1077
```json
1078
{
1079
"query": "single GraphQL query or mutation document",
1080
"variables": {
1081
"optional": "graphql variables object"
1082
}
1083
}
1084
```
1085

1086
- `query` must be a non-empty string.
1087
- `query` must contain exactly one GraphQL operation.
1088
- `variables` is optional and, when present, must be a JSON object.
1089
- Implementations may additionally accept a raw GraphQL query string as shorthand input.
1090
- Execute one GraphQL operation per tool call.
1091
- If the provided document contains multiple operations, reject the tool call as invalid input.
1092
- `operationName` selection is intentionally out of scope for this extension.
1093
- Reuse the configured Linear endpoint and auth from the active Symphony workflow/runtime config; do
1094
not require the coding agent to read raw tokens from disk.
1095
- Tool result semantics:
1096
- transport success + no top-level GraphQL `errors` -> `success=true`
1097
- top-level GraphQL `errors` present -> `success=false`, but preserve the GraphQL response body
1098
for debugging
1099
- invalid input, missing auth, or transport failure -> `success=false` with an error payload
1100
- Return the GraphQL response or error payload as structured tool output that the model can inspect
1101
in-session.
1102
1103
Illustrative responses (equivalent payload shapes are acceptable if they preserve the same outcome):
1104

1105
```json
1106
{"id":"<approval-id>","result":{"approved":true}}
1107
{"id":"<tool-call-id>","result":{"success":false,"error":"unsupported_tool_call"}}
1108
```
1109

1110
Hard failure on user input requirement:
1111

1112
- If the agent requests user input, fail the run attempt immediately.
1113
- The client detects this via:
1114
- explicit method (`item/tool/requestUserInput`), or
1115
- turn methods/flags indicating input is required.
1116

1117
### 10.6 Timeouts and Error Mapping
1118

1119
Timeouts:
1120

1121
- `codex.read_timeout_ms`: request/response timeout during startup and sync requests
1122
- `codex.turn_timeout_ms`: total turn stream timeout
1123
- `codex.stall_timeout_ms`: enforced by orchestrator based on event inactivity
1124

1125
Error mapping (recommended normalized categories):
1126

1127
- `codex_not_found`
1128
- `invalid_workspace_cwd`
1129
- `response_timeout`
1130
- `turn_timeout`
1131
- `port_exit`
1132
- `response_error`
1133
- `turn_failed`
1134
- `turn_cancelled`
1135
- `turn_input_required`
1136

1137
### 10.7 Agent Runner Contract
1138

1139
The `Agent Runner` wraps workspace + prompt + app-server client.
1140

1141
Behavior:
1142

1143
1. Create/reuse workspace for issue.
1144
2. Build prompt from workflow template.
1145
3. Start app-server session.
1146
4. Forward app-server events to orchestrator.
1147
5. On any error, fail the worker attempt (the orchestrator will retry).
1148

1149
Note:
1150

1151
- Workspaces are intentionally preserved after successful runs.
1152

1153
## 11. Issue Tracker Integration Contract (Linear-Compatible)
1154

1155
### 11.1 Required Operations
1156

1157
An implementation must support these tracker adapter operations:
1158

1159
1. `fetch_candidate_issues()`
1160
- Return issues in configured active states for a configured project.
1161

1162
2. `fetch_issues_by_states(state_names)`
1163
- Used for startup terminal cleanup.
1164

1165
3. `fetch_issue_states_by_ids(issue_ids)`
1166
- Used for active-run reconciliation.
1167

1168
### 11.2 Query Semantics (Linear)
1169

1170
Linear-specific requirements for `tracker.kind == "linear"`:
1171

1172
- `tracker.kind == "linear"`
1173
- GraphQL endpoint (default `https://api.linear.app/graphql`)
1174
- Auth token sent in `Authorization` header
1175
- `tracker.project_slug` maps to Linear project `slugId`
1176
- Candidate issue query filters project using `project: { slugId: { eq: $projectSlug } }`
1177
- Issue-state refresh query uses GraphQL issue IDs with variable type `[ID!]`
1178
- Pagination required for candidate issues
1179
- Page size default: `50`
1180
- Network timeout: `30000 ms`
1181

1182
Important:
1183

1184
- Linear GraphQL schema details can drift. Keep query construction isolated and test the exact query
1185
fields/types required by this specification.
1186

1187
A non-Linear implementation may change transport details, but the normalized outputs must match the
1188
domain model in Section 4.
1189

1190
### 11.3 Normalization Rules
1191

1192
Candidate issue normalization should produce fields listed in Section 4.1.1.
1193

1194
Additional normalization details:
1195

1196
- `labels` -> lowercase strings
1197
- `blocked_by` -> derived from inverse relations where relation type is `blocks`
1198
- `priority` -> integer only (non-integers become null)
1199
- `created_at` and `updated_at` -> parse ISO-8601 timestamps
1200

1201
### 11.4 Error Handling Contract
1202

1203
Recommended error categories:
1204

1205
- `unsupported_tracker_kind`
1206
- `missing_tracker_api_key`
1207
- `missing_tracker_project_slug`
1208
- `linear_api_request` (transport failures)
1209
- `linear_api_status` (non-200 HTTP)
1210
- `linear_graphql_errors`
1211
- `linear_unknown_payload`
1212
- `linear_missing_end_cursor` (pagination integrity error)
1213

1214
Orchestrator behavior on tracker errors:
1215

1216
- Candidate fetch failure: log and skip dispatch for this tick.
1217
- Running-state refresh failure: log and keep active workers running.
1218
- Startup terminal cleanup failure: log warning and continue startup.
1219

1220
### 11.5 Tracker Writes (Important Boundary)
1221

1222
Symphony does not require first-class tracker write APIs in the orchestrator.
1223

1224
- Ticket mutations (state transitions, comments, PR metadata) are typically handled by the coding
1225
agent using tools defined by the workflow prompt.
1226
- The service remains a scheduler/runner and tracker reader.
1227
- Workflow-specific success often means "reached the next handoff state" (for example
1228
`Human Review`) rather than tracker terminal state `Done`.
1229
- If the optional `linear_graphql` client-side tool extension is implemented, it is still part of
1230
the agent toolchain rather than orchestrator business logic.
1231

1232
## 12. Prompt Construction and Context Assembly
1233

1234
### 12.1 Inputs
1235

1236
Inputs to prompt rendering:
1237

1238
- `workflow.prompt_template`
1239
- normalized `issue` object
1240
- optional `attempt` integer (retry/continuation metadata)
1241

1242
### 12.2 Rendering Rules
1243

1244
- Render with strict variable checking.
1245
- Render with strict filter checking.
1246
- Convert issue object keys to strings for template compatibility.
1247
- Preserve nested arrays/maps (labels, blockers) so templates can iterate.
1248

1249
### 12.3 Retry/Continuation Semantics
1250

1251
`attempt` should be passed to the template because the workflow prompt may provide different
1252
instructions for:
1253

1254
- first run (`attempt` null or absent)
1255
- continuation run after a successful prior session
1256
- retry after error/timeout/stall
1257

1258
### 12.4 Failure Semantics
1259

1260
If prompt rendering fails:
1261

1262
- Fail the run attempt immediately.
1263
- Let the orchestrator treat it like any other worker failure and decide retry behavior.
1264

1265
## 13. Logging, Status, and Observability
1266

1267
### 13.1 Logging Conventions
1268

1269
Required context fields for issue-related logs:
1270

1271
- `issue_id`
1272
- `issue_identifier`
1273

1274
Required context for coding-agent session lifecycle logs:
1275

1276
- `session_id`
1277

1278
Message formatting requirements:
1279

1280
- Use stable `key=value` phrasing.
1281
- Include action outcome (`completed`, `failed`, `retrying`, etc.).
1282
- Include concise failure reason when present.
1283
- Avoid logging large raw payloads unless necessary.
1284

1285
### 13.2 Logging Outputs and Sinks
1286

1287
The spec does not prescribe where logs must go (stderr, file, remote sink, etc.).
1288

1289
Requirements:
1290

1291
- Operators must be able to see startup/validation/dispatch failures without attaching a debugger.
1292
- Implementations may write to one or more sinks.
1293
- If a configured log sink fails, the service should continue running when possible and emit an
1294
operator-visible warning through any remaining sink.
1295

1296
### 13.3 Runtime Snapshot / Monitoring Interface (Optional but Recommended)
1297

1298
If the implementation exposes a synchronous runtime snapshot (for dashboards or monitoring), it
1299
should return:
1300

1301
- `running` (list of running session rows)
1302
- each running row should include `turn_count`
1303
- `retrying` (list of retry queue rows)
1304
- `codex_totals`
1305
- `input_tokens`
1306
- `output_tokens`
1307
- `total_tokens`
1308
- `seconds_running` (aggregate runtime seconds as of snapshot time, including active sessions)
1309
- `rate_limits` (latest coding-agent rate limit payload, if available)
1310

1311
Recommended snapshot error modes:
1312

1313
- `timeout`
1314
- `unavailable`
1315

1316
### 13.4 Optional Human-Readable Status Surface
1317

1318
A human-readable status surface (terminal output, dashboard, etc.) is optional and
1319
implementation-defined.
1320

1321
If present, it should draw from orchestrator state/metrics only and must not be required for
1322
correctness.
1323

1324
### 13.5 Session Metrics and Token Accounting
1325

1326
Token accounting rules:
1327

1328
- Agent events may include token counts in multiple payload shapes.
1329
- Prefer absolute thread totals when available, such as:
1330
- `thread/tokenUsage/updated` payloads
1331
- `total_token_usage` within token-count wrapper events
1332
- Ignore delta-style payloads such as `last_token_usage` for dashboard/API totals.
1333
- Extract input/output/total token counts leniently from common field names within the selected
1334
payload.
1335
- For absolute totals, track deltas relative to last reported totals to avoid double-counting.
1336
- Do not treat generic `usage` maps as cumulative totals unless the event type defines them that
1337
way.
1338
- Accumulate aggregate totals in orchestrator state.
1339

1340
Runtime accounting:
1341

1342
- Runtime should be reported as a live aggregate at snapshot/render time.
1343
- Implementations may maintain a cumulative counter for ended sessions and add active-session
1344
elapsed time derived from `running` entries (for example `started_at`) when producing a
1345
snapshot/status view.
1346
- Add run duration seconds to the cumulative ended-session runtime when a session ends (normal exit
1347
or cancellation/termination).
1348
- Continuous background ticking of runtime totals is not required.
1349

1350
Rate-limit tracking:
1351

1352
- Track the latest rate-limit payload seen in any agent update.
1353
- Any human-readable presentation of rate-limit data is implementation-defined.
1354

1355
### 13.6 Humanized Agent Event Summaries (Optional)
1356

1357
Humanized summaries of raw agent protocol events are optional.
1358

1359
If implemented:
1360

1361
- Treat them as observability-only output.
1362
- Do not make orchestrator logic depend on humanized strings.
1363

حوالہ جاتی نفاذ Elixir میں لکھا گیا ہے—کیونکہ جب کوڈ مؤثر طور پر مفت ہو، تو آپ آخرکار زبانوں کا انتخاب ان کی خوبیوں کی بنیاد پر کر سکتے ہیں، جیسے Elixir کی بیک وقت عمل کاری—لیکن بنیادی خیال ایک سادہ Markdown دستاویز میں بیان کیا جا سکتا ہے۔ ہم آپ کو ترغیب دیتے ہیں کہ آپ اپنے پسندیدہ کوڈنگ ایجنٹ کو spec کی طرف متوجہ کریں اور اس سے اس کا اپنا ورژن نافذ کروائیں۔

Symphony کا پہلا ورژن بس tmux میں چلتا ہوا ایک Codex سیشن تھا جو Linear کو poll کر رہا تھا اور نئے کاموں کے لیے سب-ایجنٹس شروع کر رہا تھا۔ یہ کام کرتا تھا، لیکن خاص طور پر قابلِ اعتماد نہیں تھا۔ دوسرا ورژن ہمارے مرکزی پروجیکٹ ریپوزٹری کے اندر موجود تھا، جسے ایجنٹس کو مدنظر رکھ کر بنایا گیا تھا۔ ہم پہلے ہی اس ریپو میں ایجنٹس کو اعلیٰ معیار کا کام کرنے کے لیے مہارتیں اور سیاق و سباق فراہم کرنے والا ایجنٹ ہارنس تیار کر چکے تھے، لہٰذا Symphony بس ان سب کو آپس میں جوڑ دیتا ہے۔

جب بنیادی فنکشنلٹی موجود ہو گئی، تو ہم نے Symphony کو بنانے کے لیے Symphony کا ہی استعمال کیا۔

جب ہم نے اندرونی طور پر اس سسٹم کا ڈیمو پیش کیا، جو ٹاسکس کو منیج کر رہا تھا اور اپنی پاور آف ورک ویڈیو منسلک کر رہا تھا، تو ردِعمل بے حد مثبت تھا: ہمارے Symphony پروجیکٹ چینل میں اضافہ ہوا، اور ادارے بھر کی ٹیموں نے اسے فطری طور پر استعمال کرنا شروع کر دیا۔ OpenAI میں بیرونی لانچ سے پہلے اندرونی پروڈکٹ-مارکیٹ فٹ ایک لازمی شرط ہے۔ OpenAI میں نظر آنے والے استعمال کی بنیاد پر یہ واضح ہو گیا کہ ہمیں Symphony کو کمپنی کی حدود سے باہر بھی شیئر کرنا چاہیے۔

لہٰذا ہم نے اس خیال کو ایک علیحدہ SPEC.md میں منتقل کیا اور Codex سے اسے نافذ کرنے کو کہا۔ حوالہ جاتی نفاذ کے لیے ہم نے Elixir کا انتخاب کیا، جو نسبتاً کم معروف زبان ہے لیکن بیک وقت عمل کرنے والے پروسیسز کی ترتیب اور نگرانی کے لیے بہترین بنیادی سہولیات فراہم کرتی ہے۔Codex نے Elixir میں اس کا نفاذ ون-شاٹ میں تیار کر دیا، اور ہم نے اس کے بعد وضاحت اور نفاذ دونوں پر مسلسل بہتری کا عمل جاری رکھا۔ وضاحت کو بہتر بنانے کے لیے ہم نے Codex سے کہا کہ اسے کئی دیگر زبانوں—TypeScript, Go, Rust, Java, Python—میں بھی نافذ کرے، اور نتائج کو استعمال کر کے ابہام کی نشاندہی کریں اور سسٹم کو مزید سادہ بنائیں۔ یہ ہر زبان میں کامیاب رہا۔

Codex کی تیاری کے عمل کے دوران ہم نے بہت سی غیر ضروری پیچیدگیاں ختم کر دیں، جیسے مخصوص ریپوزٹریز یا Linear MCP پر انحصار۔ اب Symphony ہمارے اندرونی ریپوزٹریز یا ورک فلوز پر منحصر نہیں رہا۔ بنیادی طریقہ کار آسان ہو گیا ہے:

ہر اوپن ٹاسک کے لیے یقینی بنائیں کہ ایک ایجنٹ اپنے الگ ورک اسپیس میں چل رہا ہو۔

فعال کام میں مدد کے علاوہ، اب ڈیولپمنٹ ورک فلو بھی ایسی چیز بن چکا ہے جسے ایجنٹس جانتے ہیں اور اس پر عمل کرتے ہیں۔ ڈیولپمنٹ ورک فلو—جیسے کسی ایشو پر کام کرنا، ریپوزٹری چیک آؤٹ کرنا، اسے اِن پروگریس میں ڈالنا تاکہ PM کو معلوم ہو کہ اس پر کام ہو رہا ہے، PR شامل کرنا، اسے ریویو اسٹیٹس میں منتقل کرنا، ویڈیوز منسلک کرنا وغیرہ—اب ایک سمپل WORKFLOW.md فائل میں محفوظ ہے۔ یہ تمام عمل وہی ہیں جن پر انسان عمل کرتے تھے، مگر یہ کبھی دستاویزی شکل میں موجود نہیں تھے۔ اس غیر واضح مراحل کے بجائے اب ہم اسے واضح طور پر دستاویز کرتے ہیں، اور Symphony اس بات کو یقینی بناتا ہے کہ ایجنٹس اس پر عمل کریں۔ اس سے ہمیں ایسے ایجنٹس بنانے میں مدد ملتی ہے جو ہمارے ساتھ مل کر کام کرتے ہیں۔ اگر ہم یہ طے کریں کہ ایجنٹس مکمل کام کے ساتھ خود احتسابی بھی شامل کریں، تو ہم اسے WORKFLOW.md میں شامل کریں گے، اور Symphony ایجنٹس کو اس مرحلے تک رہنمائی فراہم کرے گا۔

ہمیں Codex کو ایپ سرور موڈ(نئی ونڈو میں کھلتا ہے) میں استعمال کرنے کا بھی موقع ملا، جو Codex کا ایک بلٹ اِن ہیڈ لیس موڈ ہے۔ اس موڈ نے ہمیں Codex کو چلانے اور اس کے ساتھ پروگراماتی طور پر بات چیت کرنے کی سہولت دی، ایک اچھی طرح دستاویزی JSON-RPC API کے ذریعے، جیسے تھریڈ شروع کرنا یا مختلف ٹرنز پر ردِعمل دینا۔ یہ طریقہ CLI یا لائیو tmux سیشنز کے ذریعے Codex کے ساتھ تعامل کرنے کے مقابلے میں کہیں زیادہ آسان اور قابلِ توسیع ہے۔

Codex App Server ہمارے استعمال کے لیے بہترین ثابت ہوا: ہم Codex کے فراہم کردہ ہارنس سے فائدہ اٹھاتے ہیں جبکہ اسے اپنی ضرورت کے مطابق جوڑنے کے لیے کنٹرولز اور ہکس بھی حاصل ہوتے ہیں۔ مثال کے طور پر، Linear کا ایکسیس ٹوکن سب ایجنٹس کے سامنے ظاہر ہونے سے بچانے کے لیے ہم ڈائنامک ٹول کالز(نئی ونڈو میں کھلتا ہے) استعمال کرتے ہیں، جس کے ذریعے ہم linear_graphql فنکشن کو ظاہر کرتے ہیں جو Linear پر براہِ راست درخواستوں کو چلانے کی اجازت دیتا ہے، بغیر MCP پر انحصار کیے یا ایکسیس ٹوکن کو کنٹینرز کے سامنے ظاہر کیے۔

آگے کیا ہے

Symphony ایک دانستہ طور پر سادہ آرکیسٹریشن لیئر ہے۔ ہم اسے اوپن سورس کر رہے ہیں تاکہ یہ دکھایا جا سکے کہ Codex App Server مختلف ورک فلو ٹولز، جیسے Linear، کے ساتھ مل کر کتنی طاقت فراہم کرتا ہے۔ اسی لیے ہم Symphony کو ایک الگ پروڈکٹ کے طور پر برقرار رکھنے کا ارادہ نہیں رکھتے۔ اسے ایک حوالہ جاتی نفاذ کے طور پر دیکھیں۔ جس طرح بہت سے ڈویلپرز نے ہارنس انجینئرنگ والی پوسٹ کو بنیاد بنا کر اپنی ریپوزٹریز تیار کیں، ہم امید کرتے ہیں کہ آپ بھی اپنے پسندیدہ کوڈنگ ایجنٹ کو Symphony کی وضاحت(نئی ونڈو میں کھلتا ہے) اور ریپوزٹری(نئی ونڈو میں کھلتا ہے) کی طرف رہنمائی کریں گے تاکہ آپ اپنے ماحول کے مطابق اپنی ورژنز تیار کر سکیں۔

اصل طاقت Codex اور اس کے ایپ سرور سے آتی ہے۔ Symphony دراصل Codex اور Linear، دو ایسے ٹولز جنہیں ہم پہلے ہی استعمال کر رہے تھے، کو آپس میں جوڑنے کا ایک طریقہ تھا تاکہ کام کے سسٹم و نسق کے مسئلے کو حل کیا جا سکے۔ جیسے جیسے کوڈنگ ایجنٹس استدلال کرنے اور ہدایات پر عمل کرنے میں بہتر ہوتے جا رہے ہیں، ہمیں اندازہ ہے کہ دیگر کمپنیوں میں رکاوٹ کوڈ لکھنے سے ہٹ کر ایجنٹک کام کے نظم و نسق کی طرف منتقل ہو جائے گی۔ دلچسپ بات یہ ہے کہ اب ان کوڈنگ ایجنٹ سسٹمز کے ساتھ تجربہ کرنے کی رکاوٹ حیرت انگیز طور پر کم ہو گئی ہے۔ آپ بس Codex کے ساتھ چیزیں بنانا شروع کر سکتے ہیں۔

کمیونٹی کی جانب سے ستائش

ریلیز کے بعد سے گزشتہ ہفتوں میں انجینئرنگ کمیونٹی کو Symphony استعمال کرتے دیکھ کر ہمیں بہت خوشی ہوئی ہے، اور 23 اپریل تک اسے GitHub پر 15 ہزار سے زائد اسٹارز(نئی ونڈو میں کھلتا ہے) مل چکے ہیں۔