npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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 --help

Kaynaktan (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 handshake

Sı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 0

3) 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 0

Tamper 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 0

4) 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 0

5) 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 0

7) 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 0

Kanı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. --yes yoksa HİÇBİR komut koşulmaz: her satırın durumu DRY, çıkış 0, diske hiçbir şey yazılmaz (spec/evidence/ dizini bile oluşmaz). Tablo, --yes altında neyin koşacağının önizlemesidir.
  • --yes açı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, expect açıklamasındaki İLK exit N eşleşmesidir. exit N bulunamayan 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). --yes altı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 edilen nowIso + 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.
  • Yol güvenliği (SEC-003). Spec kökü realpath ile çözülür; sabit bölüm yolları (spec/<bölüm>.json) ve spec/evidence dizini çö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 veya spec/ 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/evidence veya 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 SIGTERM alır; grace penceresi (400 ms) sonunda hâlâ yaşayan üye varsa grup SIGKILL ile öldürülür. Executor'ın kararı döndürmesi, grubun ölmüş (veya SIGKILL edilmiş) olması anlamına gelir — bir TIMEOUT sonucu 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-CAP sayı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.ts MAX_BUFFER_BYTES baş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. TIMEOUT yalnızca gerçek zaman aşımı kill'i ve sinyalle ölüm içindir; operatör TIMEOUT gö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 --yes onayı.

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.ts içindeki DEFAULT_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.ts README'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_EXCEEDED hatası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-ms bayrakları veya LCO_GENERATE_MAX_ATTEMPTS / LCO_GENERATE_MAX_TOKENS / LCO_GENERATE_MAX_WALL_MS env 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ı unknown görünür — asla 0 in / 0 out değil; G4 maliyet koşulu da unknown'ı geçmez (0 <= 3×0 kanı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_KEY ve LCO_LLM_MODEL kullanı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) ve LCO_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-file karşılıklı dışlar, bozuk bayrak), intent preflight (UX-004), --intent-file okuma/boş-dosya denetimi, no-clobber (<dir>/spec varsa exit 2) ve env denetimi sırasıyla. Yanlış çağrı hiç ücret ödemez.

  • Intent preflight (UX-004): --intent metni 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-file uzun 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). MCP intent argü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=true dö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_MISSING ile 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-openrouter

Without --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 official provider.only (restrictive in both modes) / provider.order (exhaustive here because fallbacks are off), require_parameters when structured output is requested, and gateway auto-routers are prohibited (RouteLLM's route-llm is 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-readable

One 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.json

Each 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 graph

Trust 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.js

JSON 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_bridge hatası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.parse ile doğrular.
  • dir argü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_ROOT ayarlı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ışındaki dir -32602 ile 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 daima LCO_MCP_EXEC_ROOT sabitleyin (mutlak yol ile — göreli pin sunucunun kendi çalışma dizinine göre çözülür, öngörülemez).
  • lco_check aracı varsayılan olarak SADECE önizleme yapar (DRY) — yes parametresi 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_generate de 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: false ve argüman katmanı fail-closed: bilinmeyen anahtar -32602; yes İSİMLE reddedilir; allowExec/allowGenerate/llm/ env gibi 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): initializeserverInfo {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ır DRY 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 (id null); 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 (id null) 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. (Node readline satı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 busy hatası 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_generate ilk 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_change yerel ve ücretsizdir — onlar kilit semantiğinde kalır (T10 exactly-one-winner). Dedup anahtarı istenen dir'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; false döndüğünde stdin okuma DURUR ve akış drain olana 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ı, sessiz 0 asla 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ış 4 olur. 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: jsonrpc tam olarak "2.0" olmalı ("1.0", 2.0 sayısı, eksik → -32600); method boş olmayan dize; id varsa dize/sayı/null — nesne/dizi/boolean id reddedilir ve ASLA yankılanmaz (yanıtın id'si null; JSON-RPC 2.0 §5.1 id-saptama kuralı); params varsa nesne olmalı (MCP adlandırılmış parametre kullanır; konumsal dizi reddedilir); zarf dışı bilinmeyen alan reddedilir (additionalProperties:false sıkılaştırma politikasının zarf uzantısı); batch (dizi gövde) tek bir -32600 hatası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öntem notifications/* 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 bile id:null hatası 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:

  1. Server-start opt-in: yürütme yeteneği yalnız sunucu LCO_MCP_ALLOW_EXEC=1 ile başlatıldığında vardır (tam olarak 1; başka her değer fail-closed). Düz başlatılmış sunucuda hiçbir parametre kombinasyonu yürütme sağlayamaz: yes argümanı (-32602) reddedilir, consent gönderen istek actionable bir reddetme (isError, exit 2) alır.
  2. İç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").
  3. Önizleme-hash'ine bağlı rıza: yürütme isteği consent.digest taşı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 özeti consent 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.
  4. 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 bulunmayan SystemRoot, PATHEXT, ComSpec). Özellikle LCO_LLM_API_KEY gibi sunucu sırları, NODE_OPTIONS ve LCO_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):

  1. Server-start opt-in: üretim yeteneği yalnız sunucu LCO_MCP_ALLOW_GENERATE=1 ile başlatıldığında vardır (tam olarak 1; 'true', '0', boş, unset → fail-closed). Bayrak LCO_MCP_ALLOW_EXEC'ten BAĞIMSIZDIR: hiçbiri diğerini içermez.

  2. Etkin içeriğe bağlı rıza: istek consent.digest taşı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. consent içermeyen istek, reddetmenin kendisi önizlemedir: yanıtta bu isteğin digest'i consent digest: satırında ilan edilir — actionable retry bir istek ötededir. dir bilinç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_scope glob'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/**src kendisi 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/*.ts ile src/*.md KANITLANARAK ayrıktır (uzantı farkı tanık gerektirir), src/*.ts ile src/*.t? kanıtlanarak örtüşür (src/a.ts tanığı), * asla / geçmez.
  • Sıralama semantiği: çakışma, iki görev arasında bir depends_on YOLU (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_on yolu 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.0 bugü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ün lco-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 derleyicisi lco-spec/1.0 ağaçlarını da okur; kabul edilen sürüm kümesi checkSpecSchemaVersion iç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.2 gibi) oku