Ini tool yang dipanggil Codex CLI saat ingin membawa change set ke GitHub atau GitLab, persis seperti yang ditampilkan server MCP osFoundry ke client mana pun, baik sudah login maupun belum:
{"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"]}]}
Perhatikan apa yang tidak ada. Tidak ada nama branch, judul, branch tujuan, flag draft, ataupun token. Client hanya menyebut change set dan dua fakta yang ia amati tentang change set itu, dan semua sisanya datang dari pihak owner saat owner memberi persetujuan. "Push only its dedicated branch" adalah janji yang dibedah di post ini. Post ini juga membahas tool yang biasanya dipanggil lebih dulu, propose_gitnest_file_import, dan apa arti budget 200 KB-nya untuk file sungguhan.
Di Mac ini tidak ada binary codex yang terpasang, jadi sisi client-nya berupa script Python yang dijalankan di sini (Apple M1, Python 3.12 lewat uv, git 2.39.5). Angka jaringan diukur dari Jepang.
$ 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
(Daftar field wajib dipotong dari output di atas. Token dihitung dengan cl100k_base, jadi hanya perkiraan untuk tokenizer milik Codex sendiri.) Keempat tool ini memakan kurang dari 1,000 token dari total 12,662 token di daftar. Config Codex yang menyebut keempatnya di enabled_tools membuat 55 tool lainnya tidak masuk ke prompt.
Kenapa kedua tool publikasi bersifat "open world", sedangkan import tidak?
openWorldHint memberi tahu client apakah sebuah tool menjangkau keluar dari sistem tertutup. File import menulis ke repository GitNest yang hanya bisa dilihat oleh project. Pull request, bahkan yang native, adalah sesuatu yang memicu notifikasi ke orang lain dan ditindaklanjuti oleh mereka. Codex memakai anotasi seperti ini untuk memutuskan kapan harus berhenti dan bertanya, jadi hint dari server adalah lapisan pertama rantai persetujuan, bahkan sebelum kartu milik osFoundry sendiri.
Apa saja yang bisa dibawa oleh file import?
Sebagian besar dijawab oleh schema. Dua belas kasus diuji terhadap inputSchema asli, divalidasi dengan jsonschema (Draft 2020-12):
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
Satu entri file hanya berisi path dan content, tidak lebih. Tidak ada mode bit, jadi script akan masuk dalam keadaan tidak executable. Tidak ada delete, karena delete butuh entri tanpa content. Setiap entri menambahkan atau mengganti file dengan teks lengkapnya, dan itu juga persis yang dibaca owner di kartu persetujuan.
Ada satu batas yang tidak bisa dinyatakan schema, yaitu total 200 KB yang disebut di description. maxLength menghitung karakter. Budget-nya menghitung byte UTF-8:
JA file: 100,000 chars passes maxLength; UTF-8 = 300,000 bytes
File berbahasa Jepang yang menyentuh batas karakter per file sudah satu setengah kali budget seluruh commit. Client yang hanya mengecek schema akan membuat call yang terpaksa ditolak server. Hitung byte-nya di sisi client.
Jadi berapa kartu persetujuan untuk import sungguhan?
Bagian 3 script membagi direktori sungguhan ke dalam call import di bawah 20 file dan 200 KB, dengan first-fit berdasarkan ukuran, lalu menghitung jumlah proposal. Tiap proposal adalah satu kartu yang harus dibuka 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
Korpusnya adalah source frontend osFoundry sendiri dan draft berbahasa Jepang dari blog ini, dibaca langsung dari disk. File source kecil lebih dulu mentok di batas 20 file. Komponen besar dan teks non-Latin lebih dulu mentok di budget byte. Mau "KB" dibaca 1,000 atau 1,024 byte, hasil untuk ketiga set ini tidak berubah.
Hal lain yang perlu diperhatikan adalah jumlah commit. Empat proposal berarti empat commit di branch, disetujui satu per satu, dan owner bisa menolak yang mana saja. Kalau file-file itu hanya masuk akal kalau bersama, tulis itu di setiap commit message, atau buat import-nya cukup kecil supaya muat dalam satu kartu.
Apa arti "push only its dedicated branch" di git?
Push ke provider yang dilakukan osFoundry di sisi server tidak publik. Push milik aplikasi desktop, yang dipakai tombol Push Change Set Branch di GitNest, bisa dilihat: gitnest_push_change_set_branch di gitnest_desktop.rs. Fungsi ini menerima nilai yang dikembalikan preflight publikasi lalu melakukan ini, berurutan:
- Membaca fetch URL dan push URL milik
origin, lalu menolak kalau keduanya mengarah ke repository berbeda, atau tidak mengarah ke provider, owner, dan repo yang disebut preflight. - Mewajibkan nama branch persis
osfoundry/change-set-<id>, dengan<id>berupa UUID. - Mewajibkan marker persis
GitNest-Change-Set: <id>. - Mewajibkan composition OID berupa 4 sampai 64 karakter hex, lalu menjalankan
git cat-file -e <oid>^{commit}. - Menjalankan
git update-ref refs/heads/<branch> <oid> <old>, dengan<old>adalah isi ref saat dibaca, atau kosong kalau ref itu belum ada. - Push satu refspec saja:
git push origin refs/heads/<branch>:refs/heads/<branch>.
Script ini membangun ulang alur itu terhadap bare repository yang berperan sebagai GitHub. Clone-nya punya dua hal yang belum di-push owner: branch lokal wip/secret-pricing dan satu commit tambahan di release/2.4. Commit hasil komposisi dibuat tanpa worktree (index sekali pakai, 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
Baris kedua itulah alasan utama refspec eksplisit. Push praktis dari clone owner akan ikut memublikasikan hotfix yang belum di-review dan branch pribadi bersama change set-nya. Refspec eksplisit hanya memindahkan satu ref, yang namanya diturunkan dari ID change set, jadi tidak mungkin bentrok dengan branch milik siapa pun.
Apa saja yang ditolak?
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
Kasus "HEAD" terlihat terlalu rewel, padahal tidak. Nama simbolik akan di-resolve ke apa pun yang sedang di-checkout saat push, dan itu belum tentu yang dikomposisi dan di-review. OID hex hanya bisa berarti satu commit. update-ref dengan nilai lama yang sudah basi adalah compare-and-swap bawaan git: kalau branch lokal bergeser di antara baca dan tulis, penulisan gagal alih-alih menimpanya diam-diam. Cek URL terpisah menutup celah yang lebih halus: git remote set-url --push bisa mengarahkan push ke tempat lain dari asal fetch, dan kodenya mewajibkan keduanya menunjuk repository yang sama.
Apa yang ditambahkan oleh pengecekan ulang saat persetujuan?
Langkah-langkah git melindungi push. Dua field expected_ melindungi keputusannya. Bagian 5 script merekonstruksi pengecekan ulang dari description tool ("Approval rechecks permissions, risk policy, required checks, and target commit"):
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 membaca versi 3 dan commit 73e59f27bc4fâŚ, dan kartu owner menampilkan keduanya. Kalau di sela waktu itu ada yang mengomposisi ulang change set, menyetujui kartu tersebut berarti memublikasikan sesuatu yang belum dilihat siapa pun, jadi proposal-nya gagal. Versi saja tidak cukup, karena dua komposisi dengan versi yang sama bisa berbeda. OID saja juga tidak cukup, karena OID tidak memberi tahu edit change set mana yang dimaksud owner. Mengunci keduanya hanya menambah dua field di client.
Berapa biayanya?
20 runs, local bare remote: compose median 114.2 ms, checks+update-ref+push median 140.0 ms (max 430.0)
Enam pengecekan dan push menambah sekitar 140 ms terhadap bare remote lokal. Dengan provider sungguhan, round trip jaringan yang paling dominan. Seluruh script, termasuk 5 call tools/list sungguhan dan 21 publikasi, memakan 11.7 s wall-clock dan 3.5 s user CPU.
Kalau mau merakitnya sendiri
- Biarkan agent menyebut change set dan apa yang ia amati (versi dan commit), jangan pernah branch, judul, target, atau token. Turunkan nama branch dari ID change set.
- Push dengan satu refspec eksplisit. Jangan pernah
--all,--mirror, ataugit pushpolos dari clone yang juga dipakai manusia. - Tulis ref lokal dengan nilai lama yang diharapkan, dan wajibkan fetch URL dan push URL menunjuk repository yang sama.
- Hanya terima OID hex untuk apa pun yang akan dipublikasikan. Resolve nama simbolik sebelum review, bukan sesudahnya.
- Nyatakan budget import dalam byte dan cek di sisi client. Batas karakter di schema tidak akan menangkap teks non-Latin.
- Tandai tool yang menjangkau orang lain dengan
openWorldHint: true, supaya prompt persetujuan milik client sendiri muncul sebelum milik Anda.
Bagaimana change set, check, dan publikasi saling terhubung di dalam repository dijelaskan di halaman GitNest, sedangkan sisi protokolnya ada di entri glosarium MCP.
Top comments (0)