Her yetenek sistemi aynı vaatle satılır: yetenekler istek üzerine yüklenir, yani ucuzdur. Gövde, bir görev gerçekten ihtiyaç duyana kadar diskte bekler. Bu doğru — ve ilginç olan kısım değil.
İstek üzerine yüklenen şey gövde. Her zaman yüklenen şey ise dizin — modelin neye uzanacağına karar verebilmesi için sistem istemine gömülen tüm yetenek adları ve açıklamaları. Bunda tembel hiçbir şey yok. İlk kullanıcı turundan önce oradadır ve hiçbir görev ona dokunmasa bile orada kalır.
Benim makinemde bu dizin 122 yeteneğe kadar büyümüştü. Tahmin etmedim, ölçtüm:
ÖNCE global olarak görünen farklı yetenek : 122 dizin 26362 karakter ~6590 token
SONRA global olarak görünen farklı yetenek : 11 dizin 1455 karakter ~363 token
her zaman açık kümeden çıkarılan yetenek : 111
dizin küçülmesi : %94,5
Gerçek işin ilk token’ından önce harcanan 26.362 karakter — kabaca 6.600 token. Her oturumda, her projede; o yeteneklerin 111’ine asla dokunmayacak projeler dahil.
Asıl Bedel Altı Bin Token Değil
Modern bir bağlam penceresinde 6.600 token hayatta kalınabilir bir sayıdır. Bu rakamı yaranın kendisi gibi sunmak, asıl sorunu küçümser.
Asıl bedel şu: bir dizin bir karar yüzeyidir. Her girdi, bir görevi kendine yönlendirmek için bir davettir. Yüzlerce birbirine benzeyen komşu eklemek yalnızca yer tüketmez; modelin neyi düşündüğünü değiştirir. Dört seçenek arasından seçim yapan bir yönlendirici iyi seçer. Aralarında bir düzine kadarı review, security, analysis veya architecture gibi kelimeleri paylaşan 122 seçenek arasından seçim yapan bir yönlendirici ise tahmine yakın bir şey yapıyor — ve yanlış yönlendirmeye harcanan token’lar, hiç gerçekleşmemiş gerçek işlerle ödenir.
Katalog depolama değildir. Muhakemenin girdisidir ve muhakeme, pencere dolmadan çok önce gürültüden bozulur.
Tembel Yükleme Bir Tasarımın Yarısıdır
Hata, kademeli açıklamaya inanmak değil. Hata, tembel kavramını tek bir alanın özelliği olduğu halde tüm sistemin özelliği sanmak.
Bir yeteneğin iki yarısı vardır:
| Yarı | Yerleşiklik | Açıklama biçimi |
|---|---|---|
| Dizin girdisi (ad + açıklama) | her zaman istemde | kademeli değil |
| Gövde (SKILL.md + betikler) | istek üzerine getirilir | kademeli |
Kademeli açıklama ikinci yarıyı optimize eder ve birincisi hakkında hiçbir şey söylemez. Dolayısıyla bir dizin sınırsızca büyüyebilir, her bir yetenek de kusursuz biçimde tembel kalır — ve her zaman açık olan dizin onunla birlikte büyür; tek tek, görünmez biçimde, çünkü hiçbir tek ekleme pahalı değildir.
Bozulmanın şekli budur: kimsenin yük olarak sınıflandırmadığı parçada sınırsız büyüme.
Yalnızca Ekleme Yapabilen Araç
İlk düzeltme denemesi bir betikti. Verilen bir proje için, o projenin ihtiyaç duyduğu yetenekleri <proje>/.agents/skills/ altına sembolik bağla. Fikir makuldü. Başarısız oldu ve nedeni tam olarak adlandırmaya değer:
yalnızca ekleme yapabiliyordu.
Kapalıyı ifade etmenin hiçbir yolu yoktu. Global kopya tam olduğu yerde kalıyordu ve bir koşum takımı global köklerle proje köklerini bağımsız olarak taradığı için “yalnızca bu proje için etkin” aslında “artık iki yerde birden var” demekti. Bu davranışı belgeleyen dört yetenek de global kirlenmeyi önlediğini iddia ediyordu — üretemeyeceği bir mekanizmaya güvenerek.
Açık, bir eylem olarak ifade edilebilir. Kapalı ise tasarlamanız gereken bir durumdur. Yalnızca ekleyen araçlar onu asla üretemez. Bu, unsetsiz bir yapılandırma sistemi ya da yalnızca izin kuralı olan bir güvenlik duvarı ile aynı hatadır: eksik olan ifade gücü, kaldıran yarıdır.
Opt-In Kavramı Olmayan Bir Dağıtım
Gerçek neden tek bir fonksiyondu. Bir kurulum betiği, paylaşılan yetenek dizinini gezip her alt dizini tanıdığı her koşum takımı köküne sembolik bağlıyordu:
# orijinal dağıtım, kısaltılmış
for skill_dir in "$SCRIPT_DIR"/tui-agent-settings/skills/*/; do
[ -f "${skill_dir}SKILL.md" ] || continue
link_file "$skill_dir" "$dest_root/$(basename "$skill_dir")"
done
Altı koşum takımı kökü. Filtre yok, kapsam kavramı yok.
Yani her yetenek inşa gereği globaldi — bir karar sonucu değil. Sistemin hiçbir yerinde “bu opt-in” diyen bir veri yoktu, çünkü sistemin sahip olduğu tek mod global moduydu. Teşhis “çok fazla yetenek” değil. Teşhis şu: deponun bir boyutu vardı — yetenek listesi — ve ikinci boyutu eksikti: kapsam. Tek boyutlu bir depo, listeyi ne kadar özenle düzenlerseniz düzenleyin, yerleşiklik hakkında bir politika ifade edemez.
Opt-In’i Yapısal Kılan Dört Kural
Opt-in, işaretleyip hatırladığınız bir bayrak değildir. Yapısal olarak doğru olmalıdır, yoksa birinin kurulum betiğini çalıştırdığı ilk anda çürür. Dört kural bunu başardı:
1. Tek fiziksel kopya. Yeteneği kütüphane tutar. Hiçbir şey onu çoğaltmaz. Çoğaltma, taşımayı tehlikeli ve “hangi kopya kazandı” sorusunu belirsiz yapan şeydir.
2. Opt-in bir kopya değil, bir bağdır. <proje>/.agents/skills/x → <kütüphane>/x. Projeye özel, kütüphane güncellemelerini izler, kayamaz, unutulacak dosya bırakmaz.
3. Global kökler asla bağ hedefi değildir. Global bir köke bağ kurmak özellik değil, hatanın kendisidir — bu yüzden etkinleştirici bunu reddeder:
✗ Refusing to link into a global skill root (/home/devhax/.agents) — that is global pollution, not opt-in.
4. Değişmez kural denetlenebilir, istisnalar ise bildirilmiş veridir. Kural şudur: kütüphane ∩ global = ∅. Bilerek global tutulan yetenekler açık bir listede yaşar ve denetleyici onları sessizce hoş görmek yerine kasten bırakılmış olarak yazdırır. Yazmak zorunda olduğunuz bir istisna, yaptığınızı unutamayacağınız istisnadır.
Son madde, bir politika ile bir temenni arasındaki farktır. Disiplin bir mekanizma değildir.
Basit Bir Taşımanın Hasar Yarıçapı
111 dizini taşımak bir dosya sistemi işlemi gibi görünüyordu. Oysa bir graf işlemiydi ve grafın büyük kısmı görünmezdi.
Kurulum betiği paketleri global deponun üzerinden dağıtmıştı; dolayısıyla diğer koşum takımları ~/.agents/skills/<ad> işaret eden sembolik bağlar tutuyordu. Bu dizinleri taşımak 111 dosyayı taşımadı. Yedi koşum takımı kökünde 99 bağı sessizce kırdı — pi, omp, commandcode, Claude, Gemini, Cline ve muse — hepsi bir an önce sorunsuz çalışıyordu.
44 tanesini bakarak buldum. Kalanını ise bir doğrulama testi, aklıma gelmeyen kökleri taradığında buldu:
find "$root" -maxdepth 1 -xtype l | wc -l # her kök için ayrı ayrı, bir kez değil
İki ders, ikisi de genel:
Bir şeyi taşımadan önce ona işaret eden her şeyi bulun. Taşımadan sonra find -xtype l çalıştırmak bir doğrulama stratejisi değil, bir özürdür.
İkinci bir koşum takımı, birincisiyle eşitlik iddia eden bir test tutabilir. Bu köklerden birinde, yetenek kümesinin pi’ninkiyle birebir aynı olmasını şart koşan bir test vardı. Yetenekleri bir kökten kaldırıp diğerinden kaldırmamak, bir temizliği kırmızı bir teste dönüştürdü — haklı olarak. Onarım, testin korumak için var olduğu eşitliği zayıflatmak değil, her iki kökten de kaldırıp o eşitliği korumaktı.
Hiç Başarısız Olmamış Bir Kapı, Kapı Değildir
Depoda artık bir doctor komutu var. Üç şeyi denetliyor: global bir kökte kütüphane yeteneği bulunmaması, paket listelerinin dosya sisteminden sapmaması ve kurulum betiğinin dağıtım dizininde kütüphane yeteneği durmaması — çünkü iki yerde birden olan bir yetenek, kurulum betiği bir sonraki çalıştığında yeniden globale taşınır.
Ama önemli olan denetim değil. Önemli olan, denetimin güvenilmeden önce kasten yanlışlanmış olması:
# tam olarak aynı hatayı, kasten geri getir
$ cp -r tui-agent-settings/skills-library/interfaces/break ~/.agents/skills/break
$ ./scripts/enable-skills.sh doctor
✗ LEAK: 'break' is in the library AND in /home/devhax/.agents/skills
✗ doctor: 1 problem(s) found. # çıkış 1
$ rm -rf ~/.agents/skills/break
$ ./scripts/enable-skills.sh doctor
✓ doctor: no leaks, no suite drift. # çıkış 0
Yalnızca ✓ yazdırmış bir denetim, başka bir şey yazdıramayan bir denetimden ayırt edilemez. Kapı bir anlam taşıyor, çünkü onu gerçek bir kusur üzerinde ateşlenirken ve sonra temizlenirken izledim.
Hatayı kazara da geri getirdim — kütüphaneye ait bir yeteneği dağıtım dizininde bırakarak. Kapı bunu yakaladı ve bugün o denetimin var olma sebebi tam da bu. Çıkardığım kural: bir koruma ateşlendiğinde, ona neden olan durumu ekle. Belirtiyi düzeltip tespiti kodlamamak, aynı arızanın tanıksız geri dönmesi demektir.
Bedeli ve Getirisi
Hiçbir şey silinmedi. Her zaman açık kümeden çıkan 111 yeteneğin tamamı hâlâ kurulu ve hâlâ kullanılabilir — yalnızca önceden yüklenmiyorlar:
$ enable-skills.sh specterops:appsec ~/work/service
Enabled SpecterOps appsec (8 skills) in /home/devhax/work/service/.agents/skills
$ enable-skills.sh doctor
✓ doctor: no leaks, no suite drift.
Opt-in kütüphanesi 112 yetenek barındırıyor. Bir grubu etkinleştirmek tek komut; devre dışı bırakmak ise bir bağ dizinini silmek — yani sistemin gerçekten temsil edebildiği bir durum.
| Önce | Sonra | |
|---|---|---|
| Her zaman yerleşik yetenek girdisi | 122 | 11 |
| Dizin boyutu | 26.362 karakter | 1.455 karakter |
| İşe başlamadan önce yaklaşık token | ~6.590 | ~363 |
| Küçülme | — | %94,5 |
Genel Kural
Kapalı olan bir yeteneğin yaşayacak bir yeri olmalıdır. Bir şeyi silmek onu ertelemek değildir; 111’in tüm değeri, tek bir komut uzaklıkta kalmalarında. Opt-in, okuma anında uygulanan bir filtre değil, bir yerleşiklik politikasıdır.
Bu desen, bir ajanın taşıdığı her kayıt defteri için geçerli: MCP araç tanımları, eklenti bildirimleri, istem şablonları, lint kural kümeleri, model listeleri. Her birinin bir dizini ve bir gövdesi vardır ve dizin hiçbir zaman bedava değildir. Gövdeler hiçbir zaman sizin sorununuz olmadı; onları çoktan çözdünüz. Dizin, siz başka bir yere bakarken büyüyen parçadır.
Öyleyse sürdürdüğünüz her kayıt defterine iki soru sorun:
Devre dışı bir girdi nerede yaşar ve “devre dışı”yı temenni değil de yapısal olarak doğru kılan ne?
İkincisinin yanıtı hiçbir şey denetlemiyor ise, bir kapsam sisteminiz yok. Bir alışkanlığınız var — ve alışkanlıklar, kurulum betiğini çalıştıran bir sonraki kişiyi atlatamaz.