cd /news/ai-agents/kenapa-osfoundry-mengirim-schema-rec… · home › topics › ai-agents › article
[ARTICLE · art-167583] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=· neutral

Kenapa osFoundry mengirim schema record di setiap halaman yang di-list Codex?

A developer rebuilt osFoundry's MCP record tools so that each listed page returns the channel's schema alongside item previews, since the edit tool's input schema only exposes values as a generic object and cannot describe per-channel fields. The writeup measures four levels of validation-error detail, arguing that API messages naming the failing field are usually enough for a self-correcting retry, and compares a before_id cursor against offset paging plus an event log for resuming later.

by read8 min views2 publishedNov 11, 2026

Berikut tool record channel yang didapat koneksi Codex CLI dari osFoundry, seperti yang di-list oleh endpoint MCP publiknya. Tool ini diambil dengan tools/list tanpa autentikasi pada 2026-11-09 dan dipangkas ke field yang relevan di sini:

{"name": "list_record_items",
 "description": "List the schema and a bounded page of item previews from one granted record channel. Use read_record_item for the complete values.",
 "inputSchema": {"required": ["record_channel_id"],
   "properties": {"record_channel_id": {"type": "string"}, "before_id": {"type": "string"},
                  "limit": {"type": "integer", "minimum": 1, "maximum": 50}}},
 "annotations": {"readOnlyHint": true}}

{"name": "read_record_item",
 "description": "Read one item by ID from a granted record channel and return its current version.",
 "inputSchema": {"required": ["record_channel_id", "item_id"]}}

{"name": "edit_record_item",
 "description": "Update validated fields in one record item. Supply the version from read_record_item; concurrent changes are rejected.",
 "inputSchema": {"required": ["record_channel_id", "item_id", "values", "expected_version", "idempotency_key"],
   "properties": {"values": {"type": "object"}}},
 "annotations": {"readOnlyHint": false, "destructiveHint": true},
 "securitySchemes": [{"type": "oauth2", "scopes": ["osfoundry:write"]}]}

Perhatikan values. Isinya {"type": "object"}, tidak lebih. Schema tool tidak bisa memberi tahu key apa saja yang dimiliki sebuah channel, mana yang field Select, atau apa yang boleh diisi ke Points, karena semua itu beda per channel dan berubah saat seseorang mengedit tab Schema. Satu-satunya tempat Codex bisa mengetahuinya adalah baris pertama deskripsi tool list: schema ikut kembali bersama halaman. Sisa tulisan ini mengukur apa yang didapat dari situ, pada sebuah rebuild, beserta dua pilihan lain di empat tool yang sama: cursor before_id alih-alih offset, dan event log untuk kembali lagi nanti.

Tipe field-nya tetap. packages/shared/src/types/record.ts di frontend osFoundry mendefinisikan RecordChannelFieldType sebagai 16 tipe: text, number, date, boolean, email, phone, url, select, multi_select, currency, percentage, rating, relation, file, json, dan formula. Setiap field membawa is_required, is_unique, dan config khusus per tipe: max_length untuk text, min/ max untuk number, max_stars untuk rating, options untuk dua tipe select, expression untuk formula.

Select adalah yang paling sering menjebak agent. Sebuah opsi berbentuk {value, label, color}. Yang disimpan adalah value, sedangkan label adalah yang dilihat orang. RecordDataGrid.tsx me-render sel select dengan options.find((o) => o.value === params.value) dan fallback ke String(params.value) di chip yang pudar kalau tidak ada yang cocok. Jadi kalau pengecekan yang longgar membiarkan "In progress" masuk ke field yang value-nya in_progress, grid akan menampilkan sesuatu yang kelihatan benar padahal salah: tanpa warna, dan tidak terlihat oleh apa pun yang mengelompokkan atau memfilter berdasarkan value. Model yang hanya pernah melihat label di grid, atau pesan dari seseorang, akan mengirim label. Harus ada yang menolak.

API record osFoundry membalas panggilan yang gagal dengan {ok: false, error: {message}}. Tool record milik aplikasi desktop untuk agent di dalam aplikasi (src-tauri/src/record_tools.rs) meneruskan pesan itu ke model apa adanya, dan komentarnya menjelaskan alasannya: pesan API "names the field that failed validation, which is usually enough for a self-correcting retry." Unit test-nya memakai values.status is not a valid option sebagai contoh.

"Usually enough" itu bisa diukur. Rebuild ini mencoba empat tingkat detail dalam sebuah penolakan:

{"error": "validation_failed"} Ini tingkat "values", persis dari hasil run:

{"error": "validation_failed", "fields": [
  {"key": "status", "message": "values.status must be one of the option values, got \"In progress\"",
   "allowed": ["new", "triaged", "in_progress", "fixed", "wont_fix"]},
  {"key": "owner", "message": "values.owner must be one of the option values, got \"Aiko\"",
   "allowed": ["aiko", "mika", "ren"]},
  {"key": "points", "message": "values.points must be between 0 and 13, got 20", "min": 0, "max": 13},
  {"key": "due", "message": "values.due must be a date as YYYY-MM-DD, got \"Friday\""},
  {"key": "age_days", "message": "values.age_days is a formula field and is computed, not written"}]}

String-string ini milik rebuild. Hanya penamaan values.<key> yang mengikuti osFoundry.

Tidak ada binary Codex yang dijalankan di sini. record_store.py mengimplementasikan list_record_items, read_record_item, edit_record_item, dan poll_resource_events di atas sqlite in-memory, dengan nama tool, nama argumen, dan batas yang sama seperti versi live. Tipe field dan key config diambil dari record.ts. Panjang preview (120 karakter), bentuk hasil, dan semua aturan validasi adalah buatan rebuild sendiri. Sebuah edit berjalan di dalam BEGIN IMMEDIATE: lookup idempotensi, perbandingan versi, validasi, merge, kenaikan versi, append event, commit. Masalah apa pun akan me-rollback seluruh edit.

triage.py adalah Codex yang di-script. Ia bekerja pada bug tracker dengan 11 field (Contoh: ticket, title, description, status, owner, severity, points, area, due, reporter, age_days), 200 item New per run, seed 1–5. Setiap item mendapat niat triage dalam kata-kata seseorang: "In progress", "Aiko", "S2 Major" atau cuma "major", points dari {1, 2, 3, 5, 8, 13, 20}, due "Friday" atau tanggal ISO, satu area. Agent bisa mengirim kata-kata itu apa adanya, atau memetakannya dulu lewat schema dengan mencocokkan label ke value. Saat ditolak, ia memperbaiki apa yang bisa diperbaiki berdasarkan penolakan, membuang yang tidak bisa, dan menandai field itu untuk ditangani seseorang. Ia tidak pernah memotong angka ke batas. Python 3.9.6, Apple M1.

1,000 item, masing-masing dengan 6 field yang ingin diisi:

Agent membaca schema Isi penolakan Field tersimpan (dari 6,000) Tersisa untuk manusia Panggilan edit per item
tidak tidak ada 1,363 (22.7%) 4,637 7.00
tidak field mana 1,363 (22.7%) 4,637 2.00
tidak field, alasan, nilai yang diizinkan 5,361 (89.3%) 639 2.00
tidak field, alasan, nilai dan label 5,857 (97.6%) 143 2.00
ya tidak ada 5,857 (97.6%) 143 1.86
ya field mana 5,857 (97.6%) 143 1.14
ya nilai, atau nilai dan label 5,857 (97.6%) 143 1.14

97.6% adalah batas atasnya. Sisa 143 adalah item di mana orangnya bilang 20 points padahal field-nya berhenti di 13. Item seperti itu memang harus ditolak, dan memang ditolak di setiap baris.

Agent yang membaca schema mencapai batas atas apa pun isi penolakannya. Edit pertamanya hanya gagal untuk 143 points yang di luar rentang. Tanpa schema, setiap edit pertama gagal: status, owner, severity, dan area berisi label, dan separuh tanggal due berisi "Friday". Dari situ, pemulihan sepenuhnya bergantung pada error-nya. "Field mana" tidak berguna tanpa nilai yang diizinkan: agent tahu status salah tapi tidak tahu yang benar, jadi ia membuangnya. Nilai yang diizinkan tanpa label mencapai 89.3%. 639 yang meleset adalah "Won't fix" lawan wont_fix dan "major" lawan s2. Keduanya tidak bisa dicocokkan dari daftar nilai. Dengan label di penolakan, agent tanpa schema bisa menyusul, dengan satu panggilan tambahan per item. Error singkat tanpa schema membuat agent memecah setiap edit menjadi enam edit satu field, 7 panggilan per item, dan tetap saja yang tersimpan kurang dari seperempat.

Jadi schema adalah separuh dari desain validasi. Pengecekan di server menghentikan nilai yang salah. Schema yang ikut di setiap halaman adalah yang membuat percobaan pertama bisa langsung benar. Biayanya 1,705 byte per halaman di channel ini, sekitar 10% dari halaman 16.7 KiB berisi 50 preview.

Satu halaman berisi 50 preview berukuran 16.7 KiB. Membaca 50 item itu secara penuh memakan 124.6 KiB, karena deskripsi di sini panjangnya 300–4,000 karakter. Untuk triage, preview sudah cukup untuk memilih apa yang perlu dibuka: status, ticket, awal judul. Codex membuka item yang akan ia ubah. Halaman yang dibatasi juga mencegah channel besar membanjiri context. Deskripsi tool menyebut "bounded" dan schema-nya menyebut maximum: 50.

before_id dan bukan offset? Orang terus membuat bug baru selagi Codex membaca. Rebuild ini mem-page 600 item, 50 per halaman, dari yang terbaru, dan mengubah channel setelah setiap halaman. Lima run:

Perubahan setelah tiap halaman Paging Halaman Pembacaan item Terbaca dua kali Tidak pernah terlihat
2 bug baru dibuat offset 65 3,120 120 0
2 bug baru dibuat before_id 60 3,000 0 0
2 item yang sudah dibaca dihapus offset 60 2,890 0 110
2 item yang sudah dibaca dihapus before_id 60 3,000 0 0

Item baru di bagian atas mendorong semuanya ke bawah, jadi halaman offset saling tumpang tindih dan Codex men-triage beberapa item dua kali. Menghapus sesuatu yang sudah dibaca menggeser semuanya ke atas, jadi halaman offset berikutnya mulai setelah item yang belum dibaca siapa pun. 110 bug yang ada dari awal sampai akhir tidak pernah terlihat. Cursor menunjuk item terakhir yang dibaca, dan insert atau delete di tempat lain tidak bisa menggesernya. Item yang dibuat selama proses berjalan tidak muncul di halaman mana pun. Untuk itulah event log ada.

Setelah satu proses atas 400 item, orang mengedit 30 item, membuat 12, dan menghapus 3. Dua cara untuk menemukan 45 itu:

Strategi Panggilan Data yang dikembalikan Ditemukan
list ulang semua halaman, bandingkan versi 9.0 135.8 KiB 45 dari 45
poll_resource_events dari sequence yang disimpan 1.0 4.6 KiB 45 dari 45

Script-nya memakai assert untuk memastikan keduanya menemukan tepat 45. Biaya polling sebesar apa yang berubah. Biaya list ulang sebesar ukuran channel, setiap kali. State yang perlu disimpan antar-run hanya satu integer, sequence terakhir.

Singkat saja, karena cara kerjanya sama seperti untuk Notes dan knowledge base. Dalam 1,000 edit di mana seseorang mengubah 1 dari 8 item di antara read dan write milik Codex, versi dari hasil read menghasilkan 131 konflik dan menimpa 0 perubahan orang tersebut. Membaca ulang versi tepat sebelum menulis menghasilkan 0 konflik dan menimpa semua 131. Setelah konflik, Codex yang di-script membaca ulang, melihat item-nya sudah in progress, dan hanya menambahkan severity.

2,000 edit valid berisi enam field, tiga run: median 72.8–75.1 µs per edit dengan validasi, 41.9–42.4 µs dengan validate() diganti stub. Selisihnya adalah query schema dan pengecekan per field di dalam transaksi. Dibandingkan satu giliran model yang butuh beberapa detik, ini tidak terasa.

Definisi singkat osFoundry tentang protokol di baliknya ada di entri glosarium MCP. Layar record channel tempat schema berasal ada di halaman Records.

── more in #ai-agents 4 stories · sorted by recency
── more on @osfoundry 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/kenapa-osfoundry-men…] indexed:0 read:8min 2026-11-11 · —