DEV Community

Cover image for Kenapa osFoundry mewajibkan Claude Code menyebut commit saat meminta check GitNest dijalankan ulang?
BBT for BBT Indonesia

Posted on

Kenapa osFoundry mewajibkan Claude Code menyebut commit saat meminta check GitNest dijalankan ulang?

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"]}]
}
Enter fullscreen mode Exit fullscreen mode

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:

  1. Tidak ada run sama sekali berarti none, tidak pernah passed.
  2. Ada failure, timed_out, atau cancelled berarti failed.
  3. Apa pun yang belum success membuatnya tetap running, termasuk status yang tidak dikenali client.
  4. 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
Enter fullscreen mode Exit fullscreen mode

"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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)