# ऐसे dry-run endpoints जो AI प्रोग्रामिंग एजेंट को ईमानदार रखें

AI प्रोग्रामिंग एजेंट को यह पता लगाने के लिए बदलाव नहीं करना चाहिए कि वह बदलाव मान्य है या नहीं। यह आलसी API design है और autonomy इसकी कीमत जल्दी दिखा देती है। कोई एजेंट किसी operator के accidental permission edit, अधूरे migration या गलत scope वाले deletion को समझने से कहीं तेज़ी से retry कर सकता है, branch बना सकता है और अगले काम पर जा सकता है।

Preview action तभी उपयोगी है जब वह किसी खास execution का इतना सटीक अनुमान दे कि व्यक्ति या एजेंट तय कर सके कि आगे बढ़ना है या नहीं। `"valid"` कहने वाला response कोई plan नहीं है। ऐसा diff जो cascading update छोड़ दे, बिना diff के भी बदतर है, क्योंकि वह झूठा भरोसा पैदा करता है।

सही design लक्ष्य सरल है: प्रस्तावित write भेजें, उसे मौजूदा state और सामान्य business rules के आधार पर evaluate करें, planned effects और failures लौटाएं, फिर execution को पुराने या बदले हुए plans से इनकार करने दें। इसमें सिर्फ `dryRun=true` जोड़ने से अधिक सावधानी चाहिए। इससे एजेंट को invalid request सुधारने का रास्ता भी मिलता है, इससे पहले कि वह किसी व्यक्ति से approval मांगे।

## Preview को ठीक वही write बतानी चाहिए

Dry-run endpoints को execution जैसी ही meaningful intent स्वीकार करनी चाहिए और उसी intent के वास्तविक effects निकालने चाहिए। अगर `POST /memberships` role दे सकता है, invitation भेज सकता है, member को billing group में जोड़ सकता है और audit entry लिख सकता है, तो preview को execution से होने वाले हर effect की जानकारी देनी चाहिए।

टीमें अक्सर ऐसा `validate` endpoint बना देती हैं जो JSON shape और required fields जांचता है। उसका अपना उपयोग है, लेकिन वह write का preview नहीं देता। वह caller को यह नहीं बता सकता कि मांगा गया role मौजूदा role से टकराता है, target account suspended है या invitation सीमित seat में से एक इस्तेमाल करेगा। अगर वह केवल इतना ही करता है, तो उसे validation कहें।

यह अंतर महत्वपूर्ण है क्योंकि एजेंट सफल calls को evidence मानते हैं। Validation-only response के बाद execution होने पर agent decision के state-dependent हिस्सों से अनजान रहता है। Full preview request और मौजूदा दुनिया, दोनों का मूल्यांकन करता है।

हर writable operation के लिए preview design करने से पहले execution contract को एक वाक्य में लिखें:

> इस input और देखे गए target revision के आधार पर execution इन नामित effects को create, update, delete या trigger करेगा।

यह वाक्य अस्पष्ट behavior सामने लाता है। `Update project settings` बहुत व्यापक है। `retention_days` को 30 से 14 करना, 18 active items की expiry फिर से निकालना और legal hold वाले items को अस्वीकार करना preview को लौटाने के लिए कुछ testable देता है।

अच्छा preview operation के semantics को बनाए रखता है। Bulk delete को अस्पष्ट count में न बदलें, सिर्फ इसलिए कि असली list लौटाना असुविधाजनक लगता है। जब service वास्तविक resources तय कर सकती हो, तब `may affect` का इस्तेमाल न करें। अगर resource set इतना बड़ा है कि उसे inline लौटाना संभव नहीं, तो total, सीमित sample और cursor या report reference लौटाएं, जिससे execution से पहले caller पूरा set देख सके।

## Plan में identity, scope और consequences चाहिए

किसी व्यक्ति के लिए `12 records will change` तब तक अर्थपूर्ण नहीं है, जब तक यह न पता हो कि कौन से records बदलेंगे और कैसे बदलेंगे। Planned action को अपने inputs, target scope और consequences की पहचान ऐसे रूपों में करनी चाहिए जिन्हें program और human दोनों देख सकें।

Single resource के update के लिए field-level diff अक्सर अच्छा रहता है। Deployment के लिए plan में images, environments, configuration revisions, restart behavior और health checks चाहिए हो सकते हैं। Billing change के लिए old charge, new charge, effective date और यह जानकारी चाहिए हो सकती है कि customer को notification मिलेगी या नहीं। हर operation को JSON Patch array में ठूंसने के बजाय output को domain के अनुसार रखें।

कम से कम ये हिस्से दिखाएं:

- Operation name और स्पष्ट preview status।
- हर प्रभावित resource का stable identifier और, यदि service revisions support करती है, तो उसका revision।
- हर meaningful change के लिए पिछली और प्रस्तावित values।
- Secondary effects, जैसे jobs, notifications, access changes या calculated charges।
- Warnings, execution blockers और ऐसे assumptions जो result बदल सकते हैं।

`Meaningful` का फैसला सोच-समझकर करें। Raw database timestamp approver के लिए शायद उपयोगी न हो। नया assigned owner, बढ़ी हुई group membership या planned deletion निश्चित रूप से उपयोगी है। पहले semantic result दिखाएं और जरूरत पड़ने पर lower-level detail दें।

Preview को direct effects और derived effects में भी अंतर करना चाहिए। मान लें कि एजेंट किसी team का storage quota घटाता है। Direct change एक quota field है। Derived result यह हो सकता है कि तीन मौजूदा projects में uploads freeze हो जाएं। इसे generic warning के नीचे छिपाने से operation वास्तविकता से अधिक सुरक्षित दिखता है। इसे अलग `effects` array में रखें और कारण का नाम दें।

Uncertainty के बारे में भी उतने ही सटीक रहें। Preview बता सकता है कि execution किसी external tax service को query करेगा या बाद के लिए काम schedule करेगा। अगर service ने tax amount तय नहीं किया है, तो उसे final tax amount न बताएं। ऐसा assumption record इस्तेमाल करें जो dependency का नाम और यह बताए कि उसके बिना execution आगे बढ़ सकता है या नहीं।

## Validation को blockers और warnings में अलग रखें

Preview को एजेंट को ठीक-ठीक बताना चाहिए कि execution को क्या रोकता है, किसकी समीक्षा होनी चाहिए और क्या केवल context है। इन categories को मिलाने से खराब retries और approval fatigue तय हो जाती है।

Blocker का मतलब है कि evaluated conditions में service execution से इनकार करेगी। Agent को input सुधारना चाहिए, missing authority लेनी चाहिए या रुक जाना चाहिए। Warning का मतलब है कि execution आगे बढ़ सकती है, लेकिन उचित operator consequence देखना चाहेगा। Context जानकारी देता है, खतरे का संकेत नहीं।

Prose के बजाय structured errors लौटाएं, जिन्हें agent को parse करना पड़े। यह shape जानबूझकर साधारण है:

```json
{
  "mode": "preview",
  "executable": false,
  "validation": [
    {
      "severity": "error",
      "code": "version_conflict",
      "path": "/if_match",
      "message": "Project prj_184 is at revision 73, not revision 71.",
      "blocks_execution": true,
      "repair": "Fetch the current project and create a new preview."
    },
    {
      "severity": "warning",
      "code": "member_count_change",
      "message": "The group will gain 42 members through nested groups.",
      "blocks_execution": false
    }
  ]
}
```

Stable codes एजेंट को response चुनने देते हैं। `version_conflict` के बाद वह current revision ला सकता है, लेकिन `legal_hold_active` के बाद जिम्मेदारी से कोई fix गढ़ नहीं सकता। `message` उस व्यक्ति के लिए है जो action की समीक्षा कर रहा है। दोनों रखें।

हर चौंकाने वाली condition को warning न कहें। अगर warning के बाद हमेशा किसी को request बदलनी पड़ती है, तो वह error होनी चाहिए। इसके उलट, किसी असामान्य लेकिन अनुमत condition के कारण execution को block न करें। कुछ न छूट जाए, इस डर से टीमें हर warning को blocker बना देती हैं। फिर एजेंट ऐसे previews भेजते हैं जो manual cleanup के बिना कभी पूरे नहीं हो सकते। Interface दिखावे का बन जाता है।

उपयोगी test सीधा है: अगर execution को वही input उसी state के विरुद्ध मिले, तो क्या वह चलेगी? अगर हां, तो warning या context report करें। अगर नहीं, तो error report करें। Authorization failures को domain validation से अलग रखें। वे अलग समस्याएं बताते हैं और उनके समाधान भी अलग होते हैं।

## Dry run caller की जानकारी के बिना write नहीं कर सकता

Preview को durable external effects से बचना चाहिए, उन effects से भी जिन्हें developers housekeeping मानकर नजरअंदाज कर देते हैं। `Temporary` row बनाना, inventory reserve करना, users को दिखने वाला sequence बढ़ाना, webhook queue करना, email भेजना या last-access timestamp update करना, ये सभी उस उम्मीद को तोड़ते हैं कि request केवल निरीक्षण के लिए सुरक्षित थी।

यह bug mature services में इसलिए दिखता है क्योंकि execution code सुविधा के आसपास बढ़ता गया। Create handler शुरुआत में identifier allocate कर सकता है, validation से पहले pending record लिख सकता है और transaction commit होने से पहले event publisher को call कर सकता है। बाद में कोई final insert के चारों ओर `if preview` लगा देता है। Local test में preview harmless दिखता है, फिर भी production में identifiers इस्तेमाल करता है, event traffic पैदा करता है या कचरा छोड़ देता है।

Preview execution को केवल controller की condition न बनाएं। Application service में इसे अलग mode की तरह रखें। यह mode shared parsing, authorization, policy और planning functions को call कर सकता है। लेकिन writes और external sends को ऐसे interfaces से गुजारना चाहिए जो या तो proposed effect बनाएं या request fail कर दें।

एक उपयोगी implementation boundary इस तरह दिख सकती है:

```text
parse request
  -> authorize caller
  -> load consistent current state
  -> validate business rules
  -> build plan
  -> preview: return plan
  -> execute: apply plan in a transaction, then publish committed effects
```

Order महत्वपूर्ण है। अगर आपका database transactions support करता है, तो plan उन्हीं reads से बनाएं जो execution को दिशा देंगे। अगर कोई dependency transaction में शामिल नहीं हो सकती, तो उसकी pending interaction को explicit effect के रूप में report करें और failures के लिए compensating action design करें। External call को transactional मान लेने से वह transactional नहीं बन जाती।

Audit records के बारे में भी निर्णय लें। आप यह record करना चाह सकते हैं कि caller ने preview मांगा था। यह उचित है, लेकिन उस event को साफ तौर पर अलग audit path में लिखें और सुनिश्चित करें कि वह completed changes के लिए बने workflows शुरू न कर दे। `Previewed` को `permission granted` के पास लिखकर downstream consumers से अंतर समझ लेने की उम्मीद न करें।

केवल output नहीं, absence भी test करें। Preview request से पहले और बाद में जांचें कि relevant tables, outgoing queues, object storage, email test sinks और downstream webhook receivers में कोई बदलाव नहीं हुआ। Unit tests अक्सर इसे नहीं पकड़ते। Disposable environment के आसपास किया गया integration test पकड़ लेगा।

## HTTP semantics के लिए स्पष्ट contract चाहिए

HTTP में कोई universal dry-run method नहीं है और ऐसा मानने से interoperability की समस्याएं पैदा होती हैं। RFC 9110 `GET`, `HEAD`, `OPTIONS` और `TRACE` को इस अर्थ में safe methods मानता है कि client state change का अनुरोध नहीं करता। वह यह नहीं कहता कि query parameter वाला `POST` safe है, न ही `dryRun` को standard request control के रूप में परिभाषित करता है।

इसलिए endpoint designer को request और response दोनों में mode को स्पष्ट करना चाहिए। `POST` अक्सर फिर भी उचित है, क्योंकि complex writes की planning के लिए request body चाहिए और evaluation महंगी हो सकती है। जरूरी यह है कि clients, logs और humans बिना अनुमान लगाए preview और execution में अंतर समझ सकें।

Simple operation के लिए स्पष्ट body field पढ़ने में आसान और गलती से छूटने में कठिन होता है:

```http
POST /v1/projects/prj_184/memberships/plan
Content-Type: application/json

{
  "subject_id": "usr_92",
  "role": "admin",
  "if_match": "73"
}
```

Dedicated `/plan` endpoint तब उपयोगी है जब planning का अपना output, lifecycle या permissions हों। यह query flags से जुड़ी बार-बार होने वाली समस्या से भी बचाता है: generated client flag छोड़ देता है, proxy cache configuration में उसे अनदेखा कर देता है या caller URL गलत copy कर देता है और write हो जाती है। अगर आप `mode` field वाला एक endpoint चुनते हैं, तो उन operations में missing या unknown values अस्वीकार करें जहां accidental execution नुकसानदेह होगी।

ऐसा response type लौटाएं जिसे executed resource न समझा जा सके। Resource-shaped body के साथ `201 Created` खराब preview response है, भले ही उसमें `preview: true` field जोड़ दें। Immediate plan के लिए `200 OK` इस्तेमाल करें, और `202 Accepted` केवल तब जब planning खुद asynchronous हो। Response body में `mode: "preview"` शामिल करें और अगर आपका API typed media types इस्तेमाल करता है, तो explicit content type सेट करें।

Previews को cache करने से बचें, जब तक आप caller identity और authorization समेत उन सभी inputs को न समझते हों जो result को प्रभावित करते हैं। सुरक्षित default `Cache-Control: no-store` है। Stale plan केवल पुराना page नहीं होता। वह agent को ऐसी write की ओर ले जा सकता है जो अब अलग resources को प्रभावित करेगी।

इस काम के लिए `OPTIONS` का गलत इस्तेमाल न करें। RFC 9110 में इसका उपयोग communication options बताने के लिए है, arbitrary body के साथ write simulate करने के लिए नहीं। जो service इसे overload करती है, वह libraries, security controls और सामान्य HTTP behavior की अपेक्षा रखने वाले सभी लोगों को भ्रमित करेगी।

## Execution को साबित करना होगा कि plan अब भी current है

Execution से पहले के अंतराल में preview गलत हो सकता है। कोई दूसरा user record edit कर सकता है, scheduled job चल सकती है, entitlement expire हो सकता है या agent response पढ़ने के बाद request बदल सकता है। यह time-of-check to time-of-use problem है और आश्वस्त करने वाला preview इसे खत्म नहीं करता।

Plan को evaluated request, पढ़े गए resource revisions, caller identity और छोटी expiry से बांधें। Server signed opaque `plan_token` लौटा सकता है या plan को retain करके identifier दे सकता है। Opaque tokens client को plan को editable authority समझने से रोकते हैं। Stored plans बड़े effects को inspect करना और approval revoke करना आसान बनाते हैं। दोनों approaches काम करते हैं, बशर्ते execution सही conditions फिर से जांचे।

Response में यह शामिल हो सकता है:

```json
{
  "mode": "preview",
  "plan_id": "plan_7f4c",
  "expires_at": "2025-06-18T14:05:00Z",
  "request_digest": "sha256:...",
  "read_revisions": [
    {"resource": "projects/prj_184", "revision": "73"}
  ],
  "executable": true
}
```

Execution के समय service को caller, digest, expiry और revisions verify करने चाहिए। फिर उसे या तो पहले से approved plan को transaction में atomically apply करना चाहिए या write transaction के भीतर plan फिर से बनाकर approved plan से compare करना चाहिए। अगर equivalence की गारंटी न दी जा सके, तो `plan_stale` के साथ request अस्वीकार करें और नया preview मांगें।

Agent को एक subject के लिए request preview करने और अलग subject के साथ body में वही plan ID execute करने की अनुमति न दें। बेहतर होगा कि execution केवल plan ID और expected revision स्वीकार करे, ताकि server को reconcile करने के लिए request की दूसरी mutable copy ही न मिले।

कुछ changes को meaningful guarantee नहीं दी जा सकती। Message भेजने का plan एक क्षण बाद अनुपयुक्त हो सकता है क्योंकि recipient का address बदल गया। Third-party service को call करने का plan उस price पर निर्भर हो सकता है जो call से पहले बदल जाए। Output में यह बात बताएं, irreversible action से ठीक पहले फिर validation करें और जब अंतर महत्वपूर्ण हो तो नया decision मांगें।

## Agent workflows को execution से पहले सोच-समझकर रुकना चाहिए

Agent को preview को decision के लिए evidence मानना चाहिए, write को अपने-आप चलाने की permission नहीं। Agent के पास यह तय करने के rules होने चाहिए कि कब execution कर सकता है, कब request सुधारनी चाहिए और कब plan किसी व्यक्ति को दिखाना अनिवार्य है।

सबसे भरोसेमंद workflow में चार actions होते हैं:

1. Intended write को preview mode में idempotency reference और expected resource revisions के साथ submit करें।
2. Response में blockers हों तो रुकें, केवल response में बताए गए fields सुधारें या missing intent के लिए व्यक्ति से पूछें।
3. जब operation team की approval boundary पार करे, तो planned effects और warnings दिखाएं।
4. केवल तब लौटाए गए plan को execute करें जब वह current हो, फिर execution result को preview से अलग record करें।

Approval का ध्यान raw JSON dump पर नहीं, consequence पर होना चाहिए। Access देने का निर्णय लेने वाला व्यक्ति principal, role, group expansion से पहुंचे resources और duration देखना चाहता है। उसे IDs से भरी request body से impact का अनुमान नहीं लगाना चाहिए।

हर harmless action के लिए agent को preview कराकर हर warning पर approval न मांगें। इससे ऐसे cards की कतार बन जाती है जिन्हें कोई पढ़ता नहीं। Application में meaningful boundaries तय करें: irreversible operations, access, money, external communication, व्यापक resource sets और वे actions जिनके effects को service uncertain बताती है। आपके दिए authority के भीतर agent छोटे, अच्छी तरह समझे गए changes चला सकता है।

Sallyport agent की वास्तविक HTTP या SSH call के लिए human decision ज़रूरी कर सकता है, जबकि API का preview उस decision को ठोस आधार देता है। दोनों controls अलग समस्याएं हल करते हैं: एक यह नियंत्रित करता है कि process कार्रवाई कर सकती है या नहीं, दूसरा बताता है कि target service क्या करेगी।

## असफल bulk change दिखाता है कि summaries पर्याप्त क्यों नहीं हैं

मान लें कि किसी agent से production support group से contractors हटाने को कहा गया। उसे 37 accounts से match करने वाला filter मिलता है और वह preview submit करता है। Service `count: 37`, `valid: true` और generic note लौटाती है कि inherited memberships बदल सकती हैं। Operator approval दे देता है क्योंकि मांगा गया outcome सामान्य लगता है।

Execution उन 37 accounts की direct membership हटा देती है। उनमें से चार nested groups के जरिए access बनाए रखते हैं। छह अन्य अलग on-call permission खो देते हैं क्योंकि service linked entitlement भी हटा देती है। Notification job सभी 37 लोगों को बताती है कि उनका access बदल गया। अब operator को पता लगाना पड़ता है कि कौन से effects intended थे, कौन से छिपे हुए थे और notification ने वास्तविक access state का सही वर्णन किया या नहीं।

Preview सबसे संकीर्ण अर्थ में technically truthful था। उसने यह वादा नहीं किया था कि filter केवल contractors को चुनेगा। फिर भी वह खराब interface था, क्योंकि user को membership graph और effect list चाहिए थी, count नहीं।

बेहतर response result को consequence के आधार पर group करता:

```json
{
  "mode": "preview",
  "operation": "remove_group_members",
  "selected": 37,
  "effects": [
    {"type": "direct_membership_removed", "count": 37},
    {"type": "access_retained_via_nested_group", "subjects": ["usr_8", "usr_19", "usr_31", "usr_44"]},
    {"type": "on_call_entitlement_removed", "subjects": ["usr_2", "usr_7", "usr_11", "usr_24", "usr_29", "usr_35"]},
    {"type": "notification_queued", "count": 37}
  ],
  "validation": [
    {
      "severity": "warning",
      "code": "access_outcome_varies",
      "message": "Four selected subjects retain group-derived access."
    }
  ]
}
```

बड़े batches के लिए सही response में downloadable report या paginated detail शामिल हो सकती है। उद्देश्य किसी व्यक्ति को हजारों rows पढ़ने के लिए मजबूर करना नहीं है। उद्देश्य write से पहले exceptional और irreversible outcomes को स्पष्ट करना है।

यह example एक आम खराब recommendation भी सामने लाता है: `dry runs केवल destructive actions के लिए इस्तेमाल करें।` टीमें इसे दोहराती हैं क्योंकि deletes खतरनाक लगते हैं और previews में engineering time लगता है। लेकिन permission grant, configuration edit या notification का blast radius deletion से बड़ा हो सकता है। Preview support का निर्णय consequence और reversibility से करें, HTTP verb या database operation से नहीं।

## Tests को previewed effects की executed effects से तुलना करनी चाहिए

Preview endpoint तब कमजोर हो जाता है जब tests केवल यह साबित करते हैं कि वह 200 response लौटाता है। उसका मुख्य वादा equivalence है: जब state और request समान हों, तो reported effects execution से मेल खाने चाहिए।

Paired tests बनाएं। Fixture तैयार करें, preview चलाएं, normalized plan capture करें, fixture reset करें, उसी intent को execute करें और execution journal की predicted effect set से तुलना करें। उन fields को छोड़ दें जिनका match होना उचित नहीं, जैसे server timestamps या generated correlation IDs। Created resources, changed values, published events, notifications या outbound calls को ignore न करें।

Filters और bulk operations के लिए property tests मददगार होते हैं। अलग-अलग states वाले resources का collection generate करें, predicate के विरुद्ध preview मांगें, fresh copy में उसे execute करें और देखें कि selected set तथा final state एक जैसे हैं। ये tests उन खराब cases को ढूंढते हैं जहां planning query एक table join करती है, लेकिन write query किसी दूसरी table को।

Preview side effects के लिए अलग test रखें। Mail, webhooks, queues और payment providers के लिए fake adapters इस्तेमाल करें, जो preview mode में call होने पर test fail कर दें। फिर real persistence layer के विरुद्ध कम से कम एक integration test चलाएं, क्योंकि ORM flush या trigger तब भी write कर सकता है जब application code साफ दिखता हो।

अंत में staleness को जानबूझकर test करें। किसी change का preview बनाएं, दूसरी request से resource बदलें, फिर पुराने plan को execute करें। Service को उसे अस्वीकार करना चाहिए। जो system plan को इसलिए apply कर देता है क्योंकि diff `still looks close enough`, वह अंततः किसी और का काम overwrite करेगा।

## Preview एक API capability है, controls छोड़ने का बहाना नहीं

Preview surprise कम करते हैं। वे authorization, concurrency checks, transaction design, idempotency, audit trails या review की जगह नहीं लेते, खासकर उन operations के लिए जिन्हें review चाहिए। जिस caller के पास authority नहीं है, उसे previews probe करके protected resources का detailed map नहीं मिलना चाहिए। जो caller execution request दोहराता है, उसे plan token valid होने के कारण वही side effect दो बार पैदा नहीं करना चाहिए।

उस write से शुरुआत करें जिसने rehearsal या production में आपकी team को सबसे ज्यादा नुकसान पहुंचाया है। हर direct और indirect effect की सूची बनाएं, ऐसा plan लागू करें जो उन्हें report करे और execution को stale plans अस्वीकार करने दें। फिर paired test लिखें जो साबित करे कि preview और execution एक-दूसरे से मेल खाते हैं। अगर आप यह नहीं बता सकते कि कोई write चलने से पहले क्या करेगी, तो system का जोखिम भरा हिस्सा agent नहीं है। API है।
