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