lco-spec
v0.2.1
Published
Turn natural-language intent into schema-validated, lintable, freezable application specs — with an LLM council or a single model.
Readme
lco-spec — Spec IR Çekirdeği (Kanıt Kapısı Deneyi)
Bu paket, LLM konsey mimarisinin kanıt kapısı (evidence gate) deneyinin çekirdeğidir: spekülasyonların (intent, sözlük, varsayım, kanıt, gereksinim, karar, sözleşme, görev) şemayla doğrulandığı, derlendiği, dondurulduğu (freeze + artifact hash), izlenebilirlik ve lint kurallarıyla susturulamaz hale getirildiği Spec IR katmanı — ve bu çekirdeğin iddialarını ölçen deterministik değerlendirme (eval) altyapısı.
Deneyin sorusu: "Konsey, tek ajandan ölçülebilir şekilde daha mı doğru — ve maliyeti kabul edilebilir mi?" Bu paket o soruya kanıtla cevap vermeyi hedefler; tahminle değil.
Çekirdek iki yüzeyden tüketilir: lco CLI (11 komut: compile, lint, freeze, verify,
change, trace, plan, init, check, generate, doctor) ve lco-mcp stdio sunucusu
(10 MCP aracı) — ikisi de aynı saf komut çekirdeklerini çağırır.
Kurulum
npm'den (publish sonrası):
npm install lco-spec # yayınlanınca; bin'ler: lco, lco-mcp
npx lco --helpKaynaktan (bu monorepo içinde) — derleme/test:
# PATH filtresi (CI'nın kullandığı form): isim filtresi paketin adı
# değişirse sessizce hiçbir şeyle eşleşmez; yol filtresi eşleşmeyi garanti eder.
pnpm --filter ./packages/spec-core build # dist'i temizler + tsc + JSON Schema dışa aktarımı (generated/spec-schema.json)
pnpm --filter ./packages/spec-core test # vitest (2700+ test: şema, derleyici, lint, eval, CLI, check, doctor, MCP, bütçe, yayın kapısı, ölçek-tavanı, kısıt-iz, canlı-deney araçları)
pnpm --filter ./packages/spec-core lint # tsc --noEmit
pnpm --filter ./packages/spec-core smoke:packed # pack -> temiz kurulum -> lco init -> lco-mcp handshakeSıra notu (fail-closed): testler ÖNCE build gerektirir — MCP spawn entegrasyon
testi dist/mcp/server.js'i gerçek bir süreç olarak ayağa kaldırır; dist yoksa bu test
sessizce atlanmaz, run pnpm --filter ./packages/spec-core build before test mesajıyla
düşer. CI/yerel akışta sıra: lint → build → test. build dist'i önce siler
(TEST-002): silinmiş bir modülün bayat dist/ kopyası pack'lenip yayımlanamaz.
Tazelik kapısı (TEST-002): generated/spec-schema.json kaynak şemadan
bayt-bayt yeniden üretilip karşılaştırılır (test + CI fail-on-diff); bayat
artefakt testi ve CI'ı düşürür. Yeniden üretim: pnpm --filter ./packages/spec-core build
sonra git add packages/spec-core/generated && git commit.
Publishing (maintainer): paket npm'de lco-spec adıyla yayımlanır. Tercih
edilen akış CI'dır (bkz. "Yayın ve Sahiplik"):
etiketleyip publish-spec-core iş akımını çalıştır — dry_run girdisi
varsayılan olarak true'dur, yani iş akımı tek başına asla yayımlamaz.
Yerel yayın da aynı kapıya takılır: prepublishOnly =
pnpm run test && node scripts/prepublish-check.js — kirli çalışma ağacı,
etiketsiz HEAD veya etiket↔sürüm uyuşmazlığı REDDEDİLİR (pretest temizleyip
derler, tek build — PATH'te pnpm gerektirir). Yayınlama bir
kullanıcı eylemidir (U4) — bu depodan otomatik publish yapılmaz.
CLI: lco
Derleme sonrası dist/cli/index.js çalıştırılabilirdir (paket bin'i lco). On bir
komut; dokuzu bir spec dizini (<dir>/spec/*.json bölüm dosyaları) alır, generate
o dizini bir niyet metninden üretir, doctor ise isteğe bağlı bir <dir>'i (varsayılan:
geçerli dizin) yalnız OKUR — tanı yazar, hiçbir şey değiştirmez.
Yardım ve sürüm (UX-002): lco --help (veya -h) genel kullanımı, lco <komut> --help
o komutun kendi yardımını stdout'a yazdırır ve exit 0 verir — yardım, komutun kendi
bağımsız-değişken doğrulamasından ÖNCE gelir (lco init --help asla hata vermez).
lco --version paketin package.json sürümünü çalışma zamanında okur ve yazdırır
(exit 0). Bilinmeyen komut/bayrak davranışı değişmedi: exit 2 + stderr'de usage.
| komut | işlev |
| --- | --- |
| compile <dir> | spec/ ağacını derle + şemayla doğrula |
| lint <dir> | derle + 12 lint kuralı; kural/ciddiyet/yol/mesaj tablosu |
| freeze <dir> | kapı kontrollü dondurma (yalnız draft durumundan; lint temiz + sayaç sıfır); spec/manifest.json'a artifact hash yazar |
| verify <dir> | bölüm hash'lerini yeniden hesapla, manifest ile karşılaştır (drift) |
| change <dir> <changeset.json> | FROZEN spec'e changeset uygular: aday revizyonu ÖNCE tamamen doğrular (compile + lint), sonra sürüm+1, state→draft ve değişen bölümleri atomik yazar; lint-geçersiz change → exit 1 ve DİSKE HİÇBİR ŞEY YAZILMAZ |
| trace <dir> | izlenebilirlik raporu (bilgilendirici): kenar sayıları, REQ başına task bağları (✓test/✗test), yetim REQ'ler, kapsam |
| plan <dir> [--json] | topolojik yürütme planı (deterministik Kahn; aynı seviyede task_id lexicographic); döngü → hata; --json makine-okur |
| init <dir> [--profile p-mini\|p-standard] [--name <ad>] | ÇALIŞAN minimal EXAMPLE spec iskeleti yazar; <dir>/spec varsa reddeder |
| check <dir> [--task TASK-0001] [--yes] [--timeout-ms 60000] | TaskContract verification komutlarını önizler/koşar — varsayılan DRY-RUN |
| generate <dir> --intent "<metin>" \| --intent-file <yol> [--variant single\|council] [--profile p-mini\|p-standard] [--max-attempts N] [--max-tokens N] [--max-wall-ms N] | doğal-dil niyetini canlı LLM ile derlenebilir spec/ taslağına çevirir; kanıt kapısı bloklarsa HİÇBİR dosya yazmaz (ayrıntı: aşağıda) |
| doctor [dir] [--json] | çalışma-ortamı tanısı (P3-2): node sürümü, LCO_LLM_*/LCO_MCP_*/LCO_GENERATE_MAX_* env (yalnız set/unset — değer ASLA yazılmaz), <dir>'de yazma/kilit/atomik-rename probu, spec/ derleme özeti, dist bin (shebang + çalıştırma modu) ve şema artefaktı tazeliği; satır başına [ad] ok/warn/fail/skip |
Çıkış kodları (tüm CLI için tutarlı sözleşme — 0 başarı, 1 içerik/kural başarısızlığı, 2 kullanım/şema hatası):
| komut | 0 | 1 | 2 |
| --- | --- | --- | --- |
| compile | derlendi | — (kullanılmaz) | derleme/şema hatası |
| lint | temiz veya yalnız uyarı | lint hatası(lar)ı | derleme hatası |
| freeze | donduruldu | kapı başarısız | derleme hatası |
| verify | hash'ler eşleşti | drift VEYA state frozen değil | derleme hatası |
| change | uygulandı + değişiklik kapısı (lint) temiz | değişiklik kapısı (lint) hataları — HİÇBİR dosya yazılmaz, frozen spec aynen kalır, aynı changeset düzeltilip tekrar denenebilir | derleme, bozuk/bilinmeyen-anahtarlı changeset, frozen olmayan spec, yazım/kilit hatası |
| trace | rapor çıktı | — (kullanılmaz) | derleme hatası |
| plan | sıra üretildi | bağımlılık döngüsü | derleme/kullanım hatası VEYA lint reddi (BACK-006: plan lint-clean bundle ister) |
| init | iskelet yazıldı | — (kullanılmaz) | <dir>/spec zaten var (üzerine yazma reddi), IO hatası |
| check | tüm PASS veya DRY | en bir FAIL/TIMEOUT/OUTPUT-CAP/UNPARSEABLE-EXPECT | derleme VEYA lint reddi (BACK-006: check lint-clean bundle ister), bilinmeyen --task, bozuk bayrak, kanıt yazım hatası |
| generate | spec/ yazıldı (state draft) | kanıt kapısı bloğu VEYA savunma-lint reddi — HİÇBİR dosya yazılmaz | kullanım hatası (bozuk bayrak, eksik/çakışan/boş/aşırı-uzun --intent), eksik LCO_LLM_* env, <dir>/spec zaten var (üzerine yazma reddi), BUDGET_EXCEEDED (koşu bütçesi aşıldı — hiçbir şey yazılmaz) |
| doctor | hiçbir kontrol fail değil (warn/skip exit 0'da kalır) | en az bir FAIL: kırık yetenek — yazılamayan/olmayan dizin, başarısız atomik-rename probu, bozuk dist bin, derlenmeyen spec/ | kullanım hatası (bozuk bayrak, fazladan konum argümanı) |
Lint kuralları: L01–L08, L10, L12, L13, L14 (12 bağlayıcı kural; L09 ve L11 şema
katmanında zorlanır, lint değil). L01–L12'nin her birinin fixtures/bad/LXX/ altında
beklenen hatayı üreten bir yakalama vektörü vardır; semantik-kapanış kuralları L13
(kırık referans) ve L14 (yargılanabilir expect) birim testleriyle ve plan/check'in
lint-clean yükleme kapısıyla (BACK-006) sabitlenir.
doctor — saha tanı aracı (P3-2)
lco doctor [dir] [--json] çalışma ortamını denetler ve sorunları raporlar;
<dir> varsayılanı geçerli dizindir. Gizlilik sözleşmesi kesindir: doctor bir env
değişkeninin DEĞERİNİ (hatta uzunluğunu) ASLA yazmaz — yalnız set/unset ve geçerlilik.
Ciddiyet eşlemesi: FAIL = kırık yetenek (yazılamayan dizin, başarısız atomik
yazma/rename probu, bozuk dist bin, derlenmeyen spec/) → exit 1; WARN =
yapılandırılmamış opsiyonel (canlı LCO_LLM_* env'i yok, bayraklı ama tam '1'
olmayan LCO_MCP_*, çöp LCO_GENERATE_MAX_*, bayat şema artefaktı) → exit 0;
SKIP = bu bağlamda uygulanamaz (spec/ yok, dist/ yok — kaynak koşusu asla
yanlış-başarısız olmaz, paketlenmiş kurulumda şema regeneratörü yok). Probe yan
etkisi yoktur: oluşturduğu gizli probe dosyasını siler ve mevcut bir kilidi —
canlı VEYA bayat — ASLA kırmaz (süresiz staleMs ile edinir; bayat kilidi pid'i ve
yaşıyla adlandırıp uyarır, kanıtı yerinde bırakır). Node sürümü eşiği
(>=22) package.json'ın engines.node alanından ÇALIŞMA ZAMANINDA okunur
(--version'ın okuduğu aynı dosya; okunamazsa derleme-sabiti yedek — test ikisini
birbirine sabitler). --json tam olarak {"checks":[{name,status,detail,remedy?}…],
"healthy":bool} yazar (plan --json ile aynı stil). Doctor CLI-yalnızdır: MCP
sunucusuna doctor aracı eklenmez (stdout JSON-RPC saflığı korunur).
Uçtan Uca Tur — Gerçek Koşulmuş
Aşağıdaki tur gerçekten koşuldu (2026-08-25, Node v24.14.0; çıktılar kırpılmış,
çıkış kodları olduğu gibi). Repro için: cd packages/spec-core ve pnpm --filter
./packages/spec-core build yapılmış olmalı; komutlar node dist/cli/index.js … ile.
1) init — çalışan EXAMPLE iskelet (p-standard: NFR OPS-0001 + TASK-0002 + kontrat):
$ node dist/cli/index.js init /tmp/lco-tour --profile p-standard --name tour-app
initialized /tmp/lco-tour/spec (profile p-standard, tour-app) with 9 section files:
spec/manifest.json
spec/intent.json
spec/glossary.json
spec/assumptions.json
spec/evidence.json
spec/requirements.json
spec/decisions.json
spec/contracts.json
spec/tasks.json
the scaffold is a WORKING EXAMPLE spec: it compiles, lints clean, and freezes as-is — replace every EXAMPLE entry with your own content
# exit 0İskelet boş-placeholder değil: strict şemaların min(1)'leri boş iskeleti geçersiz
kıldığı için init, derlenip-lintlenip-dondurulabilen gerçek bir minimal spec yazar —
her EXAMPLE … dizgesi kendi içeriğinizle değiştirilmek içindir. Tek verification
komutu node --version (her ortamda koşar).
2) compile + lint — zincir kurulumdan temiz:
$ node dist/cli/index.js compile /tmp/lco-tour
compiled /tmp/lco-tour/spec (lco-spec/1.0 v1, state: draft, project: tour-app)
intent 1
glossary 1
assumptions 0
evidence 1
requirements 2
decisions 1
contracts 1
tasks 2
test_files 2
# exit 0
$ node dist/cli/index.js lint /tmp/lco-tour
lint OK: 0 errors, 0 warnings (12 rules)
# exit 03) freeze + kasıtlı tamper → verify. Önce yedek alıp donduralım:
$ cp /tmp/lco-tour/spec/tasks.json /tmp/lco-tour/tasks.json.bak
$ node dist/cli/index.js freeze /tmp/lco-tour
frozen at 2026-08-25T17:04:56.209Z: 8 artifact hashes written to spec/manifest.json
# exit 0Tamper denemesi #1 — bir dizgenin İÇİNE sona boşluk ("purpose": "Scaffold example" →
"Scaffold example "):
$ node -e "const fs=require('fs');const p='/tmp/lco-tour/spec/tasks.json';fs.writeFileSync(p,fs.readFileSync(p,'utf8').replace('\"purpose\": \"Scaffold example\"','\"purpose\": \"Scaffold example \"'))"
$ node dist/cli/index.js verify /tmp/lco-tour
verify OK: sections match manifest.artifact_hashes
# exit 0 ← yakalanMADI (bkz. not)Bu dürüst bir sonuçtur: verify ham baytları değil, şema-normalize edilmiş bölüm içeriğini hash'ler ve trim-refine'lı metin alanlarındaki baş/son boşluklar ayrıştırma sırasında normalize edilir (Bilinen Sınırlar). Aynı boşluk dizgenin ORTASINA girerse içerik gerçekten değişir:
$ command cp -f /tmp/lco-tour/tasks.json.bak /tmp/lco-tour/spec/tasks.json # restore
$ node -e "const fs=require('fs');const p='/tmp/lco-tour/spec/tasks.json';fs.writeFileSync(p,fs.readFileSync(p,'utf8').replace('\"title\": \"EXAMPLE task — replace with your own\"','\"title\": \"EXAMPLE task — replace with your own\"'))"
$ node dist/cli/index.js verify /tmp/lco-tour
verify FAILED: drifted sections: tasks
# exit 1 ← drift yakalandıDrift'li frozen spec'i olduğu gibi yeniden freeze etmeye çalışmak reddedilir
(tek lifecycle doğrulayıcı, BACK-002): freeze yalnız draft → frozen geçişine
izin verir; sürüm yalnızca bir changeset ile ilerler. Böylece elle düzenlenmiş
frozen içerik aynı sürüm altında yeniden sabitleyerek aklanamaz:
$ node dist/cli/index.js freeze /tmp/lco-tour
freeze FAILED with 1 reason(s):
lifecycle gate failed: freeze is legal only from 'draft' (transition: freeze — draft -> frozen); current state is 'frozen' — a frozen spec cannot be re-frozen: either restore the drifted sections … or record the edit as a changeset (lco change) …
# exit 1 ← içerik aklanamaz; önce restore ya da lco change
$ command cp -f /tmp/lco-tour/tasks.json.bak /tmp/lco-tour/spec/tasks.json # restore
$ node dist/cli/index.js verify /tmp/lco-tour
verify OK: sections match manifest.artifact_hashes
# exit 04) trace — izlenebilirlik (bilgilendirici, her state'te):
$ node dist/cli/index.js trace /tmp/lco-tour
traceability: tour-app — 2 requirement(s), 2 task(s)
edges: req-task 3, task-test 3, dec-task 2, evidence-req 2
REQ-0001: 2 task(s) [TASK-0001 ✓test, TASK-0002 ✓test]
OPS-0001: 1 task(s) [TASK-0002 ✓test]
coverage: 2/2 requirements task-linked; 2/2 test-linked
# exit 05) change — changeset ile revizyon (yalnız FROZEN spec'e; örnek dosya:
examples/changeset.example.json):
{
"id": "CP-0001",
"rationale": "Scaffold EXAMPLE başlığı gerçek görev tanımıyla değiştiriliyor: …",
"modified_tasks": [
{ "task_id": "TASK-0001", "patch": { "title": "Kimlik doğrulama katmanı — revize başlık" } }
]
}$ node dist/cli/index.js change /tmp/lco-tour examples/changeset.example.json
changeset CP-0001 applied: spec_version 2 (state draft), 2 task(s), 2 requirement(s); lint OK: 0 errors, 0 warnings
# exit 0
$ node -e "const m=require('/tmp/lco-tour/spec/manifest.json');console.log(JSON.stringify({spec_version:m.spec_version,state:m.state,project:m.project.name},null,2))"
{
"spec_version": 2,
"state": "draft",
"project": "tour-app"
}Manifest artık spec_version 2, state: draft — yeni sürüm ancak bir sonraki
freeze ile yeniden dondurulur. Bu arada verify fail-closed'dur: cmdVerify
hash karşılaştırmasına gelmeden notFrozen üzerinde kısa-devre yapar; taslak hiçbir
durumda verify'den geçemez ve artifact_hashes, bir sonraki freeze yeniden
sabitleyene dek herhangi bir drift iddiası taşımaz.
change sözleşmesi (DATA-001 / BACK-005): change aday revizyonu TÜMÜYLE
hafızada doğrular (compile + lint) ve YALNIZCA temizse diske yazar. Lint-geçersiz
bir changeset exit 1 verir ve hiçbir dosya yazılmaz — "kapı başarısız"
her zaman "işlenmedi" anlamına gelir, eski davranış (önce yaz, sonra bildir)
artık yok. Yazım aşaması da atomiktir: her revizyon kök-başına kilit
(<dir>/.lco-revision.lock, exclusive-create; 10 sn'den eski kilitler ölü
sayılıp kırılır) altında, geçici dosyalar + rename ile işlenir — manifest.json
en son takas edilir (commit noktası) ve herhangi bir yazım hatası tüm süreci
bayt-bayt geri alır. Aynı atomiklik init, generate, freeze ve check
kanıt yazımları için de geçerlidir.
6) plan — topolojik sıra:
$ node dist/cli/index.js plan /tmp/lco-tour
plan: tour-app — 2 task(s) in dependency order
1. TASK-0001 [xs] deps: none | verify: node --version (exit 0) | scope: src/**
2. TASK-0002 [xs] deps: TASK-0001 | verify: node --version (exit 0) | scope: src/**
ready-now: TASK-0001
# exit 07) check — önce DRY (varsayılan), sonra --yes:
$ node dist/cli/index.js check /tmp/lco-tour
DRY RUN — no commands executed; pass --yes to execute
check: tour-app — 2 verification command(s)
TASK COMMAND EXPECT EXPECTED→ACTUAL STATUS
TASK-0001 node --version exit 0 0 → - DRY
TASK-0002 node --version exit 0 0 → - DRY
summary: 0 pass, 0 fail, 2 dry
(0 timeout, 0 output-cap, 0 unparseable-expect)
# exit 0
$ node dist/cli/index.js check /tmp/lco-tour --yes
check: tour-app — 2 verification command(s)
TASK COMMAND EXPECT EXPECTED→ACTUAL STATUS
TASK-0001 node --version exit 0 0 → 0 PASS
TASK-0002 node --version exit 0 0 → 0 PASS
summary: 2 pass, 0 fail, 0 dry
(0 timeout, 0 output-cap, 0 unparseable-expect)
evidence: /tmp/lco-tour/spec/evidence/TASK-0001-check-20260825T170456Z-001.json, /tmp/lco-tour/spec/evidence/TASK-0002-check-20260825T170456Z-001.json
# exit 0Kanıt dosyası (koşum başına YENİ bir dosya; --yes altında yazılır — SEC-004):
$ cat /tmp/lco-tour/spec/evidence/TASK-0001-check-20260825T170456Z-001.json
{
"task_id": "TASK-0001",
"checkedAt": "2026-08-25T17:04:56.607Z",
"checks": [
{
"command": "node --version",
"expect": "exit 0",
"expectedExit": 0,
"actualExit": 0,
"status": "PASS",
"durationMs": 7,
"outputTail": "v24.14.0\n"
}
]
}lco check Güvenlik Modeli
check, spec'in KENDİ komutlarını (TaskContract verification) yürüten tek komuttur;
modeli bağlayıcıdır:
- Varsayılan DRY-RUN.
--yesyoksa HİÇBİR komut koşulmaz: her satırın durumuDRY, çıkış 0, diske hiçbir şey yazılmaz (spec/evidence/dizini bile oluşmaz). Tablo,--yesaltında neyin koşacağının önizlemesidir. --yesaçık onaydır. Komutlar yalnız operatörün açık bayrağıyla yürütülür: cwd spec köküdür, komut başına--timeout-ms(varsayılan 60000 ms) sonunda komutun İZOLE process group'unun TAMAMI öldürülür (SEC-005; aşağıdaki "Yürütme izolasyonu" notuna bakın).- Fail-closed yargı. Beklenen çıkış kodu,
expectaçıklamasındaki İLKexit Neşleşmesidir.exit Nbulunamayan expect →UNPARSEABLE-EXPECT: komut hiç koşulmaz ve başarısız sayılır (çıkış 1). Yargılanamayan bir şeyi koşmak başarı tiyatrosu olurdu. - Kanıt dosyaları (SEC-004 ile sertleştirildi).
--yesaltında görev başına, KOŞUM başına YENİ bir dosya yazılır:spec/evidence/<TASK-ID>-check-<RUN>.json({task_id, checkedAt, checks:[…]}— her komut için command/expect/expectedExit/actualExit/status/durationMs/outputTail; birleşik stdout+stderr'nin son 500 karakteri). Atlanan (UNPARSEABLE-EXPECT) girdiler de kayda girer: dosya--yes'in ne yaptığının ve neyi atladığının denetim izidir. Sertleştirme:- Run-addressed + immutable:
<RUN>, enjekte edilennowIso+ task id + çarpışma sayacından üretilen deterministik bir koşum kimliğidir. Her koşum YENİ bir dosya yazar — sonraki bir koşum öncekinin izini ASLA ezmez (geç bir PASS, erken bir FAIL'in kaydını silemez). - Mod 0600: kanıt dosyaları yalnız sahibince okunur (çıktı kuyrukları sır taşıyabilir — aşağıdaki redaksiyon notuna bakın).
- Redaksiyon (en iyi çaba, garanti DEĞİL): yakalanan çıktı kalıcı hale
gelmeden ÖNCE bilinen gizli desenlerden geçer — bearer token'lar,
sk-/zai-önekli API anahtarları,PASSWORD=/TOKEN=tarzı atamalar ve JWT şekilleri[REDACTED:<tür>]ile değiştirilir. Eşleştirme kasıtlı olarak muhafazakârdır (olağan test çıktısını bozmaz); bu EN İYİ ÇABA'dır, garanti değildir — başka şekilde yazılmış bir sır değiştirilmeden kalır. - Retention/commit önerisi (dürüst): kanıt dosyaları redaksiyondan sonra
bile hassas kuyruklar taşıyabilir. Repoya commitlemeden önce gözden geçirin
ya da
spec/evidence/için gitignore deseni kullanın:printf 'spec/evidence/\n' >> .gitignore. Denetim izini repoda tutmak istiyorsanız commit öncesi insan incelemesini süreçlerinize ekleyin.
- Run-addressed + immutable:
- Yol güvenliği (SEC-003). Spec kökü realpath ile çözülür; sabit bölüm
yolları (
spec/<bölüm>.json) vespec/evidencedizini çözülen kökün İÇİNDE kalmak zorundadır (realpath karşılaştırması; önek-dizgesi karşılaştırması değil). Okuma kapısı derleme (compile) sınırındadır: kökün dışına çözülen sembolik bağlantılı bir bölüm veyaspec/dizini derleme hatası olarak reddedilir; kökün içinde kalan bağlantılar yasal kalır (meşru reorganizasyon). Yazma tarafı daha katıdır:spec/,spec/evidenceveya bir bölüm dosyası sembolik bağlantıysa yazma, bağlantıyı ADLANDIRAN yapılandırılmış bir hatayla reddedilir — yazılar asla bağlantıyı takip etmez. (POSIX hedeflenir; Windows junction davranışı kapsam dışıdır.) Bilinen kalıntı (TOCTOU): takip-etmeyen yazma kapısı denetle-sonra-yaz biçiminde çalışır — kapı ile staging/rename arasında bir ara dizin bileşenini (ör.spec/evidence) sembolik bağla değiştiren YARIŞAN bir yerel yazıcı yazmayı başka bir yere yönlendirebilir; bu tehdit modelinin dışındadır (statik ağaçlar ve önceden yerleştirilmiş bağlar kapsanır; ağaca eşzamanlı yazma erişimi olan bir saldırgan kapsanmaz) ve Node'da dirfd/O_NOFOLLOW API'leri olmadan taşınabilir biçimde kapatılamaz.
Operasyonel notlar:
- Yürütme izolasyonu (SEC-005, POSIX). Her komut kendi process group'unda
yürütülür ve süreç ağacının TAMAMI kapsanır: zaman aşımında, çıktı sınırı
aşımında VE normal bitişte grup önce
SIGTERMalır; grace penceresi (400 ms) sonunda hâlâ yaşayan üye varsa grupSIGKILLile öldürülür. Executor'ın kararı döndürmesi, grubun ölmüş (veya SIGKILL edilmiş) olması anlamına gelir — birTIMEOUTsonucu artık "işlem durdu"nun kanıtıdır ve normal bitişte bile komutun sahneye koyduğu arka süreçler karardan sonra çalışamaz. stdin/dev/null'dur: etkileşimli komutlar anında EOF görür, zaman aşımını bekleyerek asla bekletmez. Kapsam dürüstçe: process group'lar POSIX mekanizmasıdır — Windows (job object'ler gerektirir) kapsam dışıdır; kendini yeni oturuma taşıyan (setsid) bir torun gruptan kaçar ve yalnızca çekirdek düzeyinde izolasyon (cgroup/sandbox) kapsanabilir. - 1 MiB çıktı sınırı taşması
OUTPUT-CAPsayılır (OPS-003). Üretim yürütücüsü akış başına 2^20 kod-birimi (1 MiB değerinde; exec'in maxBuffer'ı utf8 altında KARAKTER sayar — bayt değil, burada da öyle; ayrıntı:runner.tsMAX_BUFFER_BYTESbaşlığı) sınırıyla çalışır; sınırı aşan çıktı GRUBU öldürür ve sonuç ayrı etiketle yargılanır:OUTPUT-CAP(exit null, kanıta yazılır, çıkış 1'e katkı) — fail-closed: geveze bir komut asla PASS olamaz.TIMEOUTyalnızca gerçek zaman aşımı kill'i ve sinyalle ölüm içindir; operatörTIMEOUTgörüyorsa teşhisi asılı komuttur, geveze komut değil — ikisi hiçbir yüzeyde (tablo, özet satırı, kanıt) aynı etiketi paylaşmaz. Çare: komutu sessizleştirin ya da çıktıyı dosyaya yönlendirin (ör.> out.log 2>&1) ve günlüğü dosyadan okuyun; özet satırı taşmayı ayrıca sayar (N output-cap). - Sinyalle öldürme → TIMEOUT. Sinyalle biten bir sürece çıkış kodu atanmaz
(
exit: null,TIMEOUT): öldürülmüş süreç, yargılanmış bir çıkış koduyla karıştırılamaz. Zombi bırakılmaz: kabuk, karara varılmadan önce reaped edilir. - Komutlar kasıtlı olarak kabukta koşar (TaskContract verification komutları kabuk
dizgeleridir:
pnpm vitest run tests/x.test.ts). Enjeksiyon yüzeyi tam olarak bu güvenlik modelinin yönettiği yüzeydir: varsayılan hiç-koşma + açık--yesonayı.
generate — Niyetten Spec'e
generate, eval boru hattını ürünleştirir: tek bir doğal-dil niyet metnini canlı bir
LLM ile derlenip-lintlenebilir bir spec/ taslağına çevirir. İçerik kapısı baskısı
yoktur — kanıt kapısı (evidence gate) spec üretir ya da gerekçeleriyle reddeder;
komut bir reddin etrafına asla içerik uydurmaz.
lco generate <dir> --intent "<metin>" | --intent-file <path> \
[--variant single|council] [--profile p-mini|p-standard] \
[--max-attempts N] [--max-tokens N] [--max-wall-ms N]Varsayılanlar:
--variant single,--profile p-standard(varsayılan TEK bir yerde seçilir:commands/generate.tsiçindekiDEFAULT_GENERATE_VARIANT; CLI, MCP sunucusu ve bu doküman aynı kaynaktan beslenir). Council pahalı yoldur ve faydası henüz kanıtlanmadığı için açık tercihle çalışır:--variant council.Dürüst maliyet zarfı (UX-001): "3 çağrı" değil, gerçek en-kötü-case zarf — HTTP denemesi (attempt) ≠ tamamlama (completion). Doğrulama-informed retry'ler tamamlama sayısını, transport retry'ler deneme sayısını büyütür. Her tamamlama en fazla 8 HTTP denemesi yapabilir (her deneme 600 s zaman aşımı, tükenen denemeler arası toplam 472 s backoff: 2+5+15+30+60+120+240 — 2026-08-28 transport sıkılaştırması: kenar-IP karartmalarına karşı 8 deneme
- uzun bekleme; istek tavanı 180→600 s, çünkü akıl yürüten model ~30KB prompt'ta tamamlamayı dakikalarca açık tutar ve bağlantı sağlıklıyken 180 s iptali sağlıklı üretimi öldürüyordu; başarılı isteklerde ek maliyet sıfır):
| variant | tamamlama (iyi → en kötü) | HTTP denemesi (en kötü) | en-kötü duvar süresi | | --- | --- | --- | --- | | single | 1 → 3 | 3 × 8 = 24 istek | 3 × (8×600 s + 472 s) = 15816 saniye (~263,6 dk) | | council | 3 → 6 | 6 × 8 = 48 istek | 6 × (8×600 s + 472 s) = 31632 saniye (~527,2 dk) |
(Sayılar kod sabitlerinden türetilir —
eval/budget.ts;budget.test.tsREADME'yi bu sabitlere sabitler, doküman kayarsa test düşer.)Koşu bütçesi (UX-001): toplam HTTP denemesi, toplam token (in+out; sağlayıcı usage bildirdiğinde) ve duvar süresi bütçeleri aşılırsa koşu yapılandırılmış
BUDGET_EXCEEDEDhatasıyla İPTAL olur (exit 2, HİÇBİR şey yazılmaz — asla sessiz kısmi başarı). Varsayılanlar zarftan türetilir: deneme limiti = belgelenmiş en kötü case (+0), duvar limiti = en kötü case + 60 s pay; token limiti varsayılan yoktur (model/sağlayıcıya göre büyüklük değişir — varsayılan sayı tahmin olur). Geçersiz kılma:--max-attempts/--max-tokens/--max-wall-msbayrakları veyaLCO_GENERATE_MAX_ATTEMPTS/LCO_GENERATE_MAX_TOKENS/LCO_GENERATE_MAX_WALL_MSenv değişkenleri (bayrak > env > varsayılan; bozuk değer exit 2). İptal temizdir: boru hattı sıkı sıkıya sıralıdır, ödenememiş promise bırakmaz; HTTP adaptörü bütçe defterini deneme BAŞINA şarj eder, cap dolunca bir sonraki istek hiç gönderilmez.Kullanım muhasebesi (UX-001 + UX-003 + PERF-001): özetler tamamlama ve HTTP denemesini ayrı ayrı gösterir (
N LLM call(s) / M HTTP attempt(s)— zaman aşımına uğrayan denemeler dahil). Sağlayıcı usage BİLDİRMEDİĞİNDE token sayılarıunknowngörünür — asla0 in / 0 outdeğil; G4 maliyet koşulu da unknown'ı geçmez (0 <= 3×0kanıt değildir). Ayrıca koşunun gönderdiği prompt baytları (K prompt bytes) koşucu tarafından YEREL olarak ölçülür ve her durumda raporlanır — gömülü şema (~23 KiB) bundle üreten her çağrıda ve her doğrulama-retry'inde tekrarlanır; bu maliyet tahmin edilmez, sayılır. Prompt önbellekleme/BJM referanslama BİLİNÇLİ olarak ertelenmiştir: sağlayıcılar önbellek anahtarı ve isabet raporlaması açısından farklıdır, koşucu sağlayıcı-agnostiktir; ölçüm önce gelir, önbellekleme ölçülen bir maliyeti gerekçelendirdiğinde eklenir.Env sözleşmesi (fail-closed):
LCO_LLM_BASE_URL,LCO_LLM_API_KEYveLCO_LLM_MODELkullanıcı tarafından açıkça sağlanmalıdır; biri eksikse komut yarım yapılandırmayla devam etmez, exit 2 verir. İsteğe bağlı:LCO_LLM_MAX_TOKENS(pozitif tamsayı; üretimi sınırlar) veLCO_LLM_EXTRA_BODY(JSON nesnesi; istek gövdesine en son birleştirilir — ör.'{"thinking":{"type":"disabled"}}'gizli reasoning'i atlar).Para-yakma sırası: tüm kullanım/çevre/doğrulama kontrolleri ilk LLM çağrısından ÖNCE koşar — bayrak çözümlemesi (
--intent/--intent-filekarşılıklı dışlar, bozuk bayrak), intent preflight (UX-004),--intent-fileokuma/boş-dosya denetimi, no-clobber (<dir>/specvarsa exit 2) ve env denetimi sırasıyla. Yanlış çağrı hiç ücret ödemez.Intent preflight (UX-004):
--intentmetni normalize edilir (trim) ve boşluk-yalnızca olduğunda reddedilir; ayrıca satır-içi kanal olarak 10.000 karakterle sınırlıdır (üstünde: exit 2, mesaj--intent-file'a yönlendirir). Uzunluk tasarımı kanala göredir:--intent-fileuzun metin için tasarlanmış kaçış yoludur — inline 10k sınırı YOK; yalnızca trim + boş-red + 1.000.000 karakterlik bir akıl tavanı (o kadar büyük bir dosya neredeyse kesin yanlış-dosya hatasıdır; mesaj tavayı adlandırır). MCPintentargümanı da satır-içi kanaldır: aynı 10k sınırı argüman katmanında uygulanır (-32602). Her red adaptör KURULUMUNDAN önce koşar: boş/aşırı-uzun intent sıfır adaptör çağrısı anlamına gelir (testlerle sabitlenmiştir).Fail-closed yargı: kanıt kapısı niyeti bloklarsa (belirsiz/çelişkili) exit 1 + gerekçe listesi, HİÇBİR dosya yazılmaz. Üretilen bundle ayrıca savunma-lint yeniden denetiminden geçer; kirli bundle da yazılmaz (yine exit 1, hiçbir şey yazılmaz).
Monotonik blok kanıtı (BACK-001): council sınıflandırıcısı
must_be_blocked=truedöndürürse sonuç KESİN olarak blocked'dır — sonraki (temiz) bir bundle bu kanıtı iptal edemez; kanıt kapıdaki kodda taşınır, prompt tavsiyesi değil. Doğrulama-informed retry'ler de UNRESOLVED madde düşüremez: retry çıktısı önceki unresolved kimliklerden (claim_id) veya sayaçlarından herhangi birini sessizce bırakırsa sonuçRESOLUTION_MISSINGile reddedilir (kimlikler gerekçede isimlendirilir); madde eklemek veya korumak serbesttir.Council bacağının degradasyonu (BACK-008): bağımsız öneri A iki denemede de şema doğrulamasını geçemezse bacak DEGRADED işaretlenir, doğrulanmamış metin yargıca verilmez (yargıç kendi önerisiyle tek başına üretir) ve özet satırı bunu açıkça yazar — nihai bundle yine tam kapıdan geçer, yazılır.
Başarı:
spec/bölüm dosyaları yazılır (state: draft) ve çıktı sıradaki adımı önerir:run lco lint/lco freeze next.
Multi-Provider LLM Architecture (gateways, profiles, heterogeneous councils)
Status framing (binding): single remains the DEFAULT generation variant.
council remains EXPERIMENTAL, in every topology. The closed PROD-003
live experiment did NOT substantiate a council advantage (pre-registered
criterion NOT MET — see audit-output/eval/LIVE-EVAL-RESULT-2026-08-30.md);
nothing here changes that. Heterogeneous councils are equally EXPERIMENTAL
and carry no accuracy claim — any future claim requires a NEW
pre-registered experiment.
Gateways
One reusable OpenAI-compatible transport (src/llm/openai-compatible.ts)
serves every gateway. No vendor SDKs. Three provider kinds:
| kind | base URL | key env (name in config) | notes |
|---|---|---|---|
| openai-compatible | your baseUrl (required — LCO never defaults an endpoint) | any env name | the legacy LCO_LLM_* path's kin; extraBody escape hatch |
| openrouter | https://openrouter.ai/api/v1 (overridable) | e.g. OPENROUTER_API_KEY | routing modes below; provider-reported cost (credits) recorded |
| routellm | https://routellm.abacus.ai/v1 (overridable) | e.g. ABACUS_ROUTELLM_API_KEY | explicit model ids; upstream provider NOT reported → recorded unknown |
Secrets never live in lco.config.json — providers carry apiKeyEnv, the
NAME of an environment variable. A raw key pasted where the name belongs fails
validation (and doctor fails the config). API-key values stay in the process
environment, exactly like LCO_LLM_API_KEY today.
lco.config.json and named profiles
Put lco.config.json in the project directory (the <dir> you pass to
generate). A complete, commented example — including a same-model
decomposed council and the frontier heterogeneous EXAMPLE profile — ships at
examples/lco.config.example.json. Shape:
{
"llm": {
"providers": {
"openrouter": { "type": "openrouter", "apiKeyEnv": "OPENROUTER_API_KEY" }
},
"profiles": {
"frontier-heterogeneous-openrouter": {
"variant": "council",
"topology": "decomposed",
"routingMode": "evaluation",
"roles": {
"classifier": { "provider": "openrouter", "model": "google/gemini-3.7-flash" },
"proposal_a": { "provider": "openrouter", "model": "anthropic/claude-opus-5" },
"proposal_b": { "provider": "openrouter", "model": "x-ai/grok-4.6" },
"judge": { "provider": "openrouter", "model": "openai/gpt-5.6-sol" }
}
}
}
}
}That frontier profile is an EXAMPLE composition, not a proven optimum —
role behavior matters more than vendor branding. The four slugs were verified
against the live OpenRouter catalogue on 2026-08-30; catalogues change, so
check current ids with lco models (below) rather than trusting any doc.
Usage — one named choice, not fifteen model flags:
export OPENROUTER_API_KEY=sk-or-... # the value lives in the ENV, never in config
lco generate app --intent "..." --variant council --llm-profile frontier-heterogeneous-openrouterWithout --llm-profile the legacy LCO_LLM_* path runs unchanged
(zero-config, single model, fail-closed). A profile and --variant must
agree. Validation is strict and fail-closed: unknown keys, secret-shaped
apiKeyEnv values, dangling references, and wrong role sets are all refused
before any paid call.
Council topologies (under --variant council)
single one configured model (default; unchanged)
council
fused HISTORICAL topology (PROD-003 ran under it):
classifier -> proposal A -> fused proposal-B + judge (3 stages)
decomposed NEW (v4 protocol): classifier -> proposal A ∥ proposal B -> judge
over BOTH validated proposals (4 stages)--variant council alone still means the fused topology — existing
invocations keep their exact meaning. The decomposed topology is selected by
a profile ("topology": "decomposed"); proposal B is generated without ever
seeing proposal A (independence by construction), and the judge receives
only schema-validated proposal content — a leg that fails validation
twice is DEGRADED, its unvalidated text is withheld, the outcome carries
degraded: [roles], and a degraded run is never presented as a full council.
Blocking evidence stays monotonic and validation retries still cannot erase
unresolved material.
A decomposed council may use the same model in all four roles (see
glm-council-decomposed in the example config) — that is deliberate: a future
same-topology comparison of same-model vs heterogeneous deliberation requires
equivalent prompt structure (infrastructure only; no such experiment is
running).
Routing modes (product vs evaluation)
product(default / reliability): gateway defaults — OpenRouter may fall back across upstream providers serving the SAME model for reliability; resolved identity (model, upstream provider, fallback-observed) is recorded from the response's router metadata.evaluation(reproducible):provider.allow_fallbacks: false(no silent upstream substitution), optional upstream restriction via the officialprovider.only(restrictive in both modes) /provider.order(exhaustive here because fallbacks are off),require_parameterswhen structured output is requested, and gateway auto-routers are prohibited (RouteLLM'sroute-llmis rejected in evaluation profiles — an auto router must never be part of a scientific comparison). Requested model, resolved model, gateway, routing settings, and the prompt protocol are recorded with the run output.
If a gateway cannot report enough information to pin an upstream (RouteLLM does not report the serving provider), that is recorded as unknown — never fabricated.
Model discovery (lco models)
lco models --provider openrouter # built-in; OPENROUTER_API_KEY
lco models --provider routellm # built-in; ABACUS_ROUTELLM_API_KEY
lco models --provider glm --config lco.config.json --limit 20
lco models --provider openrouter --json # machine-readableOne GET to the provider's free models endpoint — no completion, no
retries, 10s timeout. Prints exact API ids (display names are never API ids),
per-token pricing and context as reported; Unknown means not reported
(never 0). The catalogue changes over time — especially RouteLLM's, whose
doc-page list lags; use the live listing, not screenshots.
Usage, cost, and provenance accounting
Every council run reports per-role accounting (gateway, requested model,
resolved model when the provider reports it, calls vs transport attempts,
tokens or unknown, prompt bytes, provider-reported cost when available) plus
the run total. Honesty rules: unknown is never zero — a provider that
reports no usage renders tokens unknown, a gateway that reports no cost has
no cost line. LCO ships no price catalogue and never estimates; the only
monetary figures shown are provider-reported (OpenRouter credits).
Budget caps (attempts/tokens/wall) are LCO-enforced per run and unchanged; the decomposed envelope is 8 completions / 32 attempts worst case. Monetary caps are not simulated: LCO learns a request's exact cost only after the provider completes it, so a genuine hard spend ceiling belongs at the provider's API-key settings; LCO's role is honest observed-cost accounting.
Clarification UX (no silent high-impact gaps)
Generation may block on UNRESOLVED product decisions — by design (models may
reason about missing information; they may not silently invent the answer).
When a blocked run carries a schema-valid candidate whose UNRESOLVED decisions
caused the block, the CLI surfaces them as plain-language, domain/behavior
questions (the v4 prompt protocol instructs models to phrase decisions for
a non-engineer product owner; engineering mechanics stay in rationale or as
recorded assumptions):
GENERATION BLOCKED — USER DECISIONS REQUIRED
Questions to resolve:
DEC-0004 [impact: high]
If two customers try to complete the remaining quantity for the same fabric at the same time, what should the system do — accept both orders, or give priority to the first confirmed one?
options:
- first confirmed order gets priority (the other customer sees an out-of-stock message)
Answer with an answers file — {"DEC-0004": "your answer", …} — and re-run with --answers <file>.Nothing is written on block (unchanged). The answers loop is one deterministic round per invocation (no hidden LLM loop):
lco generate app --intent "..." --answers answers.jsonEach answer becomes verbatim user_input evidence (hash-verified) for the
next run, resolves only the decision it names, and unanswered UNRESOLVED
decisions must remain unresolved — a run that succeeds carries exactly the
decisions the evidence supports, surfaced or resolved, never silently
dropped. (Limitation, stated honestly: question identity across runs is
prompt-bound claim_ids — LCO keeps clarifications in-memory by design, so
cross-run persistence of unanswered questions is not mechanically enforced;
the new run re-surfaces whatever remains unresolved.)
Future metric extension point: SILENT CRITICAL GAP RATE
The target product behavior is "no important uncertainty disappears silently". The architecture now supports scoring a future pre-registered metric for it: SILENT CRITICAL GAP RATE — the share of production-relevant HIGH/CRITICAL issues found by an independent reviewer that a generated spec neither resolved, nor declared as an assumption, nor represented as an UNRESOLVED decision (i.e., never surfaced at all). A longer spec is NOT a better spec; this metric counts disappearances, not requirements. The ingredients exist: UNRESOLVED decisions + assumptions are first-class bundle sections, the clarification channel records what was asked, and run metadata carries the prompt protocol. No such experiment is running and no result is claimed — any use requires a NEW pre-registration (the closed PROD-003 criterion and its NOT-MET conclusion stand untouched).
MCP surface
lco_generate accepts an optional llmProfile — a NAME from the
operator-configured lco.config.json (server start: LCO_LLM_CONFIG path or
lco.config.json at the server root). The consent digest binds
{intent, profile, variant, llmProfile?}. Requests can NEVER carry raw API
keys, base URLs, or headers — those argument shapes are refused by name
(SSRF/credential/spend control); gateway selection is operator configuration.
Interactive Clarification Workspace (browser, --interactive)
lco generate <dir> --intent "…" --interactive açtığınız yerel tarayıcıda
işletilen İş Netleştirme Çalışma Alanı'dır: kanıt kapısı bir iş kararının
eksik olduğunu tespit ettiğinde uydurmak yerine sorar — önerilen seçenekler,
anlık sonuç önizlemeleri, kendi kuralınız (Other), her turdan sonra yeniden
doğrulama; sonra iş dilinde bir Davranış Değerlendirmesi, üzerinde
değişiklik isteyebileceğiniz karar blokları ve yalnızca açık onayınızda yazılan
spec/ + onaysız approvals/APPR-NNNN.json revizyon kayıtları. Sunucu
yalnızca 127.0.0.1'e bağlanır; oturum anahtarı URL fragment'ında taşınır
(sunucuya asla gönderilmez); iptal/terk durumunda hiçbir şey yazılmaz.
--answers akışı değişmeden durur (iki ayrı cevap kanalı — bayraklar birlikte
verilemez). Tam kılavuz: docs/clarification-workspace.md.
Legacy Renewal V1 (lco renew) — evidence-backed analysis & planning
Legacy Renewal V1 turns a legacy repository into a validated, evidence-anchored modernization spec — analysis + planning only, no execution (no source modification, no patches, no shell against the target, no deployment; those are separate future programs). It is best described as evidence-backed legacy application analysis and modernization planning — not automated modernization.
Prerequisite (renewal only)
Graphify — an external, pinned,
unmodified tool (supported range >=0.9.50 <0.10.0, audited against 0.9.50).
Install it separately; lco renew probes it and fails closed with install
instructions when absent or unsupported. Every other lco command works
without Graphify (lco doctor reports it as an informational warn).
Commands (lco renew <sub> <lco-project-dir>)
| command | cost | what it does |
| --- | --- | --- |
| init <dir> --target <repo> [--name n] | offline | snapshot + guarded workspace copy + graph build; the analyzed repo is NEVER written |
| refresh <dir> | offline | re-snapshot + graph rebuild (the staleness remedy) |
| status <dir> [--json] | offline | snapshot freshness w/ reasons, graph, analyses, open questions, overlay/parity, strategy, plan |
| analyze <dir> [--llm-profile n] | PAID | recovery pipeline: schema-gated LLM hypotheses anchored to verified file hashes; hallucinated paths/stale hashes are rejected, never promoted |
| review <dir> --answers f \| --interactive | free | human decisions in the browser clarification workspace (reused as-is) or headless; approvals are immutable APPR-NNNN records |
| plan <dir> [--strategy s --strategy-rationale t] [--freeze] | offline | deterministic migration plan on TaskContract semantics; refuses stale state, missing human strategy, or unresolved parity; --freeze → frozen spec revision |
| export <dir> [--out f] | offline | markdown report of validated state only |
Strategy selection and every preserve/change/drop parity ruling are human acts (workspace or explicit flags — modeled as data, never autonomous).
Artifacts (LCO project dir; target repo untouched)
<lco-project>/spec/ modernization spec (existing artifact spine)
approvals/APPR-NNNN.json immutable renewal approvals (0600)
.lco/renewal/{project,snapshot,overlay,parity,strategy}.json
.lco/renewal/analyses/AN-NNNN.json immutable LLM analysis records
.lco/renewal/graph-workspace/ guarded copy + Graphify graphTrust model (short version)
Deterministic structural facts come from the pinned Graphify graph; the target
repo is untrusted input (default-deny ingest: .env*/keys never read, archives
never expanded, size caps, symlink refusal, secret redaction before any prompt,
all reads realpath-contained). Evidence hashes are RECOMPUTED — an evidence kind
code_anchor verifies against the live tree or the claim is rejected. Analyze
and plan refuse on stale snapshots with the refresh remedy. Paid MCP analysis
requires the consent digest and makes zero LLM calls without it.
lco-mcp: MCP Sunucusu
lco-mcp (bin: dist/mcp/server.js), motoru Model Context Protocol istemcilerine
açan minimal bir stdio sunucusudur: satır-ayrılmış JSON-RPC 2.0. CLI komutlarının saf
çekirdeklerini (yazdırma yapmayan, yapılandırılmış sonuç döndüren) yeniden kullanır —
davranış CLI ile birebir aynıdır. 13 araç (10 motor + 3 yenileme):
| araç | girdi | işlev |
| --- | --- | --- |
| lco_compile | {dir} | derle + şema doğrula |
| lco_lint | {dir} | derle + lint tablosu (hata varsa isError) |
| lco_freeze | {dir} | kapı kontrollü dondurma |
| lco_verify | {dir} | drift doğrulaması |
| lco_trace | {dir} | izlenebilirlik raporu |
| lco_plan | {dir, json?} | topolojik plan (--json eşleniği) |
| lco_check | {dir, task?, consent?} | verification önizleme (DRY) / rızaya bağlı koşma — bkz. Yürütme Rızası |
| lco_init | {dir, profile?, name?} | WORKING EXAMPLE spec/ iskeleti (draft/v1) — NO-CLOBBER: dir/spec varsa reddeder, diske dokunmaz; lco init çekirdeği |
| lco_generate | {dir, intent, variant?, profile?, consent?} | intent → spec/ taslağı (ÜCRETLİ LLM çağrısı) — bkz. Ücretli Çağrı Rızası; lco generate çekirdeği + T4 kapıları |
| lco_change | {dir, changeset} | changeset (CLI zarfı, satır içi nesne) uygula: önce-tam-aday-doğrula sonra-atomik-yaz; lint-invalid → reddetme, disk bayt-bayt aynı; lco change çekirdeği |
| lco_renew_status | {dir, json?} | Yenileme durumu (DETERMINİSTİK, salt-okunur, LLM YOK) |
| lco_renew_export | {dir} | Modernizasyon raporu (deterministik; yeni analiz yapmaz; içerik olarak döner — dosya YAZMAZ; dosya çıktısı yalnızca CLI lco renew export --out) |
| lco_renew_analyze | {dir, scope?, llmProfile?, consent?} | Yenileme analizi (ÜCRETLİ LLM çağrısı) — LCO_MCP_ALLOW_GENERATE=1 + consent.digest = renewConsentDigest(dir, scope, llmProfile?); rıza yoksa sıfır çağrı |
Claude Code'a kaydetmek için (mutlak yol ile):
claude mcp add lco -- node /abs/yol/packages/spec-core/dist/mcp/server.jsJSON yapılandırma alternatifi (ör. .mcp.json veya kendi istemciniz):
{
"mcpServers": {
"lco": { "command": "node", "args": ["/abs/yol/packages/spec-core/dist/mcp/server.js"] }
}
}Notlar:
- Önce derleyin: sunucu
dist/den koşar —pnpm --filter ./packages/spec-core build(yukarıdaki test-sırası notuyla aynı gerekçe). - stdout yalnız JSON-RPC (bağlayıcı): stdout'a yalnız yanıt satırları yazılır; her
tanılama stderr'e gider. Eski
mcp_bridgehatasının (protokol akışına log karışması) tekrarı yasaktır ve test-enforcelıdır: entegrasyon testi spawn edilen sürecin stdout'undaki her satırıJSON.parseile doğrular. dirargümanı her çağrıda realpath ile normalize edilir (bağlantılı üst dizinlerden ulaşılan bir kök yasaldır ve gerçek yoluna çözülür) ve çözülen yol etkin izinli kökün (effective allowed root) içinde kalmak zorundadır (SEC-003 — bağlayıcı politika, her araca,lco_generate'ın yazma hedefine kadar). Etkin kök:LCO_MCP_EXEC_ROOTayarlıysa o değerin realpath'ı; ayarl DEĞİLSE sunucunun çalışma dizininin realpath'ı — pinsiz sunucu kendi çalışma dizinine sabitlenir; "politika yok" modu YOKTUR (denetim kalıntısı: opsiyonel güvenlik reddedildi). Dışarıdaki/red dışındakidir-32602ile reddedilir (ret mesajı kökün kaynağını adlandırır: pin mi çalışma dizini mi); etkin kök bir dizine çözülmüyorsa (silinmiş pin, silinmiş cwd) HER araç çağrısı fail-closed reddedilir. Ret hiçbir çekirdek çağrısı, LLM adapter'ı, shell yürütmesi veya dosya yazımı yapmadan önce gerçekleşir. Dağıtım uyarısı: pinsiz sunucuda etkin kökün genişliği sunucunun NEREDE başlatıldığına bağlıdır — proje dizininden başlatılan sunucu proje kökünü alır (iyi),$HOME'dan başlatılan (kullanıcı-kapsamlı MCP kaydı)$HOME'u alır,/'dan başlatılan mutlak-yollar için fiilen sınırsızdır. Dizin dışına açık bir sunucu için daimaLCO_MCP_EXEC_ROOTsabitleyin (mutlak yol ile — göreli pin sunucunun kendi çalışma dizinine göre çözülür, öngörülemez).lco_checkaracı varsayılan olarak SADECE önizleme yapar (DRY) —yesparametresi MCP yüzeyinden kaldırıldı (SEC-002); yürütme rızası için aşağıdaki Yürütme Rızası bölümüne bakın.lco_generatede request'in kendi başına ASLA ücretli çağrı harcamaz (PROD-004): rıza zinciri için aşağıdaki Ücretli Çağrı Rızası bölümüne bakın.- Tüm araç şemaları
additionalProperties: falseve argüman katmanı fail-closed: bilinmeyen anahtar-32602;yesİSİMLE reddedilir;allowExec/allowGenerate/llm/envgibi yetenek-şekilli anahtarlar da isimle reddedilir (request kendi yetenisini kendi veremez — bunlar operatörün sunucu-sınırı durumudur). - El smoke'u (gerçek stdio):
initialize→serverInfo {name: "lco-mcp", version: "0.1.0"},protocolVersion 2025-06-18;tools/list→ yukarıdaki 13 araç;tools/call lco_check {dir}→isError: false, ilk satırDRY RUN — no commands executed; pass --yes to execute. id'siz geçerli bildirimler sessizdir; id TAŞIYAN her geçerli istek yanıt alır (notifications/*bile — bilinen hiçbir işleyicisi yoksa-32601, id yankılanır); bozuk satır-32700(idnull); bilinmeyen araç-32602; bilinmeyen metod-32601.
Dayanıklılık ve Protokol Sınırları (OPS-001, SEC-006)
Sunucu tek bir stdio oturumunu sınırlarla yönetir (src/mcp/stdio.ts); hiçbir
girdi türü süreci sınırsız belleğe, sınırsız eşzamanlı işe veya sessiz bir
yarıda kesintiye (truncated exit) götüremez:
- Frame sınırı — 1 MiB/satır. stdin parça parça (chunk) okunur ve satırlar
bir BAYT bütçesi altında birleştirilir; sınırı aşan satır ASLA tamamen
tamponlanmaz: taşan baytlar sonraki newline'a kadar atılır, istemciye bir
kez
-32600 Request too large(idnull) yanıtı verilir, stderr'e tanılama düşer ve bağlantı AÇIK kalır — bir sonraki düzgün satır normal hizmet görür. Meşru MCP frameleri küçüktür (en büyük argüman 10k karakterlik inline intent'tir); 1 MiB %100 pay demektir. (Nodereadlinesatırı tamponlamadan sınırlayamaz — bu yüzden assembler sunucunun kendisindedir.) - Eşzamanlı iş sınırı — 16 in-flight istek. Bir istek kabul anından yanıtın
yazılmasına kadar "in-flight" sayılır. 17.'si anında yapılandırılmış bir
-32000 Server busyhatası alır (kendi id'si yankılanır) — sıraya girmez, bekletilmez. Bildirimler ve bozuk satırlar iş başlatmadığı için bu sınıra takılmaz. Sınır, bir istemcinin aynı anda ayakta tutabileceği araç koşusunu, mutasyonu ve çocuk süreci sınırlar. - Mutasyon serileştirme. Aynı kökteki mutasyonlar depolama katmanının
kök-başına kilidiyle zaten serileşir (T6; sunucu düzeyinde de sabitlenmiştir);
farklı kökler in-flight sınırına kadar eşzamanlı ilerler. Ek kural
(bilinçli karar): aynı kök için ikinci bir
lco_generateilk uçarken anında yapılandırılmış reddetme alır (isError, SIFIR LLM çağrısı). Önce her ikisi de rızadan geçip ücretli boru hattını İKİ KEZ koşturuyordu (yazma no-clobber ile güvenliydi ama harcama ikileydi); ücretli olan tek araç için in-flight tekrar-reddi ucuz ve dürüsttür.lco_init/lco_changeyerel ve ücretsizdir — onlar kilit semantiğinde kalır (T10 exactly-one-winner). Dedup anahtarı istenendir'in sözcüksel çözümüdür (path.resolve); sembolik bağlantı takma adlarını yakalamaz — doğruluk yededi her zaman kilitken bu yalnızca harcama dedup'idir. - stdout backpressure. Yanıtlar
Writable.write'tan geçer;falsedöndüğünde stdin okuma DURUR ve akışdrainolana kadar durur. Duraklatılmış girdi yeni satır üretmediği için yazma kuyruğu yapısal olarak sınırlıdır (in-flight ≤ 16 yanıt + duraklatılmış boru) — sınırsız tampon büyümesi yoktur. - Kapanış semantiği ve çıkış kodları. stdin EOF (düzenli kapanış): yeni
satır alınmaz, in-flight işin bitmesi ve bekleyen yazımların boşalması
beklenir, çıkış
0. stdout EPIPE (istemci öldü): yeni satır alınmaz, artık yazılmaz (ölü bora yanıt yazılmaz — yarım satır oluşmaz), in-flight işin bitmesi 10 saniyelik drain penceresi içinde beklenir (başlamış disk yazımları ve çocuk yaşam döngüleri bitsin diye), sonra çıkış3— iş ortada bırakıldı, sessiz0asla değil. Drain penceresi aşılırsa hâlâ koşan doğrulama süreç grupları SIGKILL edilir (ölü bir süreç onları reap edemez — OPS-001/SEC-005 kapsama) ve çıkış4olur. EOF yolunda yapay zamanlayıcı YOKTUR: araçların kendi iç bütçeleri vardır (UX-003 wall budget, check timeout'ları). Kapanış zamanlayıcısı süreç sınırında duvar-saatlidir — T16 gerekçesiyle aynı: gerçek bir stdio oturumunun gerçek kapanışını yönetir, deterministik çekirdeğin parçası değildir (testlerde enjekte edilebilir). - JSON-RPC 2.0 zarfı (SEC-006). Dispatch'ten ÖNCE tam doğrulama:
jsonrpctam olarak"2.0"olmalı ("1.0",2.0sayısı, eksik →-32600);methodboş olmayan dize;idvarsa dize/sayı/null— nesne/dizi/boolean id reddedilir ve ASLA yankılanmaz (yanıtın id'sinull; JSON-RPC 2.0 §5.1 id-saptama kuralı);paramsvarsa nesne olmalı (MCP adlandırılmış parametre kullanır; konumsal dizi reddedilir); zarf dışı bilinmeyen alan reddedilir (additionalProperties:falsesıkılaştırma politikasının zarf uzantısı); batch (dizi gövde) tek bir-32600hatasıyla reddedilir — sunucu tasarımı gereği satır-başına-tek-istektir (stdio-MCP batch'e ihtiyaç duymaz; belgelenmiş no-batch tavrı). Sessizlik YALNIZCA id yokluğuyla tanımlanır (JSON-RPC 2.0): idsiz geçerli istek (bildirim) yanıt almaz; id TAŞIYAN her geçerli istek — yöntemnotifications/*olsa bile — bir Request'tir ve yanıt alır (işleyicisi yoksa-32601, id yankılanır; SEC-006 kalıntı kapanışı: yöntem-adına göre susma geçersizdi). Geçersiz zarf id'siz olsa bileid:nullhatası alır; geçersiz id ASLA yankılanmaz.
Yürütme Rızası: lco_check ve LCO_MCP_ALLOW_EXEC (SEC-002)
Güven sınırı (trust boundary) modeli: spec metni modelin kontrolündedir ve bir
istem (prompt injection) MCP istemcisini lco_check'i yürütme için
kullanmaya bir adım uzaktır. Bu yüzden MCP üzerinden komut yürütme, insan
rızasının vekili DEĞİLDİR — dört katman, hepsi birlikte zorunlu:
- Server-start opt-in: yürütme yeteneği yalnız sunucu
LCO_MCP_ALLOW_EXEC=1ile başlatıldığında vardır (tam olarak1; başka her değer fail-closed). Düz başlatılmış sunucuda hiçbir parametre kombinasyonu yürütme sağlayamaz:yesargümanı (-32602) reddedilir,consentgönderen istek actionable bir reddetme (isError, exit 2) alır. - İçerik kalitesi: spec frozen + hash-doğrulanmış + lint-clean olmalıdır
(
loadBundleAtLevel('lint-clean')+verifyFrozençekirdekleri). Draft spec, freeze sonrası yeniden hash'i eşleşmeyen (drift'e girmiş) içerik veya lint-kirli bundle, neyin başarısız olduğunu adlandıran bir reddetmeyle geri çevrilir (drift doğrulaması anlamsal bölüm kimliğidir — tampon-boşluğu düzeyinde düzenleme yakalamaz; bkz. "Bilinen Sınırlar"). - Önizleme-hash'ine bağlı rıza: yürütme isteği
consent.digesttaşır — tam olarak neyin koşacağına ilişkin özet (sha256(JSON.stringify({spec_version, tasks:[{task_id, verification:[{command, expect}]}]}, null, 2)); DRY yanıt bu özeticonsent digest:satırında ilan eder). Sunucu yürütme anında beklenen özeti yeniden hesaplar ve uyuşmazlıkta reddeder: istemci bir içeriği onaylayıp başkasını koşturamaz; task filtresi seçimin parçasıdır. - Scrub edilmiş ortam: yürütülen komutlar sunucunun ortamını DEĞİL, açık bir
izin listesini miras alır:
PATH,HOME,LANG,LC_ALL,TMPDIR(+ POSIX'te bulunmayanSystemRoot,PATHEXT,ComSpec). ÖzellikleLCO_LLM_API_KEYgibi sunucu sırları,NODE_OPTIONSveLCO_MCP_*bayrakları çocuk süreçlere ASLA geçmez.
İsteğe bağlı 5. katman: LCO_MCP_EXEC_ROOT=/abs/yol çalışma alanını sabitler —
ayarlandığında rıza yalnız o yolun (realpath ile çözülmüş) altına RESOLVE EDEN
spec kökleri için geçerlidir (SEC-003: sözcüksel olarak pin altında görünen ama
sembolik bağlantıyla dışarı kaçan bir yol reddedilir). Aynı pin sunucu sınırında
HER aracın dir argümanına da uygulanır (aşağıdaki izinli-kök politikası) —
lco_generate'ın yazma hedefi dahil. Bu bir rıza-sınırı sabitlemesidir; süreç
izolasyonu P2-2 kapsamındadır.
CLI asimetrisi (bilinçli): lco check --yes miras alınan tam ortamla koşar —
orada rızayı veren insan, ortamın da sahibidir. MCP yolunda rızayı veren
operatördür (sunucuyu LCO_MCP_ALLOW_EXEC=1 ile başlatan) ve modelin koşturduğu
komutlar operatörün sırlarını göremez.
Atipik akış: dry önizleme (draft) → lco freeze → aynı digest ile
consent:{digest} yürütme — freeze spec_version'ı ve task içeriğini
değiştirmediği için digest geçerliliğini korur; frozen+verified kapısı durumu ayrıca
denetler.
Ücretli Çağrı Rızası: lco_generate ve LCO_MCP_ALLOW_GENERATE (PROD-004)
Yürütme rızasıyla aynı güven modeli, geri döndürülemez diğer kaynak için: para. Bir MCP isteği (model; prompt injection'a bir adım uzak) kendi başına sunucuyu ücretli LLM çağrısı harcatamaz. İki katman, ikisi birden zorunlu — biri bile eksikse yapılandırılmış reddetme, SIFIR LLM çağrısı (testler çağrı sayısını 0 olarak sabitler):
Server-start opt-in: üretim yeteneği yalnız sunucu
LCO_MCP_ALLOW_GENERATE=1ile başlatıldığında vardır (tam olarak1;'true','0', boş, unset → fail-closed). BayrakLCO_MCP_ALLOW_EXEC'ten BAĞIMSIZDIR: hiçbiri diğerini içermez.Etkin içeriğe bağlı rıza: istek
consent.digesttaşır — tam olarak neyin LLM'e gideceğinin özeti:sha256(JSON.stringify({ intent, profile, variant }, null, 2))(manifest artifact-hash'leriyle aynı çerçeve). Sunucu digest'i ÇÖZÜLMÜŞ değerler üzerinden (varsayılanlar uygulanmış: variant=single, profile=p-standard) isteğin işlenme anında yeniden hesaplar; uyuşmazlıkta her iki digest'i adlandıran reddetme.
consentiçermeyen istek, reddetmenin kendisi önizlemedir: yanıtta bu isteğin digest'iconsent digest:satırında ilan edilir — actionable retry bir istek ötededir.dirbilinçli olarak digest'e DAHİL DEĞİLDİR (operatörün rızası ücretli çağrının İÇERİĞİNE, yazma hedefine değildir; yazmanın kendi no-clobber + yaşam döngüsü kapıları vardır).
Adapter kuralları (CLI ile aynı): mock adapter yalnız test/kütüphane çağıranları
için sınırdan enjekte edilir (HandleRpcOptions.llm); üretim adapter'ı
cmdGenerate İÇİNDE createHttpLlm() ile çözülür ve kullanıcı tarafından sağlanan
LCO_LLM_BASE_URL/LCO_LLM_API_KEY/LCO_LLM_MODEL ortam değişkenleri eksikse
fail-closed throw eder — sunucu ASLA anahtar, uç nokta veya model uydurmaz (mock önce,
live yalnız gerçek env'den). Üretim çıktısı CLI ile aynı kapılardan geçer: no-clobber,
kanıt kapısı (blocked → nedenler, hiçbir şey yazılmaz), savunma lint'i, yaşam döngüsı
çıkış kapısı (draft/v1/profile), ve councilDegraded satırı araç yanıtında yüzeye çıkar.
CLI asimetrisi (bilinçli, Yürütme Rızası ile aynı gerekçe): lco generate --intent
yolunda harcama kararını veren insan, env'in (ve hesabın) da sahibidir. MCP yolunda
harcamayı talep eden modeldir; onayı operatör verir (sunucuyu
LCO_MCP_ALLOW_GENERATE=1 ile başlatan) ve rıza içeriğe bağlı digest ile sabitlenir.
Strictness Politikası
Bilinmeyen anahtar her yerde reddedilir, sessizce silinmez:
- SpecBundle'ın tüm zod object yüzeyleri
.strict()(bundle kökü, manifest, task, refs, verification öğesi, karar alternatifleri…); metin alanlarıtrim().min(1)— boşluk-dize geçmez. - Changeset zarfı
ChangeSetSchema.strict(): typo bir üst-düzey anahtar (modified_taskz) fail-closed hatadır — sessiz no-op sürüm sıçraması değil. - Task patch
TaskContractSchema.partial().strict(): typo yama anahtarı (titel) reddedilir; MERGE sonrası tam şema yeniden doğrulanır. - MCP araç argümanlarında bilinmeyen anahtar →
-32602. - TS zod zinciri ile dışa aktarılan
generated/spec-schema.json(additionalProperties: false) hizalıdır. (Bu paketin eski sürümündeki "zod siler / JSON Şema reddeder" yüzey farkı, tamamlama planının şema-sıkılaştırma göreviyle kapatıldı.)
L12 Kapsam-Örtüşme Semantiği (BACK-007)
L12_SCOPE_OVERLAP bir ERROR'dur ve dondurma kapısıdır; bu yüzden örtüşme modeli
yaklaşıklık değil, TANIMLI bir desen dili üzerinde kesindir:
- Desen dili (
permitted_scopeglob'ları):/-ayrılmış segment dizisi; segment içinde edebi karakter kendini,?TAM OLARAK BİR karakteri (/hariç),*SIFIR VEYA DAHA FAZLA karakteri (/hariç; ardışık yıldızlar tektir:a**b=a*b), yalnızca**yazılan segment İSTENEN SAYIDA segmenti (sıfır dahil) eşler (src/**→srckendisi ve altındaki her şey).\/'ye normalize edilir; boş segmentler (//, sonaki/) atılır. Bu dilin dışındaki dizeler (karakter sınıfları, küme parantezleri) edebi kabul edilir. - Örtüşme tanımı: iki glob, İKİSİNİ de sağlayan bir dosya yolu VARsa örtüşür.
Bu alt küme için kesin hesaplanır (segment-birleşim +
**-farkındalıklı yol DP;src/lint/rules/l12.ts— birim-test edilmiş saf fonksiyonlar, tablo + kaba-kuvvet çapraz denetim ile). Sonuç:src/*.tsilesrc/*.mdKANITLANARAK ayrıktır (uzantı farkı tanık gerektirir),src/*.tsilesrc/*.t?kanıtlanarak örtüşür (src/a.tstanığı),*asla/geçmez. - Sıralama semantiği: çakışma, iki görev arasında bir
depends_onYOLU (geçişli kapanış; A←B←C zinciri de dahil) VARSA bastırılır — doğrudan kenar yeterli ama gerekli değildir. Kayıtlı gerekçe: zincir de aynı şekilde serileştirir, tavlama denetimin adını verdiği yanlış-pozitif sınıfıydı; kapanış girdi tavanlarındaki boyutlarda ucuzdur (iteratif DFS; derin zincillerde yığın taşması yok); döngü içindeki her çift "sıralı" sayılır ama döngü zaten L04'ün hatasıdır. Elmas ortası (B ve C ikisi de A'ya bağlı, aralarında yol YOK) hâlâ işaretlenir — gerçekten paralel koşabilirler. - Hata mesajı çareyi adlandırır: görevler arasına
depends_onyolu ekle YA DA kapsamları ayır.
Girdi Tavanları (PERF-001)
Şema, kareli lint/hash işlerinin KoşMASINDAN ÖNCE girdiyi sınırlar (düşmanca MCP
girdisi ve başıboş LLM çıktısı için bir duvar — seyyar tripwire değil). Tavanlar
fixture/eval bünyesindeki en büyük gözlemlenen kullanımın ~10x+ üstünde seçildi
(ölçüm önce yapıldı; tam tablo src/schemas/limits.ts başlığında):
| Alan | Ölçülen max | Tavan |
| --- | --- | --- |
| görev / bundle | 4 | 100 |
| requirement / karar / kanıt / sözlük / varsayım / sözleşme | 1–4 | 100 (her biri) |
| refs.*, depends_on, permitted_scope, protected (görev başına) | 0–2 | 50 |
| tests, verification (görev başına) | 1 | 20 |
| test case sayısı (test başına) | 2 | 50 |
| title | 25 krk | 500 |
| purpose / rollback | 63–73 krk | 4.000 |
| instructions | 111 krk | 20.000 |
| liste öğesi / komut / dosya-yolu | ≤83 krk | 1.000–2.000 |
| intent.statement | 111 krk | 100.000 (niyet yankısı uzun olabilir) |
KIRICI SIKILAŞTIRMA: tavanan aşan bundle şema hatasıyla reddedilir ve hata
limiti + çareyi adlandırır (bundle exceeds 100 tasks — split the spec into
separately frozen bundles). Ölçek regresyonu src/scale-benchmark.test.ts ile
korumalıdır: 10/100/1000 görevlik deterministik sentetik bundle'lar üzerinde
L12 + kapanış + lint + hash + derleme, cömert (~10x) tavanların altında
kalmalıdır (turuncu değil kırmızı bir sınır — aşıldığında mertibe regresyonu var
demektir).
Şema Sürümü ve Uyumluluk Politikası (lco-spec/1.x)
PROD-005. manifest.spec_schema alanı, ağacın yazıldığı şema sürümünü bildirir.
Tek kaynak src/schemas/version.ts içindeki SPEC_SCHEMA_VERSION sabitidir (şu an
lco-spec/1.0); manifest şeması bu sabiti birebir zorunlu kılar ve init iskeleti
aynı sabiti yazar — literal kodda başka hiçbir yerde tekrarlanmaz.
Politika:
1.0bugüne dek çıkarılmış TEK şema sürümüdür; şemaların minor-sürüm kavramı henüz yoktur. Aşağıdaki kurallar GELECEK minor'ları yönetir — bugünlco-spec/1.0'dan başka okunabilir sürüm yoktur ve bu politika kendisi için minor makineleri icat etmez.- Okuma uyumluluğu (read-compat): major 1 içinde YENİ derleyiciler ESKİ 1.x
donmuş ağaçlarını okumak ZORUNDADIR — bir frozen artifact'ın dayandığı garanti
budur. Bir
1.1çıkarsa, 1.1 derleyicisilco-spec/1.0ağaçlarını da okur; kabul edilen sürüm kümesicheckSpecSchemaVersioniçindeki işaretli büyüme noktasında BİLİNÇLİ olarak büyütülür. - Kendinden yeni minor bildiren spec: derleyicinin bildiğinden YENİ bir 1.x
minor'u bildiren ağaç (
lco-spec/1.2gibi) oku
