DEV Community

Cover image for Codex CLI जब GitHub pull request माँगता है, तो osFoundry सिर्फ़ एक branch कैसे push करता है?
BBT for BBT हिन्दी

Posted on

Codex CLI जब GitHub pull request माँगता है, तो osFoundry सिर्फ़ एक branch कैसे push करता है?

यह वह tool है जिसे Codex CLI तब call करता है जब उसे कोई change set GitHub या GitLab पर ले जाना हो। osFoundry का MCP server इसे हर client को ऐसे ही list करता है, चाहे वह sign in हो या नहीं:

{"name": "propose_gitnest_provider_publication",
 "description": "Ask the repository owner to review a composed GitNest change set, push only its dedicated branch using their linked Git provider account, and open a draft pull request. Approval rechecks permissions, risk policy, required checks, and target commit.",
 "inputSchema": {"properties": {
     "repository_id": {"type": "string"},
     "change_set_id": {"type": "string"},
     "expected_change_set_version": {"type": "integer", "minimum": 1},
     "expected_composition_oid": {"type": "string"},
     "idempotency_key": {"type": "string", "minLength": 8, "maxLength": 128}},
   "required": ["repository_id", "change_set_id", "expected_change_set_version",
                "expected_composition_oid", "idempotency_key"],
   "additionalProperties": false},
 "annotations": {"readOnlyHint": false, "openWorldHint": true, "destructiveHint": true},
 "securitySchemes": [{"type": "oauth2", "scopes": ["osfoundry:write"]}]}
Enter fullscreen mode Exit fullscreen mode

ध्यान दीजिए कि इसमें क्या नहीं है। न branch का नाम, न title, न target branch, न draft flag, न token। Client बस एक change set का नाम देता है और उसके बारे में देखे गए दो facts, बाकी सब कुछ owner की तरफ़ से आता है जब owner approve करता है। "Push only its dedicated branch" वही promise है जिसे यह post खोलकर देखती है। साथ में उस tool की भी बात है जो आम तौर पर पहले आता है, propose_gitnest_file_import, और यह भी कि उसके 200 KB budget का असली files पर क्या मतलब है।

इस Mac पर कोई codex binary install नहीं है, इसलिए client side एक Python script है, जो यहीं चलाया गया (Apple M1, uv के ज़रिए Python 3.12, git 2.39.5)। Network के numbers Japan से measure किए गए।

$ uv run --with tiktoken --with jsonschema python3 publish_sim.py
== Part 1: live tools/list (no token) ==
tools/list x5 median 505.7 ms; 59 tools, 13 GitNest
read_gitnest_repository               osfoundry:read  ro=True  openWorld=False destructive=False
propose_gitnest_file_import           osfoundry:write ro=False openWorld=False destructive=True
propose_gitnest_native_publication    osfoundry:write ro=False openWorld=True  destructive=True
propose_gitnest_provider_publication  osfoundry:write ro=False openWorld=True  destructive=True
these four = 989 of 12662 tokens
Enter fullscreen mode Exit fullscreen mode

(ऊपर के output से required fields की lists हटा दी गई हैं। Tokens cl100k_base से गिने गए हैं, इसलिए Codex के अपने tokenizer के लिए ये अंदाज़न हैं।) चारों tools list के 12,662 tokens में से 1,000 से कम लेते हैं। जो Codex config इन्हें enabled_tools में लिखता है, वह बाकी 55 को prompt से बाहर रखता है।

दोनों publication tools "open world" क्यों हैं और import क्यों नहीं?

openWorldHint client को बताता है कि कोई tool किसी closed system के बाहर तक पहुँचता है या नहीं। File import एक ऐसी GitNest repository में लिखता है जिसे सिर्फ़ project देख सकता है। Pull request, चाहे native ही क्यों न हो, ऐसी चीज़ है जिसकी notification दूसरे लोगों को जाती है और जिस पर वे action लेते हैं। Codex ऐसे annotations से तय करता है कि कब रुककर पूछना है, इसलिए server के hints approval chain की पहली कड़ी हैं, osFoundry के अपने card से भी पहले।

File import में क्या-क्या भेजा जा सकता है?

ज़्यादातर जवाब schema ही दे देता है। Live inputSchemas के against बारह cases, jsonschema (Draft 2020-12) से validate किए गए:

file_import            20 files                           ok
file_import            21 files                           REJECT maxItems
file_import            one file 100,000 chars             ok
file_import            one file 100,001 chars             REJECT maxLength
file_import            file with mode '100755'            REJECT additionalProperties
file_import            delete: {'path','delete':true}     REJECT required
file_import            idempotency_key 7 chars            REJECT minLength
native_publication     full pin                           ok
native_publication     no expected_composition_oid        REJECT required
provider_publication   expected_change_set_version 0      REJECT minimum
provider_publication   extra 'draft': false               REJECT additionalProperties
provider_publication   extra 'target_branch'              REJECT additionalProperties
Enter fullscreen mode Exit fullscreen mode

एक file entry में सिर्फ़ path और content होते हैं, और कुछ नहीं। Mode bits नहीं हैं, इसलिए कोई script non-executable होकर पहुँचती है। Delete भी नहीं है, क्योंकि delete के लिए बिना content वाली entry चाहिए होती। हर entry किसी file को उसके पूरे text के साथ add या replace करती है, और owner approval card पर ठीक यही पढ़ता है।

एक limit ऐसी है जिसे schema express नहीं कर सकता: description में लिखा 200 KB का total। maxLength characters गिनता है। Budget UTF-8 bytes गिनता है:

JA file: 100,000 chars passes maxLength; UTF-8 = 300,000 bytes
Enter fullscreen mode Exit fullscreen mode

Per-file character limit तक भरी हुई एक Japanese file पहले से ही पूरे commit के budget की डेढ़ गुना है। जो client सिर्फ़ schema check करता है, वह ऐसी calls बनाएगा जिन्हें server को reject करना पड़ेगा। Bytes client पर ही गिनिए।

एक असली import कितने approval cards में बदल जाता है?

Script का Part 3 असली directories को size के हिसाब से first-fit करके 20 files और 200 KB से कम की import calls में बाँटता है, और proposals गिनता है। हर proposal एक card है जिसे owner को खोलना पड़ता है:

GitNest web lib (.ts)           25 files   126,756 chars   127,210 bytes (1.00 B/char) | 2 proposals
   per proposal (files, KB): 20/115, 5/12
GitNest web components (.tsx)   22 files   274,206 chars   274,819 bytes (1.00 B/char) | 2 proposals
   per proposal (files, KB): 10/200, 12/75
Team 2 JA articles (.md)        55 files   359,213 chars   662,285 bytes (1.84 B/char) | 4 proposals
   per proposal (files, KB): 13/200, 16/196, 18/194, 8/73
Enter fullscreen mode Exit fullscreen mode

ये corpora osFoundry के अपने frontend sources और इसी blog के Japanese drafts हैं, सीधे disk से पढ़े गए। छोटी source files पहले 20-file limit से टकराती हैं। बड़े components और non-Latin text पहले byte budget से टकराते हैं। "KB" को 1,000 bytes माना जाए या 1,024, इन तीनों sets में कोई फ़र्क़ नहीं पड़ा।

दूसरी ध्यान देने वाली बात commits की गिनती है। चार proposals मतलब branch पर चार commits, जो एक-एक करके approve होते हैं, और owner इनमें से किसी को भी decline कर सकता है। अगर files सिर्फ़ साथ में ही समझ में आती हैं, तो यह बात हर commit message में लिखिए, या import को इतना छोटा रखिए कि वह एक ही card में आ जाए।

Git में "push only its dedicated branch" का मतलब क्या है?

osFoundry का server-side provider push public नहीं है। Desktop app का अपना push, जिसे GitNest का Push Change Set Branch button इस्तेमाल करता है, public है: gitnest_desktop.rs में gitnest_push_change_set_branch। यह publication preflight से लौटी values लेता है और इसी क्रम में यह करता है:

  1. origin का fetch URL और push URL पढ़ता है, और मना कर देता है अगर दोनों अलग repositories की तरफ़ इशारा करते हैं, या preflight में बताए गए provider, owner और repo की तरफ़ नहीं।
  2. Branch का नाम ठीक osfoundry/change-set-<id> होना ज़रूरी है, जहाँ <id> एक UUID है।
  3. Marker ठीक GitNest-Change-Set: <id> होना ज़रूरी है।
  4. Composition OID का 4 से 64 hex characters का होना ज़रूरी है, फिर git cat-file -e <oid>^{commit} चलाता है।
  5. git update-ref refs/heads/<branch> <oid> <old> चलाता है, जहाँ <old> वह value है जो ref में पढ़ते समय थी, या खाली अगर ref मौजूद नहीं थी।
  6. सिर्फ़ एक refspec push करता है: git push origin refs/heads/<branch>:refs/heads/<branch>।

Script इसे एक bare repository के against दोबारा बनाती है, जो GitHub की जगह खड़ी है। Clone में दो चीज़ें हैं जिन्हें owner ने push नहीं किया: एक local branch wip/secret-pricing और release/2.4 पर एक extra commit। Composed commit बिना worktree के बनाया जाता है (एक throwaway index, hash-object, update-index --cacheinfo, write-tree, commit-tree):

remote refs before: 3  (dana/refactor-cart, main, release/2.4)
`git push --all` would change 2 remote refs: ['release/2.4', 'wip/secret-pricing']
explicit refspec push changed 1 ref: osfoundry/change-set-5b0c2e8e-3f6a-4d2b-9a71-0c4e9d1f7a23 -> 73e59f27bc4f
untouched: True; marker in pushed commit: True
Enter fullscreen mode Exit fullscreen mode

वह दूसरी line ही explicit refspec की पूरी वजह है। Owner के clone से एक आसान-सा push change set के साथ-साथ एक बिना review वाला hotfix और एक private branch भी publish कर देता। Explicit refspec सिर्फ़ एक ref को move करता है, जिसका नाम change set ID से बनता है, इसलिए वह किसी के branch से टकरा नहीं सकता।

क्या-क्या reject होता है?

branch name 'codex/ci' instead of contract     refused: publication branch does not match the preflight contract
marker for another change set                  refused: publication marker does not match the preflight contract
OID 'HEAD' instead of hex                      refused: composition OID must be a hex commit id
hex OID that is not in the clone               refused: fatal: Not a valid object name deadbeefdeadbeefdeadbeefdeadbeefdeadbeef^{commit}
update-ref with a stale expected old value     exit 128: fatal: update_ref failed for ref 'refs/heads/osfoundry/change-set-5b0c2e8e-3f6a-
push URL differs from fetch URL                refused: origin fetch and push remotes point at different repositories
Enter fullscreen mode Exit fullscreen mode

"HEAD" वाला case ज़रूरत से ज़्यादा बारीक लगता है, पर है नहीं। Symbolic name push के वक़्त जो भी checkout है उस पर resolve होता है, और ज़रूरी नहीं कि वह वही हो जो compose और review हुआ था। Hex OID का मतलब सिर्फ़ एक commit हो सकता है। Stale update-ref git का अपना compare-and-swap है: अगर read और write के बीच local branch आगे खिसक गई, तो write चुपचाप उसे replace करने के बजाय fail हो जाता है। अलग URLs वाला check एक कम दिखने वाला छेद बंद करता है: git remote set-url --push से pushes को किसी और जगह भेजा जा सकता है, fetch जहाँ से होता है उससे अलग, और code माँगता है कि दोनों एक ही repository की तरफ़ हों।

Approval के वक़्त का recheck क्या जोड़ता है?

Git steps push को बचाते हैं। दोनों expected_ fields फ़ैसले को बचाते हैं। Script का Part 5 tool descriptions ("Approval rechecks permissions, risk policy, required checks, and target commit") से recheck को दोबारा बनाता है:

as proposed                          publish
recomposed to v4 before the click    refused: change set is v4, card says v3
lint re-ran and failed               refused: required checks not passed: ['lint']
Enter fullscreen mode Exit fullscreen mode

Codex ने version 3 और commit 73e59f27bc4f… पढ़ा, और owner के card पर दोनों दिखते हैं। अगर इस बीच कोई change set को recompose कर दे, तो card approve करने से कुछ ऐसा publish हो जाएगा जिसे किसी ने देखा ही नहीं, इसलिए proposal fail हो जाता है। सिर्फ़ version काफ़ी नहीं, क्योंकि एक ही version की दो compositions अलग हो सकती हैं। सिर्फ़ OID भी काफ़ी नहीं, क्योंकि उससे यह पता नहीं चलता कि owner change set के किस edit की बात कर रहा था। दोनों को pin करने की कीमत client के लिए बस दो fields है।

इसकी कीमत क्या है?

20 runs, local bare remote: compose median 114.2 ms, checks+update-ref+push median 140.0 ms (max 430.0)
Enter fullscreen mode Exit fullscreen mode

छह checks और push, local bare remote पर करीब 140 ms जोड़ते हैं। असली provider के साथ network round trip ही हावी रहता है। पूरी script, 5 live tools/list calls और 21 publications समेत, 11.7 s wall-clock और 3.5 s user CPU में चली।

अगर आप इसे खुद wire करें

  • Agent को change set और उसने जो देखा (version और commit) बताने दीजिए, branch, title, target या token कभी नहीं। Branch का नाम change set ID से derive कीजिए।
  • एक explicit refspec से push कीजिए। ऐसे clone से जिसे कोई इंसान भी इस्तेमाल करता है, कभी --all, --mirror या सिर्फ़ git push नहीं।
  • Local ref को expected old value के साथ लिखिए, और ज़रूरी कीजिए कि fetch और push URLs एक ही repository की तरफ़ हों।
  • जो भी publish होना है, उसके लिए सिर्फ़ hex OIDs accept कीजिए। Symbolic names को review से पहले resolve कीजिए, बाद में नहीं।
  • Import budget bytes में बताइए और client पर check कीजिए। Schema की character limits non-Latin text को नहीं पकड़तीं।
  • जो tools दूसरे लोगों तक पहुँचते हैं, उन्हें openWorldHint: true से mark कीजिए, ताकि client का अपना approval prompt आपके prompt से पहले आए।

Repository में change sets, checks और publication एक साथ कैसे बैठते हैं, यह GitNest page पर है, और protocol वाला हिस्सा MCP glossary entry में।

Top comments (0)