# क्या bulk API operations को पहले अपने targets दिखाने चाहिए?

AI एजेंट को «inactive accounts archive करो» जैसे अस्पष्ट अनुरोध को कभी भी बिना सीमा वाली write call में नहीं बदलना चाहिए। कई records बदलने से पहले उसे exact target set दिखाना चाहिए, दिखाई गई selection को सुरक्षित रखना चाहिए और ऐसी confirmation लेनी चाहिए जो सिर्फ उसी selection पर लागू हो।

पहली बार कोई खराब filter live customer, test tenant या ऐसे account से match होने तक यह जरूरत से ज्यादा सावधानी जैसी लग सकती है, जिसे किसी ने ticket में exception के रूप में लिखा ही नहीं। इंसान भी यही गलती कर सकता है, लेकिन एजेंट machine speed पर request भेज सकता है और पहले गलत परिणाम के बाद भी काम जारी रख सकता है। जरूरी control request से पहले दी गई विनम्र चेतावनी नहीं है। जरूरी है एक ऐसी जांच योग्य सीमा, जो यह तय करने और उन्हें बदलने के बीच रहे कि असर किन लोगों या records पर पड़ेगा।

## Count blast radius नहीं बता सकता

Count बताता है कि operation कितने records को प्रभावित करेगा। यह नहीं बताता कि वे records कौन हैं। «482 subscriptions» सुनने में ठीक लग सकता है, लेकिन उसमें enterprise tenant, कानूनी कारणों से बनाए रखने वाले suspended accounts या ऐसा region शामिल हो सकता है जिसका request में जिक्र नहीं था।

Target manifest जरूरी बनाएं। छोटे operation में manifest हर identifier को इतना context देकर दिखा सकता है कि reviewer गलती पहचान सके। बड़े operation में पूरी list किसी exportable review surface पर दिखाएं और status, tenant, region या owner जैसी उपयोगी groupings भी दें। इंसान को सिर्फ filter string देखकर membership का अनुमान लगाने पर मजबूर न करें।

एक उपयोगी preview पांच सीधे सवालों के जवाब देता है:

- Write किस API resource और environment को भेजी जाएगी?
- इस set को किस selector ने तैयार किया?
- Set में कौन से records हैं, stable IDs और आसानी से समझ आने वाले fields के साथ?
- हर record को कौन सा mutation मिलेगा?
- किस स्पष्ट rule के कारण कौन से records बाहर रखे गए?

आखिरी सवाल एक असहज गलती पकड़ता है: request तकनीकी रूप से सही हो सकता है, फिर भी intent के खिलाफ जा सकता है क्योंकि कोई छिपा exception selector में आया ही नहीं। अगर operator कहता है «सभी unpaid invoices, disputed को छोड़कर», तो preview में disputed exclusion साफ दिखना चाहिए। चुप्पी से reviewer को अंदाजा लगाना पड़ता है कि एजेंट ने exception समझा या उसे भूल गया।

Sample को manifest न समझें। 10,000-record update में पहले 20 records दिखाने से बहुत कम साबित होता है। Samples से साफ दिखाई देने वाली समस्या पकड़ी जा सकती है, लेकिन stored selection में हर वह record शामिल होना चाहिए जिसे बदला जा सकता है।

## Preview और commit एक ही selection से जुड़े होने चाहिए

Preview तभी सार्थक है जब commit यह साबित कर सके कि वह reviewed set पर ही काम कर रहा है। अगर एजेंट 14:00 बजे search का preview बनाता है और लिखने से पहले 14:05 बजे वही search फिर चलाता है, तो population बदल सकती है। नए records filter में आ सकते हैं, मौजूदा records की state बदल सकती है और pagination rows का क्रम बदल सकती है।

सबसे सुरक्षित API design server-side selection snapshot बनाता है। Preview endpoint selection ID, उसकी expiry, membership count और revision या digest लौटाता है। Commit endpoint वही selection ID स्वीकार करता है और snapshot expire या बदल जाने पर request अस्वीकार कर देता है।

```http
POST /v1/subscriptions/selections
Content-Type: application/json

{
  "filter": {
    "status": "past_due",
    "region": "eu",
    "exclude_tags": ["disputed", "legal_hold"]
  },
  "fields": ["id", "customer_name", "status", "amount_due", "tags"]
}
```

एक सही response कुछ इस तरह दिख सकता है:

```json
{
  "selection_id": "sel_7f2c",
  "expires_at": "2025-03-08T15:00:00Z",
  "count": 482,
  "digest": "sha256:4c76...",
  "records": [
    {"id":"sub_104","customer_name":"Northwind Parts","status":"past_due","amount_due":3100,"tags":[]},
    {"id":"sub_219","customer_name":"Orchard Studio","status":"past_due","amount_due":450,"tags":[]}
  ]
}
```

`records` array cursor के जरिए आ सकती है, लेकिन selection ID का संबंध पूरी frozen membership से होना चाहिए, केवल मौजूदा page से नहीं। Review interface result को pages में दिखा सकता है, पर review के तहत मौजूद object नहीं बदलना चाहिए।

मंजूरी के बाद एजेंट original filter के बजाय saved selection भेजता है:

```http
POST /v1/subscriptions/bulk-actions
Content-Type: application/json
Idempotency-Key: 9b03c6f0-7dfa-4f22-b0e5-4b52ca4f1a51

{
  "selection_id": "sel_7f2c",
  "expected_digest": "sha256:4c76...",
  "action": {"type": "pause_collection", "reason": "approved credit hold review"}
}
```

Server को mismatched digest पर conflict response लौटाकर request अस्वीकार करनी चाहिए। सफल request को सिर्फ `{"ok": true}` के बजाय action ID और हर record के परिणाम का location लौटाना चाहिए। एक सामान्य success message partial completion छिपा देता है, जबकि bulk काम में यही सबसे आम failure mode है।

अगर vendor API snapshots नहीं बना सकता, तो एजेंट ordered IDs list सुरक्षित रखकर, canonical representation का hash बनाकर और उस ID list को write endpoint पर भेजकर operation को जोड़ सकता है। इसकी सीमाएं हैं: URL और body limits, stale records और ऐसे APIs जो सिर्फ filter स्वीकार करते हैं। इन स्थितियों में इस control को snapshot के बराबर न मानें। हर सीमित batch से ठीक पहले फिर preview बनाएं और membership बदलने पर रुक जाएं।

## Stable pagination तय करती है कि review का कोई अर्थ है या नहीं

बदलते data पर होने वाले action की approval के लिए offset pagination खराब आधार है। मान लें एजेंट page one में records 1 से 100 देखता है, फिर कोई दूसरा process शुरुआत के पास के 20 records archive कर देता है। जब एजेंट offset 100 मांगता है, तो ऊपर खिसके records छूट सकते हैं। Inserts से duplicates भी आ सकते हैं। Review count लगभग सामान्य दिख सकता है, जबकि individual membership बदल चुकी हो।

Service द्वारा जारी cursor इस्तेमाल करें और API provider से पूछें कि क्या वह cursor snapshot से पढ़ता है। जो cursor सिर्फ sort position को encode करता है, उसमें sorted field बदलने पर data drift कर सकता है। Mutable `updated_at` field पर sorting खास तौर पर खराब है, जब proposed action खुद उस timestamp को update करता हो।

जब API आपके नियंत्रण में हो, तो ये properties स्पष्ट रूप से उपलब्ध कराएं:

- Snapshot identifier या immutable high-water mark।
- Immutable identifier पर deterministic sort।
- ऐसी expiry, जो चुपचाप नया data लौटाने के बजाय fresh preview जरूरी बनाए।
- Response field, जो बताए कि caller consistent snapshot पढ़ रहा है या नहीं।

RFC 9110 POST, PUT, PATCH और DELETE जैसी methods को unsafe मानता है, क्योंकि वे server state बदल सकती हैं। यह classification workflow design नहीं है, लेकिन इससे एक व्यावहारिक नियम मजबूत होता है: सिर्फ इसलिए list endpoint और उसके बाद unsafe method को एक atomic operation न मानें कि code में दोनों calls पास-पास लिखी हैं।

जिस external API में stable cursors या selection snapshots में से कोई भी सुविधा नहीं है, उसमें scope इतना छोटा करें कि कोई व्यक्ति हर request की समीक्षा कर सके। Agent-side cache और लंबा loop बनाकर सीमा से बचना आकर्षक है। अक्सर इससे बिना transaction boundary और किसी authoritative उत्तर के एक दूसरा database बन जाता है कि run के बीच में record बदलने पर सही स्थिति क्या थी।

## Approval में सिर्फ records नहीं, mutation भी दिखना चाहिए

Reviewer को membership और effect दोनों की मंजूरी देनी होती है। «482 records पर बदलाव लागू करें» तब तक अधूरा है जब तक interface यह न बताए कि records delete, disable, reassign या charge होंगे, publish होंगे या किसी field में बदलाव होगा। बदलने वाले हर field की before और proposed after value शामिल करें। जब सभी values को एक जैसा update मिलना हो, तो साथ में संक्षिप्त aggregate भी दें।

Absolute writes और conditional writes को अलग रखें। Absolute write कहता है `status = archived`, चाहे preview के बाद कुछ भी हुआ हो। Conditional write कहता है «तभी archive करें जब status अभी भी inactive हो और version अभी भी 17 हो»। Conditional writes आम तौर पर सुरक्षित होते हैं, क्योंकि कोई दूसरा व्यक्ति record बदल दे तो वे fail closed करते हैं।

जहां API इसे support करे, हर mutation में version, ETag या last-known revision इस्तेमाल करें। यह target manifest का विकल्प नहीं है। यह अलग समस्या हल करता है: execution शुरू होने तक reviewed record शायद eligible न रहा हो।

एक compact approval record इस तरह दिख सकता है:

```json
{
  "request_id": "req_91a8",
  "selection_id": "sel_7f2c",
  "selection_digest": "sha256:4c76...",
  "target_count": 482,
  "action": {
    "type": "pause_collection",
    "precondition": {"status": "past_due"}
  },
  "approved_by": "operator account identifier",
  "approved_at": "2025-03-08T14:16:02Z"
}
```

एजेंट को इसी set पर किसी दूसरे action के लिए इस approval का दोबारा इस्तेमाल न करने दें। Collection pause करना, credits जारी करना और records delete करना अलग परिणाम पैदा करते हैं, भले target list एक जैसी हो। Approval को selection digest के साथ canonical action payload से भी जोड़ें।

Time limits जरूरी हैं। ऐसी approval जो agent के इस्तेमाल करने तक valid रहती है, human review के एक क्षण को स्थायी permission में बदल देती है। Operation के अनुसार approvals की short expiry रखें, selection बदलने पर उन्हें invalid करें और agent action में बड़ा बदलाव करे तो नया निर्णय मांगें।

## Partial completion के लिए journal और stop rule चाहिए

हर bulk operation किसी rate limit, timeout, validation failure या network break से टकराएगा। सबसे खतरनाक प्रतिक्रिया यह है कि पहले से बदले records जाने बिना पूरा job फिर चला दिया जाए। इससे duplicate charges, दोहराए गए notifications या भ्रामक audit trail बन सकते हैं।

Requested action को एक operation ID और एक idempotency key दें। हर target का परिणाम दर्ज करें: succeeded, failed, precondition बदलने के कारण skipped या service ने durable result न लौटाया हो तो unknown। «Unknown» और failed एक बात नहीं हैं। Retry करने से पहले इसे investigation state मानें।

Execution से पहले stop rule तय करें। Authorization failure या unexpected schema response जैसी structural error पर रुकना समझदारी है, जबकि अलग-अलग validation failures को review के लिए इकट्ठा किया जा सकता है। Generic «continue on error» setting से बचें। यह अनपहचाने API contract बदलाव को खराब हुए records की लंबी सूची में बदल सकती है।

एक आम failure देखें। एजेंट role change के लिए 800 user accounts का preview बनाता है और फिर client-side loop शुरू करता है। पहली 300 requests सफल होती हैं। Deployment endpoint बदल देता है, इसलिए missing field intended viewer role के बजाय administrator पर default होने लगती है। अगला response सफल दिखता है। Loop चलता रहा तो गलती फैलती जाएगी। अगर एजेंट हर response का shape दर्ज करे और contract approved action से अलग होते ही रुक जाए, तो blast radius पहली असामान्य response पर खत्म हो जाता है।

Destructive काम के लिए execution से पहले compensation design करें। Compensation request में हर record की prior value captured होनी चाहिए, न कि यह अस्पष्ट वादा कि कोई बाद में इसे undo कर देगा। फिर भी rollback को harmless न मानें। बाद का legitimate edit blind reversal को गलत बना सकता है और emails या exports जैसे external side effects शायद वापस न हों।

## खराब write जितना data read भी उजागर कर सकता है

Teams अक्सर deletion पर careful confirmation लगाती हैं, लेकिन selection पर नहीं। इससे यह तथ्य छूट जाता है कि preview बनाने के लिए एजेंट पूरी customer list, personal addresses, payment states या internal notes ला सकता है। Preview में इंसान को records पहचानने के लिए पर्याप्त data होना चाहिए, underlying API में मौजूद हर field नहीं।

जानबूझकर narrow field set मांगें। Stable ID, display name, status, ownership और proposed change से जुड़े values आम तौर पर पर्याप्त होते हैं। Secrets, tokens, free-form notes और असंबंधित personal data को selection response से बाहर रखें। इससे review कम शोर वाला बनता है और बाद के messages में एजेंट द्वारा दोहराए जा सकने वाले data की मात्रा घटती है।

यही सिद्धांत filters पर भी लागू होता है। किसी field को देखने की permission न होने पर एजेंट को query broaden नहीं करनी चाहिए। अगर वह साबित नहीं कर सकता कि कोई record selection में होना चाहिए या नहीं, तो ambiguity सामने रखे और human के निर्णय का इंतजार करे। अनुमान लगाना operational judgment नहीं है।

Environment को manifest का हिस्सा मानें। Production, staging और sandbox में resource names एक जैसे हो सकते हैं। Target count और action summary के साथ destination host या account identifier भी रखें। Engineers ने सही record list को गलत environment पर approve किया है, क्योंकि preview में environment को पृष्ठभूमि की छोटी-सी जानकारी जैसा दिखाया गया था।

## Agents को call का अधिकार दें, credentials की custody नहीं

Broad API token रखने वाला एजेंट target set दिखने से पहले bulk call कर सकता है। इस design के आसपास prompts और logs जोड़े जा सकते हैं, लेकिन credential फिर भी process को नियंत्रण से बच निकलने का रास्ता देता है। Credential ऐसे action executor के पास रखें जो external API तक request पहुंचने से पहले उसे deny या approve कर सके।

समर्थित HTTP और SSH actions के लिए Sallyport यही तरीका अपनाता है: एजेंट अपने MCP connection से अनुरोध करता है, जबकि app credential अपने पास रखकर action करती है। इसकी per-session authorization और optional per-call key approval उस workflow के लिए उपयोगी हैं जिसमें एजेंट bulk request तैयार कर सकता है, लेकिन उसे reusable secret material नहीं मिलना चाहिए।

यह approval पूरी bulk safety design नहीं है। HTTP call के approval card से reviewer यह नहीं जान सकता कि filter 10 records लौटाएगा या 10,000, जब तक एजेंट पहले target manifest तैयार करके सुरक्षित न रखे। Authority नियंत्रित करने के लिए gateway का उपयोग करें, फिर preview, selection, approval और commit को application workflow में एक-दूसरे से बांधें।

Audit record में भी दो स्तर की detail चाहिए। एक journal में agent run और उसे approve करने वाले व्यक्ति की जानकारी होनी चाहिए। दूसरे में हर external call, selection digest, operation ID, endpoint और result status समेत, दर्ज होनी चाहिए। Incident होने पर investigators को दोनों सवालों का जवाब चाहिए: «काम किस process ने मांगा?» और «कौन से records बदले?»

## Intent अस्पष्ट होते ही bulk workflow को fail closed होना चाहिए

Agent workflow इस तरह बनाएं कि natural-language request से सीधे mutation तक पहुंचना संभव न हो। नीचे का sequence जानबूझकर साधारण रखा गया है, क्योंकि साधारण workflow की जांच आसान होती है।

1. एजेंट request को selector, proposed mutation, environment और exclusions में बदलता है। इनमें कुछ भी अस्पष्ट हो तो clarification मांगता है।
2. वह stable selection बनाता है और सभी members के review fields लाता है। Selection ID, digest, query, timestamp और page completeness दर्ज करता है।
3. वह manifest और proposed effect दिखाता है। Human उसी exact pair को approve या reject करता है।
4. वह selection reference, expected digest, action payload, idempotency key और जहां उपलब्ध हों वहां record versions के साथ commit request भेजता है।
5. वह completed, failed, skipped और unknown outcomes अलग-अलग बताता है। Partial result को कभी भी इस खुशहाल संदेश में नहीं बदलता कि job पूरा हो गया।

«cleanup script चलाएं» जैसे raw command को approve न करें, जब command बाद में अपने targets calculate कर सकती हो। यह तरीका लोकप्रिय है क्योंकि तेज और परिचित है, खासकर उन teams में जो अपने scripts पर पहले से भरोसा करती हैं। समस्या यह है कि approval code text को cover करती है, live data set को नहीं। Confirmation और execution के बीच data में छोटा बदलाव भी approved command से unapproved काम करा सकता है।

Recurring jobs के लिए narrow selectors और maximum count पहले से तय करें। Selection उस सीमा से बाहर जाए या उसमें कोई unfamiliar category शामिल हो, तो review जरूरी बनाएं। Maximum count guardrail है, authorization नहीं। Manifest ही यह evidence देता है कि job ने वास्तव में किन records को छुआ।

पहला implementation task सरल है: अपने bulk endpoint से selection identifier और digest लौटवाएं, फिर उन दोनों values को दोहराए बिना आने वाली commit requests अस्वीकार करें। यह contract बन जाने पर agents, dashboards और scripts सभी ऐसी ठोस सीमा का पालन कर सकते हैं जिसे नजरअंदाज करना मुश्किल हो।
