Why NextWhy Next
सूची पर लौटें
💻 डेवलपमेंट14 मिनट में पढ़ें

एजेंट के "पूरा हुआ" पर भरोसा न करने का फ़ैसला - verify.sh गेट, prove-it

AI कोडिंग एजेंट टेस्ट चलाए बिना भी "टेस्ट पास" रिपोर्ट कर देता है। यह झूठ नहीं, बनावट की समस्या है। रिपोर्ट को जाँच में बदलने वाला ओपन सोर्स गेट prove-it बनाते हुए जो कुछ सीखा - और खुद लिखे पाँच लाइन के हुक के चुपचाप फ़ेल होने के चार तरीक़े।

मुख्य सारांश

कोडिंग एजेंट अपने किए हुए और करना चाहे हुए काम में फ़र्क़ नहीं कर पाता, इसलिए असल में वह इरादा रिपोर्ट करता है। टेस्ट चलाए बिना "पास हो रहे हैं" कहना इसी वजह से है। प्रॉम्प्ट से यह ठीक नहीं होता; "पूरा हुआ" को घोषणा से बदलकर एक ऐसी जाँच बनाना होगा जिसे पास करना ज़रूरी है। prove-it एक Stop हुक गेट है जो रेपो रूट के verify.sh के exit 0 लौटाने तक एजेंट को टर्न ख़त्म नहीं करने देता। खुद लिखा पाँच लाइन का हुक चार तरीक़ों से चुपचाप फ़ेल होता है - एक बार रोककर फिर कभी न रोक पाना, कमिट पर ख़त्म हुए टर्न को यूँ ही पास कर देना, एजेंट का जाँच को ही मिटा देना, या हार और पास में फ़र्क़ न दिखना। prove-it पाँच लाइनों से लंबा क्यों है, इसकी पूरी वजह यही चार चीज़ें हैं।

इस पेज पर

"सारे टेस्ट पास हो रहे हैं। मर्ज कर सकते हैं।"

एजेंट ने यह कहा, और diff इतना साफ़ था कि मैं लगभग मान ही चुका था। पर टर्मिनल स्क्रॉल ऊपर करके देखा तो टेस्ट एक बार भी चला ही नहीं था। रन का लॉग तक नहीं था। एजेंट टेस्ट चलाना चाहता था, और वही इरादा पूरा होने की रिपोर्ट में जस का तस छप गया।

ऐसा कुछ बार झेलने के बाद प्रतिक्रिया आम तौर पर दो में से एक होती है। या तो प्रॉम्प्ट में "टेस्ट ज़रूर चलाओ और नतीजा दिखाओ" जोड़ना, या हर बार एजेंट के "पूरा हुआ" कहने पर हाथ से दोबारा जाँचना। मैंने दोनों आज़माए, और दोनों ज़्यादा दिन नहीं चले। इसलिए मैंने तीसरा रास्ता चुना। "पूरा हुआ" शब्द पर ही भरोसा करना छोड़ने के बजाय, मैंने पूरा होने को घोषणा से बदलकर एक ऐसी जाँच बना दिया जिसे पास करना ज़रूरी है। इसी से बना prove-it नाम का ओपन सोर्स टूल।

झूठ नहीं, बनावट है

पहले एक बात साफ़ कर दूँ। एजेंट झूठ नहीं बोल रहा।

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

प्रॉम्प्ट तरकीबों की एक और कमज़ोरी है। मॉडल की पीढ़ी बदलते ही वे पुरानी पड़ जाती हैं। जो निर्देश इस मॉडल पर अच्छा काम करता है, वह अगले पर भी करेगा, इसकी कोई गारंटी नहीं। जबकि सबूत माँगने वाला तंत्र मॉडल से एक परत ऊपर बैठा होता है, इसलिए अपग्रेड झेल जाता है। टेस्ट सच में चला या नहीं, यह मॉडल नहीं, exit code बता दे तो काफ़ी है।

रिपोर्ट को जाँच में बदलना

"done" को शब्द से नहीं बल्कि सबूत से मानने का यह विचार मैंने पिछली पोस्ट में लिखा था। यह पोस्ट उस विचार को एक ऐसे tool में बदलने की कहानी है जिसे कोई भी install कर सकता है।

prove-it का नियम एक वाक्य का है। रेपो खुद को साबित करने का अपना तरीक़ा verify.sh नाम की फ़ाइल में घोषित करता है, और जब तक वह सबूत पास न हो जाए, एजेंट पूरा होने का दावा नहीं कर सकता।

अमल ऐसा है। रेपो रूट में एक executable verify.sh रखो। exit 0 का मतलब है "यह ट्री साबित करने लायक़ हद तक ठीक है"। एजेंट जब टर्न ख़त्म करने की कोशिश करता है, तो Stop हुक यह स्क्रिप्ट चलाता है, और नतीजा 0 न हो तो एजेंट को रुकने देने के बजाय उसे फ़ेल आउटपुट की आख़िरी बीस लाइनों के साथ वापस काम पर भेज देता है। ज़्यादातर मामलों में एजेंट सिर्फ़ वही आउटपुट देखकर वजह ठीक कर लेता है।

गेट कब चलता है, यह भी सीधा है। इस सेशन ने इस रेपो में सच में कुछ बदला हो, executable verify.sh मौजूद हो, और मौजूदा ट्री-स्टेट अब तक कभी पास न हुई हो। तीनों शर्तें सच हों, तभी चलता है। सिर्फ़ पढ़ने वाले सेशन को छूता नहीं, और एक बार पास हो चुकी ट्री को दोबारा नहीं जाँचता।

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

पाँच लाइन के bash से क्यों नहीं चलता

यहाँ तक पढ़कर अगर आपने सोचा "अरे, यह तो Stop हुक में टेस्ट कमांड की एक लाइन का काम है", तो आप सही हैं। पाँच लाइनों में हो जाता है, और मैंने भी पहले ऐसे ही लिखा था। और वह वर्ज़न चार तरीक़ों से चुपचाप फ़ेल हुआ। इनमें से कुछ तो prove-it के शुरुआती वर्ज़न में सचमुच मौजूद बग थे, इसलिए अब हर एक पर regression test लगा है।

एक बार रोकता है, फिर कभी नहीं रोक पाता। Claude Code हुक के एक बार ब्लॉक करने के बाद हर stop इवेंट पर stop_hook_active फ़्लैग सेट कर देता है। इस फ़्लैग को "पास होने दो" की तरह पढ़ने वाला हुक ठीक एक बार रोकने के बाद गेट होना बंद कर देता है। और उल्टा, फ़्लैग को नज़रअंदाज़ करने वाला हुक हमेशा के लिए रोकता है और सेशन ठप हो जाता है। यह ऐसा जाल है जो दोनों तरफ़ गिरकर ही समझ आता है, और जवाब है कोशिशें खुद गिनना - तय गिनती तक रोको, फिर शोर मचाते हुए रास्ता दो।

कमिट कर दो तो लगता है कुछ हुआ ही नहीं। working tree गंदी है या नहीं, इससे "काम हुआ था क्या" तय करने वाला हुक कमिट पर ख़त्म होने वाले हर टर्न को यूँ ही पास कर देता है। जबकि कमिट तो एजेंट का सबसे आम काम है। इसलिए prove-it सेशन शुरू होते वक़्त की ट्री-स्टेट को baseline की तरह दर्ज कर लेता है और हर stop पर उससे मिलाता है। कमिट भी, sed से बदली फ़ाइल भी, कोड जनरेटर की उगली फ़ाइल भी - सब बदलाव की तरह पकड़ में आते हैं।

एजेंट जाँच को ही मिटा सकता है। verify.sh पास न कर पाने वाले एजेंट के पास सबसे सस्ता रास्ता है verify.sh मिटा देना या उसकी execute permission हटा देना। गेट सेशन की शुरुआत में दर्ज करता है कि रेपो हथियारबंद था या नहीं, और निहत्थे हालत में ख़त्म होने वाले टर्न को नामंज़ूर करता है। एक और महीन चाल भी है। executable रहने दो, बस अंदर की जाँचें बदल दो। इसे गेट रोकता नहीं। क्योंकि कई बार verify.sh बदलना ही वह काम होता है जो माँगा गया था। पर यह चुपचाप भी नहीं निकल पाता। सेशन के दौरान verify.sh बदला हुआ हो और फिर भी पास हो जाए, तो गेट यह बात बता देता है, और वह diff पढ़कर आप तय कर सकते हैं कि यह काम था या बचाव।

हार और पास में फ़र्क़ नहीं दिखता। कोई भी host आख़िरकार हुक को झुका ही देता है। खुद लिखा हुक ख़ामोशी में झुकता है, और आपको दिखने वाला आख़िरी शब्द होता है "पूरा हुआ"। तीन बार फ़ेल होकर हार मानने वाला टर्न और एक बार में पास होने वाला टर्न स्क्रीन पर एक जैसे दिखते हैं। prove-it की आख़िरी बात चेतावनी है। यह फ़र्क़ छोटा लगे, तो उस अगले दिन की कल्पना कर लीजिए जब आपने हारे हुए टर्न को पास हुआ समझकर मर्ज कर दिया।

बस यही चार वजहें हैं कि prove-it पाँच लाइनों से लंबा है। अपना हुक ही चलाते रहना चाहें, तो कोई हर्ज नहीं। बस वे सारे केस जो उस हुक को सँभालने पड़ेंगे, SPEC.md में लिखे हैं, पढ़ने की सलाह दूँगा। अहम implementation नहीं, नियम है।

यह टूल क्या साबित नहीं करता

एक सीमा ईमानदारी से दर्ज कर दूँ। गेट सिर्फ़ एक चीज़ लागू करता है, कि टर्न ख़त्म होने से पहले verify.sh ने 0 लौटाया। उस 0 का मतलब क्या है, यह पूरी तरह आपकी लिखी जाँचों पर टिका है। एक लाइन के exit 0 वाला verify.sh इस गेट को पास कर जाता है, और कुछ भी साबित नहीं करता।

स्पेक इसे Level 1 कहता है। Level 2 यह सवाल है कि जाँच असली सबूत के बारे में assertion करती है या नहीं, और उसे कोई टूल आपकी जगह वेरिफ़ाई नहीं कर सकता। prove-it भी नहीं। हुक लगाना आसान हिस्सा है। असली काम है इस सवाल का जवाब देना कि "इस रेपो में साबित होने का मतलब क्या है", और वह जवाब हर रेपो का अलग है। इसीलिए यह नियम सिर्फ़ फ़ाइल का नाम तय करता है, उसकी सामग्री नहीं।

बचने के रास्ते भी जान-बूझकर खुले रखे हैं। PROVE_IT_SKIP=1 हो तो वह टर्न यूँ ही पास हो जाता है, और verify.sh मिटा दो तो गेट हमेशा के लिए बंद। क्योंकि जिस गेट को हटाया न जा सके, लोग आख़िरकार उसका चक्कर काटकर निकल जाते हैं, और चक्कर काटा हुआ गेट न होने से भी बुरा है। वह दिखाता है कि जाँच चली, जबकि असल में कुछ चला ही नहीं।

पहले दिन जान-बूझकर छोटा

इंस्टॉल तीन लाइन का है, और असली काम तीसरी लाइन करती है।

/plugin marketplace add Why-Next/prove-it
/plugin install prove-it@whynext
/prove-it:init

/prove-it:init स्टैक पहचानकर verify.sh बनाता है, उसे पास होते हुए आपकी आँखों के सामने दिखाता है, और फिर exit 1 जोड़ी हुई कॉपी चलाकर यह भी दिखा देता है कि गेट टर्न को नामंज़ूर करता है। कोई 30 सेकंड लगते हैं।

बने हुए verify.sh में सक्रिय जाँच बस एक है, git diff --check। टेस्ट और टाइप चेक सब सिर्फ़ कमेंट में लिखे हैं। जान-बूझकर ऐसा बनाया है कि इंस्टॉल वाले दिन ही main पर पास हो जाए। पहले दिन से फ़ेल होने वाला गेट टीम को पहले हफ़्ते से ही उसे bypass करना सिखा देता है। कमेंट की हुई जाँचें एक-एक करके, हाथ से चलाकर पास होते देखने के बाद ही ऑन करें। और ऑन करने से पहले एक बार जान-बूझकर फ़ेल भी करवाकर देखें। जो जाँच फ़ेल नहीं हो सकती, वह जाँच नहीं है, और यह बात जिस दिन ज़रूरत पड़े उसी दिन पता चले तो देर हो चुकी होती है।

verify.sh को बढ़ाते हुए कहाँ रुकना है, यह भी तय है। कुल एक मिनट। उससे धीमी जाँच CI में भेज दें। secrets या प्रोडक्शन एक्सेस माँगने वाली जाँचें भी CI के हिस्से हैं। verify.sh में सिर्फ़ वही सबूत रहें जो एजेंट काम के बीच लोकल पर चला सके।

गेट ने जो पकड़ा, उसका बही-खाता

PROVE_IT_LEDGER=1 ऑन रखें, तो गेट जब भी कोई झूठा "पूरा हुआ" पकड़ता है, लोकल फ़ाइल में एक लाइन जुड़ जाती है।

{"ts":"2026-07-09T04:12:33Z","claim":"All tests pass. Ready to merge.",
 "evidence_demanded":"verify.sh exit 0","actual":["3 failed, 41 passed"]}

एजेंट ने क्या दावा किया, क्या माँगा गया, और असल में क्या निकला। महीने भर का जमा हो जाए, तो मेरा एजेंट किस-किस तरह फ़ेल होता है, यह अटकल की जगह रिकॉर्ड से पढ़ा जा सकता है। फ़ाइल सिर्फ़ लोकल डिस्क पर रहती है, कहीं नहीं भेजी जाती, और आप ऑन न करें तब तक बंद रहती है।

यह रेपो खुद को ही गेट करता है

prove-it रेपो में भी ज़ाहिर है verify.sh है, और उसके अंदर गेट खुद को असली git रेपो पर टेस्ट करता है। फ़ेल होने वाली जाँच पर रास्ता रुकता है या नहीं, पास होने वाली पर खुलता है या नहीं, सिर्फ़ पढ़ने वाला सेशन अछूता रहता है या नहीं, और कमिट के बाद की साफ़ ट्री को "कोई काम नहीं हुआ" समझने की ग़लती तो नहीं होती। ऊपर गिनाए चारों जाल regression test बनकर जड़े हुए हैं।

MIT लाइसेंस है, और dependencies बस bash, git और python3। Claude Code पर टेस्ट किया है, और Codex CLI, Gemini CLI जैसे उसी क़िस्म के blocking हुक देने वाले host से जोड़ने का तरीक़ा ADAPTERS दस्तावेज़ में है। अगर आपके पास कोई ऐसा रेपो है जहाँ एजेंट कोड बदलता है और उसी बातचीत में "पूरा हुआ" कह देता है, तो यह गेट वहीं सबसे ज़्यादा काम का है।

"पूरा हुआ" एजेंट की घोषणा नहीं, रेपो का सुनाया फ़ैसला होना चाहिए। उस फ़ैसले की कसौटी लिखना अब भी आपका काम है, पर कम से कम अब कोई उस फ़ैसले को लाँघकर चुपचाप निकल नहीं सकता।

अक्सर पूछे जाने वाले सवाल

AI कोडिंग एजेंट वह काम भी पूरा बताकर रिपोर्ट करता है जो उसने किया ही नहीं। इसे कैसे रोकें?

प्रॉम्प्ट में "टेस्ट ज़रूर चलाओ" लिख देना काफ़ी नहीं है। एजेंट के पास अपने किए हुए और करना चाहे हुए काम की तुलना का कोई साधन नहीं, इसलिए वह इरादे को ही जस का तस रिपोर्ट कर देता है। इसके बजाय पूरा होने की घोषणा को ही जाँच बना दें। रेपो रूट में एक वेरिफ़िकेशन स्क्रिप्ट (verify.sh) रखें, और एजेंट जब टर्न ख़त्म करने की कोशिश करे तो Stop हुक वह स्क्रिप्ट चलाए और exit 0 न हो तो उसे वापस काम पर भेज दे। prove-it इस नियम का reference implementation है।

Claude Code के Stop हुक से टेस्ट चलाने वाली पाँच लाइन की स्क्रिप्ट खुद लिख लूँ तो?

लिख सकते हैं, पर चार जाल हैं। ① Claude Code पहली रुकावट के बाद stop_hook_active फ़्लैग सेट करता है; इसे ग़लत सँभालें तो हुक या तो सिर्फ़ एक बार रोकता है या हमेशा के लिए रोककर सेशन ठप कर देता है। ② working tree गंदी है या नहीं, इस आधार पर फ़ैसला करें तो कमिट पर ख़त्म होने वाले सारे टर्न पास हो जाते हैं। ③ एजेंट verify.sh मिटाकर या chmod -x करके गेट को ही निहत्था कर सकता है। ④ हुक जब आख़िरकार रास्ता देता है, तब हार पास जैसी दिखती है। खुद लिखना हो तो ये चारों केस सँभालने पड़ेंगे।

verify.sh में कौन सी जाँचें डालनी चाहिए?

इस सवाल का जवाब कि "इस रेपो में किसी बदलाव को साबित होने के लिए क्या सच होना चाहिए"। आम तौर पर असल में चलाया जाने वाला टेस्ट कमांड, टाइप चेक, फिर lint - इसी क्रम में एक-एक करके ऑन करें। पूरा रन एक मिनट में ख़त्म होना चाहिए, और secrets या प्रोडक्शन एक्सेस माँगने वाली जाँचें CI में रखें। पहले दिन से महत्वाकांक्षी न बनाना अहम है। धीमा या अस्थिर गेट हफ़्ते भर में bypass हो जाता है, और bypass हुआ गेट न होने से भी बुरा है।

संबंधित लेख

0 टिप्पणियाँ