Ini tool yang dipanggil Claude Code ketika ingin check GitNest yang gagal dijalankan lagi, persis seperti yang dicantumkan endpoint MCP live osFoundry di tools/list (katalognya bisa diambil tanpa token):
{
"name": "propose_gitnest_check_rerun",
"description": "Ask the repository owner to retry one finished check on a specific composed change-set version. Approval rechecks repository access, version, commit and check state.",
"inputSchema": {
"type": "object",
"properties": {
"repository_id": {"type": "string"},
"change_set_id": {"type": "string"},
"check_name": {"type": "string", "maxLength": 200},
"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", "check_name",
"expected_change_set_version", "expected_composition_oid", "idempotency_key"],
"additionalProperties": false
},
"annotations": {"readOnlyHint": false, "openWorldHint": false, "destructiveHint": true},
"securitySchemes": [{"type": "oauth2", "scopes": ["osfoundry:write"]}]
}
Pasangan read-nya menerima dua argumen: list_gitnest_checks {repository_id, change_set_id}, "bounded check status for the current composed commit of a change set". Dua read lain ikut jadi bahan keputusan: list_gitnest_saved_results (limit 1–50) dan read_gitnest_work (kind berisi tasks, changes, atau runs). Ketiganya osfoundry:read dengan readOnlyHint: true.
Enam field wajib hanya untuk menjalankan ulang satu check itu banyak. Tombol CI cukup butuh ID job. Dua field tambahannya, versi dan commit, adalah topik tulisan ini. Di Mac ini tidak ada binary claude yang terpasang, jadi checks_sim.py yang berperan sebagai client. Kode dan output lengkapnya ada di code/.
Apa arti "hijau" saat agent membaca check?
Sebelum mengusulkan apa pun, agent harus membaca daftar check dengan benar. UI GitNest di osFoundry merangkum check run sebuah commit dengan satu fungsi murni kecil (summarise di check-runs.ts). Urutan pengecekannya itulah aturannya:
- Tidak ada run sama sekali berarti
none, tidak pernahpassed. - Ada
failure,timed_out, ataucancelledberartifailed. - Apa pun yang belum
successmembuatnya tetaprunning, termasuk status yang tidak dikenali client. - Baru setelah itu
passed.
Script-nya mem-port aturan itu dan membandingkannya dengan dua jalan pintas yang biasa ditulis model sendiri:
== verdict ==
case summarise no-failure ratio
no checks none green green
all passed passed 2 green green
one queued running 1/2 green red
one failed failed: e2e red red
one timed out failed: e2e green red
one cancelled failed: e2e green red
unknown status running 1/2 green red
shortcut wrong: no-failure 5/7, ratio 1/7
"Tidak ada yang gagal, jadi hijau" salah di 5 dari 7 kasus. Commit yang belum diperiksa dianggap hijau, check yang masih antre dianggap hijau, timeout juga hijau. Cek rasio (passed == total) lebih mendekati, tapi dengan nol check 0 == 0 dan hasilnya juga hijau. Justru kasus ini yang disebut komentar di kode GitNest sebagai yang paling menyesatkan, karena commit tanpa check sama sekali belum diverifikasi. UI menampilkannya sebagai "No checks have run against this commit", tanpa centang.
Kalau kamu meminta Claude Code menjawab "sudah hijau?", berikan empat aturan ini di CLAUDE.md. Tool-nya mengembalikan status, bukan vonis.
Kenapa rerun membawa versi dan commit?
Karena orang yang menyetujuinya sedang melihat commit tertentu, dan change set bisa berubah sebelum dia klik.
Di GitNest, change set disusun dari saved result menjadi satu commit. Kalau disusun ulang, karena ada saved result baru atau base-nya bergeser, change set itu dapat versi baru dan commit gabungan baru, dan check-nya mulai dari awal lagi. Usulan rerun menunggu manusia. Kalau isinya hanya "rerun e2e on cs_4e81a0", persetujuan akan menjalankan ulang e2e pada change set dalam kondisi saat itu, yang bisa jadi commit yang belum pernah dilihat pemiliknya.
checks_sim.py memodelkan masa tunggu itu. Tiap percobaan adalah satu usulan. Agent membaca versi 3, commit A, dengan e2e gagal. Waktu review pemilik berdistribusi log-normal dengan median 8 menit. Ini asumsi, bukan hasil pengukuran. Selama menunggu, change set disusun ulang sebagai proses Poisson, rata-rata tiap 45 menit, dan setelah disusun ulang check butuh 4 menit untuk jalan. Di 10% percobaan, sesi Claude Code kedua membaca kegagalan yang sama dan mengajukan usulannya sendiri dengan key sendiri, lalu pemilik menyetujui keduanya. Tiga guard saat persetujuan, masing-masing 10.000 percobaan:
== approval-time guards (n=10000, review median 8 min, recompose mean every 45 min, 10% second session) ==
guard ran on reviewed commit ran on unreviewed commit duplicate run refused (stale) refused (not finished)
none 7789 1411 1764 0 0
version+oid 7789 0 740 2435 0
full 7789 0 0 2435 740
Tanpa guard, 1.411 persetujuan menjalankan ulang check di commit yang belum di-review pemilik, dan 1.764 mengantrekan check yang sudah antre atau sedang jalan. Membandingkan versi dan commit saat persetujuan menurunkan run yang belum di-review jadi 0, dan sebagai gantinya menolak 2.435 kartu yang sudah basi. Sisa 740 duplikat itu berasal dari sesi kedua: versi sama, commit sama, tapi persetujuan pertama sudah mengantrekan ulang check-nya. Guard ketiga, "apakah check ini masih berstatus selesai?", yang menangkapnya. Itulah set lengkap yang disebut di deskripsi tool: "Approval rechecks repository access, version, commit and check state."
Laju penyusunan ulang adalah input yang paling tidak pasti, jadi berikut baris tanpa guard di tiga laju:
== sensitivity: unreviewed-commit runs with guard 'none' ==
recompose every 15 min unreviewed 2449 duplicates 3128
recompose every 45 min unreviewed 1411 duplicates 1764
recompose every 180 min unreviewed 452 duplicates 1208
Bahkan change set yang berubah tiap tiga jam menghasilkan 452 run di commit yang salah per 10.000. Di happy path guard ini tidak ada biayanya: jumlah run di commit yang sudah di-review tetap 7.789 di ketiga baris.
Kenapa versi dan commit sekaligus?
Di model ini keduanya selalu bergerak bersama, jadi salah satunya saja akan memberi skor yang sama. Tapi keduanya menjawab pertanyaan berbeda. Versi adalah counter milik change set sendiri, dan berubah saat isinya berubah. Commit adalah tempat check benar-benar dijalankan, dan itulah yang ditampilkan kartu sebagai "Reviewed commit: …". Dengan meminta client mengirim keduanya, server bisa menolak berdasarkan salah satunya, dan pemilik membaca hash commit di kartu, bukan counter internal. Schema memaksa versi berupa integer ≥ 1. Commit berupa string bebas, jadi pengecekannya terjadi di server.
Apa yang ditangkap schema sebelum pemanggilan?
Script-nya memvalidasi argumen terhadap inputSchema live dengan jsonschema:
== propose_gitnest_check_rerun arguments ==
ok PASS
no oid REJECT 'expected_composition_oid' is a required property
version 0 REJECT 0 is less than the minimum of 1
version as string REJECT '3' is not of type 'integer'
two checks REJECT ['e2e', 'lint'] is not of type 'string'
check 201 chars REJECT 'ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
key 7 chars REJECT 'abcdefg' is too short
extra 'force' REJECT Additional properties are not allowed ('force' was unexpected)
required: repository_id, change_set_id, check_name, expected_change_set_version, expected_composition_oid, idempotency_key
Dua penolakan di sini soal desain, bukan sekadar kerapian. check_name adalah string, bukan list, jadi "jalankan ulang semuanya" berarti satu kartu per check dan pemilik memutuskan satu per satu. Lalu additionalProperties: false membuat model yang mengarang "force": true mendapat error, bukan flag yang diam-diam diabaikan.
Idempotency key sebaiknya diturunkan dari isi usulannya, bukan dari percobaannya. Script-nya memakai "rerun-" + sha256(change_set_id, version, oid, check_name)[:32]:
key retry same: True key after recompose same: False len 38
Retry setelah respons hilang mengirim key yang sama dan tidak membuat kartu kedua. Penyusunan ulang mengubah key-nya, jadi usulan untuk commit baru menjadi kartu baru, dan memang begitu seharusnya.
Seberapa berat tool ini, dan seberapa jauh server-nya?
== tools/list ==
59 tools, 48140 B via json.dumps, 12722 tok (cl100k)
list_gitnest_checks 612 B 158 tok readOnly=True destructive=False scope=osfoundry:read
list_gitnest_saved_results 707 B 182 tok readOnly=True destructive=False scope=osfoundry:read
read_gitnest_work 685 B 189 tok readOnly=True destructive=False scope=osfoundry:read
propose_gitnest_check_rerun 1011 B 268 tok readOnly=False destructive=True scope=osfoundry:write
all 13 gitnest tools 11227 B 3009 tok
13 tool GitNest memakan sekitar seperempat token katalog (dihitung dengan tiktoken cl100k_base, jadi anggap saja perkiraan). Sepuluh panggilan tanpa autentikasi untuk masing-masing dari Mac ini di Jepang ke endpoint live:
$ python3 probe.py
initialize HTTP 200 308 B median 566 ms min 326 max 807
capabilities: {"tools": {"listChanged": false}}
tools/list HTTP 200 45368 B median 722 ms min 420 max 1406
tools/call list_gitnest_checks HTTP 401 337 B median 552 ms min 409 max 903
Respons 401 membawa error_description="Connect osFoundry to continue" dan URL resource_metadata, dan itulah yang membuat Claude Code masuk ke alur OAuth-nya. initialize tidak mengiklankan push, jadi tidak ada yang memberi tahu client bahwa retry sudah disetujui atau check sudah selesai. Menunggu check itu berarti pekerjaan runner di mesinmu ditambah satu klik manusia, hitungannya menit, bukan milidetik. Pola yang hemat adalah mengakhiri giliran setelah mengusulkan, lalu memanggil list_gitnest_checks sekali saat user bilang sudah menyetujui.
Siapa yang sebenarnya menjalankan check?
Bukan osFoundry. Panel runner GitNest menyebut check "run on your machine, not ours". Runner yang terdaftar melakukan polling dengan token-nya sendiri (OSF_RUNNER_TOKEN=… osfoundry-runner --api https://api.osfoundry.io), dan tanpa runner check akan "stay queued". Menyetujui retry hanya mengantrekan ulang check. Claude Code tidak pernah memegang token runner, dan tool read tidak menyertakan binding lokal maupun kredensial. Agent bisa melihat bahwa e2e gagal. Agent tidak bisa membuat apa pun berjalan tanpa kamu dan runner-mu.
Kalau kamu memasangnya sendiri
- Taruh aturan vonis di instruksi client, jangan hanya di UI-mu. "Tidak ada check" dan "status tidak dikenal" adalah titik di mana agent melaporkan hijau.
- Buat rerun menyebut commit yang dipakai saat review, dan bandingkan saat persetujuan, bukan saat pengusulan.
- Cek ulang juga status check itu sendiri saat persetujuan. Versi dan commit tidak bisa menangkap dua usulan untuk kegagalan yang sama.
- Terima satu check per usulan. List mengubah retry untuk test yang flaky menjadi rerun massal yang tidak dipilih siapa pun.
- Turunkan idempotency key dari (change set, versi, commit, check), supaya retry tergabung dan penyusunan ulang tidak.
- Jauhkan eksekusi dari platform dan dari agent: runner memegang token, manusia memegang tombolnya.
Halaman review change set beserta panel check-nya ada di halaman GitNest. Untuk protokolnya sendiri, lihat entri glosarium MCP.
Top comments (0)