दस एजेंट्स ने एक ही टाइपचेक एक साथ चलाया. हल एक फ़ोल्डर निकला.

एक ही checkout में सत्रह कोडिंग एजेंट, एक साथ दस tsc प्रोसेस, load average 37 और सिर्फ़ 87 MB खाली RAM. नब्बे सेकंड का टाइपचेक 7 मिनट 36 सेकंड ले गया. यहाँ है वह माप, मशीन क्यों गणना नहीं कर रही थी, और वह छोटा साझा लॉक जिसने मामला सुलझा दिया. किसी भी रिपॉज़िटरी में कॉपी करने लायक.

7 सितंबर को हमारी डेवलपमेंट मशीन ने ढंग से जवाब देना बंद कर दिया. न क्रैश, न फ़्रीज़. बस हर चीज़ दस गुना ज़्यादा समय लेने लगी, उन चीज़ों समेत जिनका कोड से कोई लेना-देना नहीं था.

जो शक सबसे पहले होते हैं, वे सब गलत निकले. लैपटॉप गरम नहीं हो रहा था: कोई throttling दर्ज नहीं, बैटरी 30.6 C पर. कोई बेकाबू प्रोसेस CPU नहीं खा रहा था. कुछ डिप्लॉय नहीं हुआ था. असामान्य बस इतना था कि एक ही रिपॉज़िटरी में सत्रह एजेंट CLI ज़िंदा थे, जो हमारे यहाँ एक सामान्य कामकाजी दिन है.

असल में जो हो रहा था, वह यह है, अंदाज़े से नहीं, माप से.

माप

16 GB, 8 कोर वाली मशीन, साढ़े पाँच घंटे से चालू, सत्रह एजेंट काम पर:

हमने क्या मापामान
ज़िंदा एजेंट CLI17
एक साथ चलते tsc --noEmit प्रोसेस, दो मिनट के भीतर देखे गए3, फिर 10
Load average37 से 41
खाली RAM / मेमोरी कंप्रेसर87 MB / 7.2 GB
डेस्कटॉप ऐप पर एक टाइपचेक, भरी हुई मशीन26 s CPU के लिए 7 min 36 दीवार-घड़ी
वही टाइपचेक, शांत मशीन33 s

निर्णायक पंक्ति दूसरी आख़िरी है. छब्बीस सेकंड का CPU साढ़े सात मिनट में फैला हो तो यह दस प्रतिशत उपयोग है. टाइपचेक गणना नहीं कर रहा था. वह मेमोरी का इंतज़ार कर रहा था.

और उनमें से एक प्रोसेस को ऑपरेटिंग सिस्टम ने बीच में मार दिया. मारा गया tsc गैर-शून्य कोड और खाली आउटपुट के साथ बाहर आता है, जिसे असली टाइप एरर से अलग पहचाना नहीं जा सकता. यानी धीमी होने के अलावा मशीन ऐसे फ़ैसले भी सुना रही थी जिन पर कोई भरोसा नहीं कर सकता था.

किसी ने कुछ गलत नहीं किया

यही हिस्सा ठहरकर देखने लायक है, क्योंकि यही वजह है कि इस खराबी को आते हुए देखना इतना मुश्किल है.

उनमें से हर एजेंट नियम का पालन कर रहा था. हर एक ने TypeScript एडिट किया था. हर एक को कहा गया था कि काम लौटाने से पहले अपने टाइप जाँच ले. हर एक ने tsc --noEmit चलाया. किसी को दूसरे दिख नहीं रहे थे. ऐसा कोई साझा बोर्ड नहीं है जहाँ एजेंट लिख सके कि मैं अभी महँगा काम कर रहा हूँ, थोड़ा रुको.

फिर यह खुद को खिलाने लगता है. मशीन भरी होने की वजह से टाइपचेक धीमा होता है. उसे देख रहा एजेंट मान लेता है कि वह अटक गया है. तो वह उसे मारता है और नया शुरू कर देता है. यह प्रतिक्रिया अकेले में सही है और समूह में विनाशकारी, और यह खराबी उसी परिवार की है जो हमने एक महीने पहले दर्ज की थी, जब एजेंट अपने पीछे अटकी हुई सर्च प्रोसेस छोड़ जाते थे: Process Guard वह जाल है जो शुरू हो चुकी चीज़ों को ढूँढ़ता है, यहाँ हम शुरू होने से ही रोकते हैं.

तीन जवाब जो हमने नहीं चुने

कम एजेंट चलाओ. इससे लक्षण आधा होता है और बग बना रहता है. भरी हुई मशीन पर दो एक साथ चलते टाइपचेक अब भी एक से धीमे हैं, और बेड़ा घटाना यानी समस्या की कीमत उसी चीज़ से चुकाना जो काम को तेज़ बनाती है.

आख़िर में एक ही टाइपचेक. लुभावना, और गलत, ऐसी वजह से जिसका परफ़ॉर्मेंस से कोई लेना-देना नहीं. दस टिकट बाद मिली टाइप एरर अनाथ होती है: जिस एजेंट ने उसे लिखा था वह बंद हो चुका है, उसका संदर्भ जा चुका है, और एक लाइन ठीक करने के लिए किसी इंसान को पूरा विषय दोबारा खोलना पड़ता है. हम जाँच को टालना नहीं चाहते थे.

इंक्रीमेंटल कंपाइलेशन. आज़माया और छोड़ दिया. --noEmit मोड में फ़ायदा संदिग्ध है, और साथ-साथ चलती प्रोसेस साझा .tsbuildinfo को खराब कर देती हैं. यह आधी समस्या हल करता है, दूसरी आधी बिगाड़कर.

इसके बजाय हमने क्या किया: एक जाँच, साझा

नियम यह नहीं है कि कम बार जाँचो. नियम है एक समय पर एक टाइपचेक, हर प्रोजेक्ट के लिए, सबके लिए. एक रैपर स्क्रिप्ट N जाँचों की जगह एक ले आती है, और तीन स्थितियों का जवाब देती है:

  1. पिछले रन के बाद कुछ नहीं बदला, तो उसी का नतीजा लौटा दो.
  2. एक रन पहले से चल रहा है, तो उसका इंतज़ार करो और उसका नतीजा ले लो.
  3. वरना, लॉक ले लो और मशीन पर अकेला tsc बनो.

एजेंट के नज़रिए से कुछ नहीं बदला: वह yarn typecheck टाइप करता है, उसे उसकी टाइप एरर मिलती हैं. वह पहले से ज़्यादा इंतज़ार भी कभी नहीं करता, क्योंकि जिस रन के पीछे वह लगा है वह उसके अपने रन से पहले शुरू हुआ था. मशीन दस के बजाय एक की कीमत चुकाती है.

पूरा विचार यही है. दिलचस्प बात यह है कि इसे जिन दो तंत्रों की ज़रूरत है, वे आपकी उम्मीद से कहीं छोटे हैं.

लॉक एक फ़ोल्डर है

फ़ाइल नहीं, डेटाबेस नहीं, डीमन नहीं. एक फ़ोल्डर.

try {
  mkdirSync(lockDir);       // सफल: लॉक हमारा है
} catch (err) {
  if (err.code === 'EEXIST') { /* किसी और के पास है, इंतज़ार करते हैं */ }
}

mkdir या तो फ़ोल्डर बनाता है या EEXIST के साथ नाकाम होता है, और यह वह परमाणु रूप से करता है, macOS, Windows और Linux पर, बिना डिपेंडेंसी और बिना नेटिव कॉल के. फ़ाइल लिखना और फिर देखना कि वह मौजूद है या नहीं, दो ऑपरेशन होते, और दो ऑपरेशन ठीक वही जगह है जहाँ दूसरा एजेंट बीच में घुस आता है.

फ़ोल्डर के भीतर हम एक owner.json रखते हैं जिसमें pid, होस्टनेम और शुरू होने का समय होता है. वह फ़ाइल निदान के लिए है और मरा हुआ लॉक पहचानने के लिए. बहिष्करण का काम वह कभी नहीं करती.

मरा हुआ लॉक दो स्थितियों में अपने आप वापस ले लिया जाता है: मालिक प्रोसेस गायब हो गई हो (यह तभी जाँचा जाता है जब होस्टनेम मेल खाता हो, क्योंकि एक मशीन से दूसरी पर pid का कोई मतलब नहीं), या लॉक पंद्रह मिनट से पुराना हो.

एक जाल जिसने हमें एक बग की कीमत चुकाई. mkdir और owner.json लिखे जाने के बीच एक खिड़की होती है जिसमें मालिक पढ़ा नहीं जा सकता. उस खिड़की में लॉक को मरा हुआ घोषित करना यानी उसे उसी से छीन लेना जिसने अभी-अभी लिया है, ठीक वही रेस जिसे रोकने के लिए वह फ़ाइल है. इसलिए जब कोई मालिक पढ़ा न जा सके, तो हम फ़ोल्डर की उम्र पर फ़ैसला करते हैं, गायब फ़ाइल पर नहीं.

फ़िंगरप्रिंट एक तारीख़ और एक गिनती है

पहली स्थिति को जानना होता है कि पिछले रन के बाद कुछ बदला या नहीं. सीधा जवाब होता सोर्स फ़ाइलों का हैश निकालना. हम ऐसा नहीं करते.

फ़िंगरप्रिंट है <सबसे नई mtime>:<फ़ाइलों की संख्या>, tsconfig के include से निकली जड़ों पर, साथ में खुद tsconfig.

2,300 फ़ाइलों पर हर बाइट पढ़ना उस जाँच से महँगा पड़ता है जिसे हम बचा रहे हैं. अकेली तारीख़ फ़ाइल हटने को नहीं पकड़ती. अकेली गिनती एडिट को नहीं पकड़ती. दोनों मिलकर दोनों को ढक लेती हैं. जो झूठा नकारात्मक हमने स्वीकार किया है वह है एक ही मिलीसेकंड में दो एडिट जो गिनती वही छोड़ जाएँ, और उसमें सबसे बुरा नतीजा कुछ सेकंड पुराना कैश्ड परिणाम है, कभी कोई चुपचाप छूटी टाइप एरर नहीं, क्योंकि रोकने वाली जाँच अब भी बिल्ड की जाँच है.

लिखा हुआ नियम काफ़ी नहीं था, हमने hook जोड़ा

निर्देश पहले दिन से AGENTS.md में था: tsc कभी सीधे मत चलाओ, हमेशा साझा स्क्रिप्ट. वह काफ़ी नहीं रहा, और ईमानदारी से यह कहना ज़रूरी है कि क्यों.

एडिट के बाद टाइप जाँच लेना गहरे बैठा हुआ रिफ़्लेक्स है. दबाव में एजेंट निर्देश दोबारा पढ़े बिना npx tsc --noEmit टाइप कर देता है. और उस भीड़ को दोबारा खड़ा करने के लिए, जिसे रोकने के लिए लॉक बना है, एक ही एजेंट का नियम तोड़ना काफ़ी है. निर्देश पर बहस हो सकती है. hook पर नहीं.

तो हमने Bash टूल पर एक PreToolUse hook लगाया, जो सीधा tsc मना करता है और मना करते हुए सही कमांड का नाम भी बता देता है:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "node \"$CLAUDE_PROJECT_DIR/scripts/hooks/block-direct-tsc.mjs\"" }
        ]
      }
    ]
  }
}

hook टूल कॉल को stdin पर JSON के रूप में पढ़ता है, मना करने के लिए stderr पर वजह के साथ 2 से बाहर निकलता है, और जाने देने के लिए 0 से. दो बारीकियाँ काम के hook और चिढ़ पैदा करने वाले hook के बीच फ़र्क़ बनाती हैं.

यह tsc को कमांड की जगह पर पहचानता है, स्ट्रिंग में कहीं भी नहीं. तीन अक्षरों को कहीं भी ढूँढ़ना grep -rn tsc AGENTS.md को भी मना कर देता. इसलिए पैटर्न माँगता है कि tsc पंक्ति की शुरुआत में हो या ;, &&, ||, | या ( के बाद, और उससे पहले चाहे तो कोई पैकेज रनर और एक पाथ हो. यह tsc --version को जाने भी देता है: जानकारी देने वाले फ़्लैग को मना करने की कोई वजह नहीं.

यह एक दूसरी चीज़ भी मना करता है जिसका हमें अंदाज़ा नहीं था. हमने एक एजेंट को लॉक के पीछे इंतज़ार करते देखा, फिर कुछ देर बाद यह तय करते देखा कि लॉक ज़रूर मरा हुआ होगा, और खुद को खोलने के लिए लॉक फ़ोल्डर हटाते देखा. इससे ज़िंदा प्रोसेस के बगल में दूसरा भारी प्रोसेस शुरू हो जाता है, यानी लॉक जो कुछ बचाता है उस सब का बेहतरीन बाइपास. इसलिए लॉक या कैश फ़ोल्डर हटाना भी मना है, इस समझाइश के साथ कि मरा हुआ लॉक अपने आप वापस ले लिया जाता है.

वह दूसरी रोक हम पहले से कभी न लिखते. वह इस बात को देखने से आई कि अटकने पर एजेंट असल में करते क्या हैं, और यह कल्पना करने से बेहतर स्रोत है कि वे क्या कर सकते हैं.

यह कहाँ तक जाता है

hook Claude Code का ख़ास है. बेड़े के बाकी एजेंट CLI को सिर्फ़ लिखा हुआ नियम दिखता है. यह एक ज्ञात छेद है और हमने इसे स्वीकार किया है: ऐसे hook मानक के इंतज़ार में, जिसे हर CLI पढ़े, बेड़े के बड़े हिस्से को ढकने वाली रोक कोई रोक न होने से बेहतर है.

साझा स्क्रिप्ट खुद प्रोवाइडर-निरपेक्ष है, क्योंकि वह बस एक कमांड है. जो भी CLI yarn typecheck चला सकता है उसे लॉक का फ़ायदा मिलता है, चाहे कोई उसे मजबूर करे या न करे.

इससे क्या लेकर जाना है

टाइपचेक हमारा सबसे शोर मचाने वाला मामला था, कोई ख़ास मामला नहीं. यह पैटर्न हर उस कमांड पर लागू होता है जो महँगी हो, छोटी अवधि में दोहराने पर वही नतीजा दे, और हर एजेंट उसी अच्छी वजह से चलाता हो: डिपेंडेंसी इंस्टॉल करना, पूरा टेस्ट सूट चलाना, प्रोडक्शन के लिए बिल्ड करना, तय पोर्ट पर डेव सर्वर शुरू करना.

तीन सवाल, इसी क्रम में, और पूरा डिज़ाइन आपके पास है:

  1. क्या मैं हाल का कोई नतीजा दोबारा इस्तेमाल कर सकता हूँ?
  2. क्या मैं पहले से चल रहे रन में शामिल हो सकता हूँ?
  3. वरना, क्या मैं ही वह हूँ जो इसे अकेले शुरू करता है?

अगर कई एजेंट तुम्हारी मशीन साझा करते हैं, तो मापने लायक चीज़ यह नहीं है कि कितने चल रहे हैं. यह है कि उनमें से कितने एक ही मिनट में एक ही कमांड शुरू करते हैं. तुम्हारी मशीन असल में वही संख्या महसूस करती है, और जब तक तुम उसे नहीं देखोगे, तुम गर्मी को दोष देते रहोगे.

अगर तुम यह पूरी तस्वीर चाहते हो कि हम एक ही रिपॉज़िटरी पर कई एजेंट कैसे चलाते हैं बिना उनके एक-दूसरे पर चढ़े, तो वह कोडिंग एजेंट्स को समानांतर में चलाना में है, और जो प्रोसेस सचमुच शुरू हो चुकी हैं उनके लिए सुरक्षा जाल है Process Guard. साझा स्क्रिप्ट और hook दोनों AgentsRoom की रिपॉज़िटरी में रहते हैं, यानी वहीं जहाँ उस दोपहर वे सत्रह एजेंट काम कर रहे थे.

AgentsRoom डाउनलोड करें

अपने सभी AI एजेंट्स को, अपने सभी प्रोजेक्ट्स पर, एक ही विंडो से चलाएं।

मुफ़्तAgentsRoom डाउनलोड करें

कंपेनियन ऐप: चलते-फिरते अपने एजेंट्स मॉनिटर करें

Claude, Codex, Antigravity CLI या किसी अन्य AI प्रदाता का उपयोग करें।

एक्सटेंशन इंस्टॉल करें
Chrome Web Store

बग और अनुरोध सीधे अपने सार्वजनिक बैकलॉग में भेजें।

AgentsRoom को कार्य करते देखें।

मल्टी-प्रोजेक्ट
मल्टी-प्रोवाइडर
मल्टी-एजेंट
लाइव स्टेटस
फाइल डिफ और कमिट
मोबाइल ऐप
लाइव प्रीव्यू
एजेंट टीमें
ब्राउज़र ऑटोमेशन
बैकलॉग-संचालित डेव
प्रॉम्प्ट लाइब्रेरी
स्किल्स लाइब्रेरी
सभी सुविधाएँ देखें

पढ़ते रहें