# Parametrik CAD — Knowledge / Parametre Sistemi (Faz 1) Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** FikoYT 3D motorunu tamamen merkezi parametrik yapmak — depo tek otorite; her feature değeri merkezi parametre; serbest + intrinsic parametreler, formüller, gruplar, oto/manuel akıllı rebuild, tam kaydet/aç.

**Architecture:** Saf-TS çekirdek (ifade motoru + depo + bağımlılık grafiği + orkestratör) `FikoModellemeYonetici` içine tek `FikoKnowledgeYonetici` olarak girer. `rebuild()` başına **çözümleme geçişi** eklenir: tüm parametreler topolojik çözülür, değerler `IOzellik` alanlarına (write-through önbellek) yazılır → 7200 satırlık geometri kodu değişmez. UI: ürün ağacındaki mevcut `grp.userData.FikoParametreler` chip sistemi yeniden kullanılır (f(x) chip eklenir) + yeni odaklı `fiko-parametre-workbench` paneli.

**Tech Stack:** TypeScript 5.8, Angular 20 (standalone, OnPush, signal), Three.js 0.177, Karma + Jasmine (`ng test`).

**Spec:** `docs/superpowers/specs/2026-06-20-parametrik-cad-knowledge-design.md`

**Konum:** `src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/`

---

## Dosya Yapısı

Yeni çekirdek (saf TS, THREE bağımsız), alt klasör `fiko-modelleme/knowledge/`:
- `knowledge/IFikoKnowledge.ts` — tipler: `IFikoParametre`, `IFikoParametreGrup`, `ParamTip`, `IAlanTanim`, `IFeatureAlanHaritasi`
- `knowledge/FikoIfadeMotoru.ts` — ifade motoru (tokenizer + parser + evaluator)
- `knowledge/FikoIfadeMotoru.spec.ts`
- `knowledge/FikoParametreDeposu.ts` — parametre + grup deposu, intrinsic yayın
- `knowledge/FikoParametreDeposu.spec.ts`
- `knowledge/FikoBagimlilikGrafi.ts` — bağımlılık grafiği (topo sıra, döngü, downstream)
- `knowledge/FikoBagimlilikGrafi.spec.ts`
- `knowledge/FikoFeatureAlanHaritasi.ts` — feature tipi → yayınlanacak sayısal alanlar
- `knowledge/FikoKnowledgeYonetici.ts` — orkestratör (depo+motor+grafik, oto/manuel, rebuild köprüsü)
- `knowledge/FikoKnowledgeYonetici.spec.ts`

Değişecek mevcut dosyalar:
- `FikoModellemeYonetici.ts` — `IOzellik.id`, knowledge instance, resolve geçişi, `rebuild(baslangicIndex)`, feature-oluşturma kancası, serialize/deserialize
- `Fiko3DComponent/agac-item/agac-item.component.html` — f(x) chip bloğu
- `Fiko3DComponent/agac-item/agac-item.component.ts` — f(x) chip tıklama → formül editörü köprüsü

Yeni UI:
- `fiko-parametre-workbench/fiko-parametre-workbench.component.ts` (+`.html` template inline) — odaklı CAD parametre paneli

---

## FAZ 1A — Çekirdek (saf mantık, TDD)

### Task 1: Knowledge tipleri

**Files:**
- Create: `src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/knowledge/IFikoKnowledge.ts`

- [ ] **Step 1: Tip dosyasını yaz**

```typescript
// knowledge/IFikoKnowledge.ts
export type ParamTip = 'Uzunluk' | 'Aci' | 'Tamsayi' | 'Reel' | 'Boolean' | 'Metin';

/** Bir parametre (serbest ya da intrinsic). intrinsic ise feat backref dolu. */
export interface IFikoParametre {
  id: string;
  ad: string;                              // benzersiz tanımlayıcı (formülde kullanılır)
  tip: ParamTip;
  deger: number | boolean | string;        // çözülmüş güncel değer
  formul?: string;                         // varsa değer formülden hesaplanır
  birim?: string;                          // gösterim (mm/deg)
  aciklama?: string;
  kilitli?: boolean;
  grupId?: string;                         // ait olduğu grup (yoksa kök)
  feat?: { featId: string; alan: string }; // intrinsic ise feature alanı backref
  hata?: string;                           // son çözümlemede hata (Türkçe mesaj)
}

export interface IFikoParametreGrup {
  id: string;
  ad: string;
  ustGrupId?: string;
  aciklama?: string;
}

/** Bir feature tipinde yayınlanacak tek sayısal alan tanımı. */
export interface IAlanTanim {
  alan: string;        // IOzellik alan adı (örn 'param', 'holeCap')
  etiket: string;      // parametre adında kullanılacak okunur etiket (örn 'Uzunluk')
  tip: ParamTip;
}

/** feature.tip → yayınlanacak alanlar. */
export type IFeatureAlanHaritasi = { [featureTip: string]: IAlanTanim[] };
```

- [ ] **Step 2: Commit**

```bash
git add src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/knowledge/IFikoKnowledge.ts
git commit -m "feat(knowledge): parametre tipleri (IFikoParametre/Grup/AlanHaritasi)"
```

---

### Task 2: FikoIfadeMotoru — ifade motoru

**Files:**
- Create: `knowledge/FikoIfadeMotoru.ts`
- Test: `knowledge/FikoIfadeMotoru.spec.ts`

Kurallar: uzunluk birimleri mm tabanlı (`mm`=1, `cm`=10, `m`=1000), açı birimleri derece tabanlı (`deg`/`°`=1, `rad`=180/π). Trig fonksiyonları **derece** alır/verir (CAD bağlamı). Sabitler `PI`, `E`. Koşullu: `if(kosul, a, b)`.

- [ ] **Step 1: Failing test yaz**

```typescript
// knowledge/FikoIfadeMotoru.spec.ts
import { FikoIfadeMotoru, FikoIfadeHata } from './FikoIfadeMotoru';

const coz = (m: Record<string, number>) => (ad: string) => m[ad];

describe('FikoIfadeMotoru', () => {
  it('aritmetik öncelik', () => {
    expect(FikoIfadeMotoru.degerlendir('2 + 3 * 4', coz({})).deger).toBe(14);
    expect(FikoIfadeMotoru.degerlendir('(2 + 3) * 4', coz({})).deger).toBe(20);
    expect(FikoIfadeMotoru.degerlendir('2 ^ 3 ^ 2', coz({})).deger).toBe(512); // sağ-assoc
    expect(FikoIfadeMotoru.degerlendir('-5 + 2', coz({})).deger).toBe(-3);
  });
  it('parametre referansı + çıkarımı', () => {
    const r = FikoIfadeMotoru.degerlendir('boy / 4 + etKalinlik', coz({ boy: 100, etKalinlik: 5 }));
    expect(r.deger).toBe(30);
    expect(r.referanslar.sort()).toEqual(['boy', 'etKalinlik']);
  });
  it('fonksiyonlar (trig derece)', () => {
    expect(FikoIfadeMotoru.degerlendir('sin(30)', coz({})).deger).toBeCloseTo(0.5, 6);
    expect(FikoIfadeMotoru.degerlendir('sqrt(16)', coz({})).deger).toBe(4);
    expect(FikoIfadeMotoru.degerlendir('max(3, 7, 5)', coz({})).deger).toBe(7);
    expect(FikoIfadeMotoru.degerlendir('round(3.6)', coz({})).deger).toBe(4);
  });
  it('koşullu + karşılaştırma', () => {
    expect(FikoIfadeMotoru.degerlendir('if(boy > 50, 10, 20)', coz({ boy: 100 })).deger).toBe(10);
    expect(FikoIfadeMotoru.degerlendir('if(boy > 50 && boy < 80, 1, 2)', coz({ boy: 100 })).deger).toBe(2);
  });
  it('birim literalleri (mm tabanı, derece tabanı)', () => {
    expect(FikoIfadeMotoru.degerlendir('2cm + 5mm', coz({})).deger).toBe(25);
    expect(FikoIfadeMotoru.degerlendir('1m', coz({})).deger).toBe(1000);
    expect(FikoIfadeMotoru.degerlendir('90deg', coz({})).deger).toBe(90);
  });
  it('sabitler', () => {
    expect(FikoIfadeMotoru.degerlendir('PI', coz({})).deger).toBeCloseTo(Math.PI, 6);
  });
  it('hatalı ifade → FikoIfadeHata', () => {
    expect(() => FikoIfadeMotoru.degerlendir('2 +', coz({}))).toThrowError(FikoIfadeHata);
    expect(() => FikoIfadeMotoru.degerlendir('bilinmeyen()', coz({}))).toThrowError(FikoIfadeHata);
  });
  it('referanslar() değerlendirmeden çıkarır', () => {
    expect(FikoIfadeMotoru.referanslar('a + b*2 - sin(c)').sort()).toEqual(['a', 'b', 'c']);
  });
});
```

- [ ] **Step 2: Test fail (motor yok)**

Run: `npx ng test --watch=false --browsers=ChromeHeadless --include='**/FikoIfadeMotoru.spec.ts'`
Expected: FAIL — "Cannot find module './FikoIfadeMotoru'"

- [ ] **Step 3: Motoru yaz**

```typescript
// knowledge/FikoIfadeMotoru.ts
export class FikoIfadeHata extends Error {
  constructor(m: string) { super(m); this.name = 'FikoIfadeHata'; }
}

export type ParamCozumleyici = (ad: string) => number | boolean | string | undefined;
export interface IfadeSonuc { deger: number | boolean | string; referanslar: string[]; }

type Tok = { t: 'num' | 'id' | 'op' | 'lp' | 'rp' | 'comma'; v: string; n?: number };

const SABITLER: Record<string, number> = { PI: Math.PI, E: Math.E };
const BIRIM: Record<string, number> = { mm: 1, cm: 10, m: 1000, deg: 1, '°': 1, rad: 180 / Math.PI };
const D2R = Math.PI / 180, R2D = 180 / Math.PI;
const FONK: Record<string, (a: number[]) => number> = {
  sin: a => Math.sin(a[0] * D2R), cos: a => Math.cos(a[0] * D2R), tan: a => Math.tan(a[0] * D2R),
  asin: a => Math.asin(a[0]) * R2D, acos: a => Math.acos(a[0]) * R2D, atan: a => Math.atan(a[0]) * R2D,
  atan2: a => Math.atan2(a[0], a[1]) * R2D,
  sqrt: a => Math.sqrt(a[0]), abs: a => Math.abs(a[0]), sign: a => Math.sign(a[0]),
  min: a => Math.min(...a), max: a => Math.max(...a), pow: a => Math.pow(a[0], a[1]),
  round: a => Math.round(a[0]), floor: a => Math.floor(a[0]), ceil: a => Math.ceil(a[0]),
  log: a => Math.log(a[0]), exp: a => Math.exp(a[0]),
};

function tokenize(s: string): Tok[] {
  const out: Tok[] = []; let i = 0;
  const ops = ['<=', '>=', '==', '!=', '&&', '||', '+', '-', '*', '/', '%', '^', '<', '>', '!'];
  while (i < s.length) {
    const c = s[i];
    if (c === ' ' || c === '\t' || c === '\n') { i++; continue; }
    if (c === '(') { out.push({ t: 'lp', v: c }); i++; continue; }
    if (c === ')') { out.push({ t: 'rp', v: c }); i++; continue; }
    if (c === ',') { out.push({ t: 'comma', v: c }); i++; continue; }
    if (/[0-9.]/.test(c)) {
      let j = i; while (j < s.length && /[0-9.]/.test(s[j])) j++;
      let num = parseFloat(s.slice(i, j));
      // birim eki?
      const rest = s.slice(j);
      const bm = rest.match(/^(mm|cm|m|deg|rad|°)/);
      if (bm) { num *= BIRIM[bm[1]]; j += bm[1].length; }
      out.push({ t: 'num', v: s.slice(i, j), n: num }); i = j; continue;
    }
    if (/[A-Za-z_]/.test(c)) {
      let j = i; while (j < s.length && /[A-Za-z0-9_]/.test(s[j])) j++;
      out.push({ t: 'id', v: s.slice(i, j) }); i = j; continue;
    }
    const op = ops.find(o => s.startsWith(o, i));
    if (op) { out.push({ t: 'op', v: op }); i += op.length; continue; }
    throw new FikoIfadeHata(`Geçersiz karakter: "${c}"`);
  }
  return out;
}

// AST düğümleri
type Node =
  | { k: 'num'; v: number }
  | { k: 'id'; v: string }
  | { k: 'un'; op: string; a: Node }
  | { k: 'bin'; op: string; a: Node; b: Node }
  | { k: 'call'; ad: string; args: Node[] };

const ONCELIK: Record<string, number> = { '||': 1, '&&': 2, '==': 3, '!=': 3, '<': 4, '<=': 4, '>': 4, '>=': 4, '+': 5, '-': 5, '*': 6, '/': 6, '%': 6, '^': 8 };

class Parser {
  i = 0;
  constructor(private toks: Tok[]) {}
  peek(): Tok | undefined { return this.toks[this.i]; }
  next(): Tok { const t = this.toks[this.i++]; if (!t) throw new FikoIfadeHata('Beklenmeyen ifade sonu'); return t; }
  parse(): Node { const n = this.expr(0); if (this.i < this.toks.length) throw new FikoIfadeHata('Fazla simge'); return n; }
  expr(min: number): Node {
    let sol = this.atom();
    while (true) {
      const t = this.peek();
      if (!t || t.t !== 'op' || ONCELIK[t.v] == null || ONCELIK[t.v] < min) break;
      const op = t.v; const sag = ONCELIK[op]; this.i++;
      const sonraki = op === '^' ? sag : sag + 1; // ^ sağ-assoc
      const b = this.expr(sonraki);
      sol = { k: 'bin', op, a: sol, b };
    }
    return sol;
  }
  atom(): Node {
    const t = this.next();
    if (t.t === 'op' && (t.v === '-' || t.v === '!')) return { k: 'un', op: t.v, a: this.expr(7) };
    if (t.t === 'num') return { k: 'num', v: t.n! };
    if (t.t === 'lp') { const n = this.expr(0); const r = this.next(); if (r.t !== 'rp') throw new FikoIfadeHata('")" bekleniyordu'); return n; }
    if (t.t === 'id') {
      if (this.peek()?.t === 'lp') {
        this.i++; const args: Node[] = [];
        if (this.peek()?.t !== 'rp') { args.push(this.expr(0)); while (this.peek()?.t === 'comma') { this.i++; args.push(this.expr(0)); } }
        const r = this.next(); if (r.t !== 'rp') throw new FikoIfadeHata('")" bekleniyordu');
        return { k: 'call', ad: t.v, args };
      }
      return { k: 'id', v: t.v };
    }
    throw new FikoIfadeHata(`Beklenmeyen simge: "${t.v}"`);
  }
}

function topla(n: Node, refs: Set<string>): void {
  if (n.k === 'id') { if (SABITLER[n.v] == null) refs.add(n.v); }
  else if (n.k === 'un') topla(n.a, refs);
  else if (n.k === 'bin') { topla(n.a, refs); topla(n.b, refs); }
  else if (n.k === 'call') n.args.forEach(a => topla(a, refs));
}

function num(v: number | boolean | string): number {
  if (typeof v === 'number') return v;
  if (typeof v === 'boolean') return v ? 1 : 0;
  const f = parseFloat(v as string); return isNaN(f) ? 0 : f;
}

function eval2(n: Node, coz: ParamCozumleyici): number {
  switch (n.k) {
    case 'num': return n.v;
    case 'id': {
      if (SABITLER[n.v] != null) return SABITLER[n.v];
      const d = coz(n.v);
      if (d === undefined) throw new FikoIfadeHata(`Bilinmeyen parametre: "${n.v}"`);
      return num(d);
    }
    case 'un': { const a = eval2(n.a, coz); return n.op === '-' ? -a : (a ? 0 : 1); }
    case 'call': {
      if (n.ad === 'if') { if (n.args.length !== 3) throw new FikoIfadeHata('if(kosul,a,b) 3 argüman ister'); return eval2(n.args[0], coz) ? eval2(n.args[1], coz) : eval2(n.args[2], coz); }
      const f = FONK[n.ad]; if (!f) throw new FikoIfadeHata(`Bilinmeyen fonksiyon: "${n.ad}"`);
      return f(n.args.map(a => eval2(a, coz)));
    }
    case 'bin': {
      const a = eval2(n.a, coz), b = eval2(n.b, coz);
      switch (n.op) {
        case '+': return a + b; case '-': return a - b; case '*': return a * b;
        case '/': return a / b; case '%': return a % b; case '^': return Math.pow(a, b);
        case '<': return a < b ? 1 : 0; case '<=': return a <= b ? 1 : 0;
        case '>': return a > b ? 1 : 0; case '>=': return a >= b ? 1 : 0;
        case '==': return a === b ? 1 : 0; case '!=': return a !== b ? 1 : 0;
        case '&&': return a && b ? 1 : 0; case '||': return a || b ? 1 : 0;
      }
      throw new FikoIfadeHata(`Bilinmeyen operatör: "${n.op}"`);
    }
  }
}

export class FikoIfadeMotoru {
  static degerlendir(ifade: string, coz: ParamCozumleyici): IfadeSonuc {
    if (!ifade || !ifade.trim()) throw new FikoIfadeHata('Boş ifade');
    const ast = new Parser(tokenize(ifade)).parse();
    const refs = new Set<string>(); topla(ast, refs);
    return { deger: eval2(ast, coz), referanslar: [...refs] };
  }
  static referanslar(ifade: string): string[] {
    try { const ast = new Parser(tokenize(ifade)).parse(); const r = new Set<string>(); topla(ast, r); return [...r]; }
    catch { return []; }
  }
}
```

- [ ] **Step 4: Test pass**

Run: `npx ng test --watch=false --browsers=ChromeHeadless --include='**/FikoIfadeMotoru.spec.ts'`
Expected: PASS (8 spec)

- [ ] **Step 5: Commit**

```bash
git add src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/knowledge/FikoIfadeMotoru.ts src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/knowledge/FikoIfadeMotoru.spec.ts
git commit -m "feat(knowledge): ifade motoru (tokenizer+parser+evaluator) + testler"
```

---

### Task 3: FikoBagimlilikGrafi — bağımlılık grafiği

**Files:**
- Create: `knowledge/FikoBagimlilikGrafi.ts`
- Test: `knowledge/FikoBagimlilikGrafi.spec.ts`

Düğüm = parametre `ad`. Kenar: A formülünde B geçiyorsa B→A (B değişince A etkilenir). Topolojik sıra, döngü tespiti, bir kümeden downstream kapanış.

- [ ] **Step 1: Failing test**

```typescript
// knowledge/FikoBagimlilikGrafi.spec.ts
import { FikoBagimlilikGrafi } from './FikoBagimlilikGrafi';

describe('FikoBagimlilikGrafi', () => {
  // a serbest; b = a+1; c = b*2
  const kenarlar = { a: [], b: ['a'], c: ['b'] } as Record<string, string[]>;
  it('topolojik sıra (bağımlılık önce)', () => {
    const g = new FikoBagimlilikGrafi(kenarlar);
    const sira = g.topolojikSira();
    expect(sira.indexOf('a')).toBeLessThan(sira.indexOf('b'));
    expect(sira.indexOf('b')).toBeLessThan(sira.indexOf('c'));
  });
  it('downstream kapanış', () => {
    const g = new FikoBagimlilikGrafi(kenarlar);
    expect(g.downstream(['a']).sort()).toEqual(['b', 'c']);
    expect(g.downstream(['b']).sort()).toEqual(['c']);
  });
  it('döngü tespiti', () => {
    const g = new FikoBagimlilikGrafi({ x: ['y'], y: ['x'] });
    expect(g.donguVarMi()).toBe(true);
    const g2 = new FikoBagimlilikGrafi(kenarlar);
    expect(g2.donguVarMi()).toBe(false);
  });
});
```

- [ ] **Step 2: Test fail**

Run: `npx ng test --watch=false --browsers=ChromeHeadless --include='**/FikoBagimlilikGrafi.spec.ts'`
Expected: FAIL — modül yok

- [ ] **Step 3: Grafiği yaz**

```typescript
// knowledge/FikoBagimlilikGrafi.ts
/** bagimliliklar[x] = x'in formülünde geçen parametre adları. */
export class FikoBagimlilikGrafi {
  private ileri = new Map<string, Set<string>>();   // dep -> dependents (dep değişince bunlar etkilenir)
  private dugumler: string[];
  constructor(bagimliliklar: Record<string, string[]>) {
    this.dugumler = Object.keys(bagimliliklar);
    for (const ad of this.dugumler) this.ileri.set(ad, new Set());
    for (const [ad, deps] of Object.entries(bagimliliklar))
      for (const d of deps) { if (!this.ileri.has(d)) this.ileri.set(d, new Set()); this.ileri.get(d)!.add(ad); }
  }
  topolojikSira(): string[] {
    const derece = new Map<string, number>(); const tumu = new Set([...this.ileri.keys()]);
    for (const a of tumu) derece.set(a, 0);
    for (const [, deps] of this.ileri) for (const t of deps) derece.set(t, (derece.get(t) || 0) + 1);
    const kuyruk = [...tumu].filter(a => (derece.get(a) || 0) === 0).sort();
    const sira: string[] = [];
    while (kuyruk.length) {
      const a = kuyruk.shift()!; sira.push(a);
      for (const t of (this.ileri.get(a) || [])) { derece.set(t, derece.get(t)! - 1); if (derece.get(t) === 0) kuyruk.push(t); }
    }
    return sira;
  }
  downstream(degisen: string[]): string[] {
    const sonuc = new Set<string>(); const yigin = [...degisen];
    while (yigin.length) { const a = yigin.pop()!; for (const t of (this.ileri.get(a) || [])) if (!sonuc.has(t)) { sonuc.add(t); yigin.push(t); } }
    return [...sonuc];
  }
  donguVarMi(): boolean {
    const toplam = new Set([...this.ileri.keys()]).size;
    return this.topolojikSira().length < toplam;
  }
}
```

- [ ] **Step 4: Test pass**

Run: `npx ng test --watch=false --browsers=ChromeHeadless --include='**/FikoBagimlilikGrafi.spec.ts'`
Expected: PASS (3 spec)

- [ ] **Step 5: Commit**

```bash
git add src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/knowledge/FikoBagimlilikGrafi.ts src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/knowledge/FikoBagimlilikGrafi.spec.ts
git commit -m "feat(knowledge): bağımlılık grafiği (topo sıra, downstream, döngü)"
```

---

### Task 4: FikoFeatureAlanHaritasi — alan haritası

**Files:**
- Create: `knowledge/FikoFeatureAlanHaritasi.ts`

`IOzellik`'in ana feature tipleri için yayınlanacak sayısal alanlar. Eksik tipler sonradan eklenir; harita merkezi kayıttır. (Tam liste spec'teki ~70 tip; burada yaygın olanlar + yapı verilir, kalanlar aynı kalıpla eklenir.)

- [ ] **Step 1: Haritayı yaz**

```typescript
// knowledge/FikoFeatureAlanHaritasi.ts
import { IFeatureAlanHaritasi } from './IFikoKnowledge';

/** feature.tip → yayınlanacak sayısal alanlar. Etiket = ürün ağacı/parametre adında görünür. */
export const FEATURE_ALAN_HARITASI: IFeatureAlanHaritasi = {
  pad:      [{ alan: 'param', etiket: 'Uzunluk', tip: 'Uzunluk' }, { alan: 'param2', etiket: 'Uzunluk2', tip: 'Uzunluk' }, { alan: 'draftAci', etiket: 'EğimAçısı', tip: 'Aci' }],
  pocket:   [{ alan: 'param', etiket: 'Derinlik', tip: 'Uzunluk' }, { alan: 'param2', etiket: 'Derinlik2', tip: 'Uzunluk' }],
  shaft:    [{ alan: 'aci', etiket: 'Açı', tip: 'Aci' }, { alan: 'aci2', etiket: 'Açı2', tip: 'Aci' }],
  groove:   [{ alan: 'aci', etiket: 'Açı', tip: 'Aci' }, { alan: 'aci2', etiket: 'Açı2', tip: 'Aci' }],
  hole:     [{ alan: 'holeCap', etiket: 'Çap', tip: 'Uzunluk' }, { alan: 'holeDerinlik', etiket: 'Derinlik', tip: 'Uzunluk' }, { alan: 'cbCap', etiket: 'HavşaÇap', tip: 'Uzunluk' }, { alan: 'cbDerinlik', etiket: 'HavşaDerinlik', tip: 'Uzunluk' }, { alan: 'csCap', etiket: 'HavşaKoniÇap', tip: 'Uzunluk' }, { alan: 'csAci', etiket: 'HavşaAçı', tip: 'Aci' }],
  fillet3d: [{ alan: 'kenarMesafe', etiket: 'Yarıçap', tip: 'Uzunluk' }, { alan: 'kenarMesafe2', etiket: 'Yarıçap2', tip: 'Uzunluk' }],
  chamfer3d:[{ alan: 'kenarMesafe', etiket: 'Uzunluk', tip: 'Uzunluk' }, { alan: 'chamferAci', etiket: 'Açı', tip: 'Aci' }],
  draft:    [{ alan: 'aci', etiket: 'Açı', tip: 'Aci' }],
  shell:    [{ alan: 'kalinlik', etiket: 'Kalınlık', tip: 'Uzunluk' }],
  rib:      [{ alan: 'kalinlik', etiket: 'Kalınlık', tip: 'Uzunluk' }],
  stiffener:[{ alan: 'kalinlik', etiket: 'Kalınlık', tip: 'Uzunluk' }],
  thickness:[{ alan: 'kalinlik', etiket: 'Kalınlık', tip: 'Uzunluk' }],
  primitif: [{ alan: 'pEn', etiket: 'En', tip: 'Uzunluk' }, { alan: 'pBoy', etiket: 'Boy', tip: 'Uzunluk' }, { alan: 'pYuk', etiket: 'Yükseklik', tip: 'Uzunluk' }, { alan: 'pCap', etiket: 'Çap', tip: 'Uzunluk' }, { alan: 'pTbCap', etiket: 'TepeÇap', tip: 'Uzunluk' }],
  patternDik:     [{ alan: 'adet', etiket: 'Adet', tip: 'Tamsayi' }, { alan: 'aralik', etiket: 'Aralık', tip: 'Uzunluk' }, { alan: 'adet2', etiket: 'Adet2', tip: 'Tamsayi' }, { alan: 'aralik2', etiket: 'Aralık2', tip: 'Uzunluk' }],
  patternDairesel:[{ alan: 'adet', etiket: 'Adet', tip: 'Tamsayi' }, { alan: 'aci', etiket: 'Açı', tip: 'Aci' }],
  patternIzgara:  [{ alan: 'adet', etiket: 'Adet', tip: 'Tamsayi' }, { alan: 'aralik', etiket: 'Aralık', tip: 'Uzunluk' }, { alan: 'adet2', etiket: 'Adet2', tip: 'Tamsayi' }, { alan: 'aralik2', etiket: 'Aralık2', tip: 'Uzunluk' }],
  translate:[{ alan: 'trMesafe', etiket: 'Mesafe', tip: 'Uzunluk' }, { alan: 'trX', etiket: 'X', tip: 'Uzunluk' }, { alan: 'trY', etiket: 'Y', tip: 'Uzunluk' }, { alan: 'trZ', etiket: 'Z', tip: 'Uzunluk' }],
  rotate:   [{ alan: 'aci', etiket: 'Açı', tip: 'Aci' }],
  scaling:  [{ alan: 'scaleFaktor', etiket: 'Faktör', tip: 'Reel' }],
  makro:    [{ alan: 'mCap', etiket: 'Çap', tip: 'Uzunluk' }, { alan: 'mEtKal', etiket: 'EtKalınlık', tip: 'Uzunluk' }, { alan: 'mUzun', etiket: 'Uzunluk', tip: 'Uzunluk' }, { alan: 'mAci', etiket: 'Açı', tip: 'Aci' }, { alan: 'mYaricap', etiket: 'Yarıçap', tip: 'Uzunluk' }],
  yazi3d:   [{ alan: 'boyut', etiket: 'Boyut', tip: 'Uzunluk' }, { alan: 'harfBosluk', etiket: 'HarfBoşluk', tip: 'Uzunluk' }],
};

/** Bir feature için yayın alanlarını döndürür (yoksa boş). */
export function featureAlanlari(tip: string): import('./IFikoKnowledge').IAlanTanim[] {
  return FEATURE_ALAN_HARITASI[tip] || [];
}
```

- [ ] **Step 2: Commit**

```bash
git add src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/knowledge/FikoFeatureAlanHaritasi.ts
git commit -m "feat(knowledge): feature alan haritası (intrinsic yayın katalogu)"
```

---

### Task 5: FikoParametreDeposu — depo

**Files:**
- Create: `knowledge/FikoParametreDeposu.ts`
- Test: `knowledge/FikoParametreDeposu.spec.ts`

Parametre + grup CRUD, ad çakışma/geçerlilik, rename-safe (formüllerde referans güncelle), intrinsic yayın, grup taşıma döngüsüzlük.

- [ ] **Step 1: Failing test**

```typescript
// knowledge/FikoParametreDeposu.spec.ts
import { FikoParametreDeposu } from './FikoParametreDeposu';

describe('FikoParametreDeposu', () => {
  let d: FikoParametreDeposu;
  beforeEach(() => { d = new FikoParametreDeposu(); });

  it('serbest parametre ekle + ad çakışma', () => {
    d.parametreEkle({ ad: 'boy', tip: 'Uzunluk', deger: 100 });
    expect(d.getAd('boy')?.deger).toBe(100);
    expect(() => d.parametreEkle({ ad: 'boy', tip: 'Uzunluk', deger: 5 })).toThrow();
  });
  it('geçersiz ad reddedilir', () => {
    expect(() => d.parametreEkle({ ad: '1boy', tip: 'Reel', deger: 1 })).toThrow();
    expect(() => d.parametreEkle({ ad: 'bo y', tip: 'Reel', deger: 1 })).toThrow();
  });
  it('rename → formüldeki referans güncellenir', () => {
    d.parametreEkle({ ad: 'boy', tip: 'Uzunluk', deger: 100 });
    const c = d.parametreEkle({ ad: 'cap', tip: 'Uzunluk', deger: 0, formul: 'boy / 4' });
    d.adDegistir(d.getAd('boy')!.id, 'uzunluk');
    expect(d.getAd('cap')!.formul).toBe('uzunluk / 4');
    expect(d.getAd('uzunluk')).toBeTruthy();
  });
  it('intrinsic yayın (idempotent)', () => {
    d.featAlanYayinla('f1', 'Pad.1', 'param', 'Uzunluk', 'Uzunluk', 20);
    const p = d.getFeatAlan('f1', 'param');
    expect(p?.ad).toBe('Pad.1\\Uzunluk');
    expect(p?.deger).toBe(20);
    expect(p?.feat).toEqual({ featId: 'f1', alan: 'param' });
    d.featAlanYayinla('f1', 'Pad.1', 'param', 'Uzunluk', 'Uzunluk', 99); // tekrar → yeni kopya değil
    expect(d.parametreler.filter(x => x.feat?.featId === 'f1' && x.feat?.alan === 'param').length).toBe(1);
  });
  it('grup ekle + taşı + döngü engeli', () => {
    const g1 = d.grupEkle('Gövde');
    const g2 = d.grupEkle('Delikler', g1.id);
    d.parametreEkle({ ad: 'boy', tip: 'Uzunluk', deger: 100, grupId: g1.id });
    expect(d.getAd('boy')!.grupId).toBe(g1.id);
    d.tasiParametre(d.getAd('boy')!.id, g2.id);
    expect(d.getAd('boy')!.grupId).toBe(g2.id);
    expect(() => d.tasiGrup(g1.id, g2.id)).toThrow(); // g1'i kendi torununa taşıma → döngü
  });
});
```

- [ ] **Step 2: Test fail**

Run: `npx ng test --watch=false --browsers=ChromeHeadless --include='**/FikoParametreDeposu.spec.ts'`
Expected: FAIL — modül yok

- [ ] **Step 3: Depoyu yaz**

```typescript
// knowledge/FikoParametreDeposu.ts
import { FikoYTId } from '@/src/service/fikoyt-ui/helpers/FikoYTId';
import { IFikoParametre, IFikoParametreGrup, ParamTip } from './IFikoKnowledge';
import { FikoIfadeMotoru } from './FikoIfadeMotoru';

const GECERLI_AD = /^[A-Za-z_][A-Za-z0-9_]*$/;

export class FikoParametreDeposu {
  parametreler: IFikoParametre[] = [];
  gruplar: IFikoParametreGrup[] = [];

  getId(id: string) { return this.parametreler.find(p => p.id === id); }
  getAd(ad: string) { return this.parametreler.find(p => p.ad === ad); }
  getFeatAlan(featId: string, alan: string) { return this.parametreler.find(p => p.feat?.featId === featId && p.feat?.alan === alan); }

  parametreEkle(p: Partial<IFikoParametre> & { ad: string; tip: ParamTip; deger: any }): IFikoParametre {
    if (!GECERLI_AD.test(p.ad)) throw new Error(`Geçersiz parametre adı: "${p.ad}" (harf/altçizgi ile başlamalı, boşluksuz)`);
    if (this.getAd(p.ad)) throw new Error(`"${p.ad}" adında parametre zaten var`);
    const yeni: IFikoParametre = { id: new FikoYTId().toString(), ad: p.ad, tip: p.tip, deger: p.deger, formul: p.formul, birim: p.birim, aciklama: p.aciklama, kilitli: p.kilitli, grupId: p.grupId, feat: p.feat };
    this.parametreler.push(yeni);
    return yeni;
  }

  parametreSil(id: string): void { this.parametreler = this.parametreler.filter(p => p.id !== id); }

  adDegistir(id: string, yeniAd: string): void {
    const p = this.getId(id); if (!p) return;
    if (!GECERLI_AD.test(yeniAd)) throw new Error(`Geçersiz ad: "${yeniAd}"`);
    if (this.getAd(yeniAd) && this.getAd(yeniAd) !== p) throw new Error(`"${yeniAd}" zaten var`);
    const eski = p.ad; p.ad = yeniAd;
    // formüllerde tam-kelime referansı güncelle
    const re = new RegExp(`\\b${eski.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'g');
    for (const q of this.parametreler) if (q.formul && FikoIfadeMotoru.referanslar(q.formul).includes(eski)) q.formul = q.formul.replace(re, yeniAd);
  }

  /** intrinsic yayın: aynı (featId,alan) varsa günceller, yoksa oluşturur. featAd örn 'Pad.1'. */
  featAlanYayinla(featId: string, featAd: string, alan: string, etiket: string, tip: ParamTip, deger: any): IFikoParametre {
    const v = this.getFeatAlan(featId, alan);
    if (v) { if (!v.formul) v.deger = deger; return v; }
    const ad = this.benzersizAd(`${featAd}\\${etiket}`);
    const yeni: IFikoParametre = { id: new FikoYTId().toString(), ad, tip, deger, feat: { featId, alan } };
    this.parametreler.push(yeni); return yeni;
  }
  /** bir feature'a ait tüm intrinsic parametreleri sil (feature silinince). */
  featKaldir(featId: string): void { this.parametreler = this.parametreler.filter(p => p.feat?.featId !== featId); }

  private benzersizAd(taban: string): string {
    if (!this.getAd(taban)) return taban; let i = 2; while (this.getAd(`${taban}_${i}`)) i++; return `${taban}_${i}`;
  }

  // ── Gruplar ──
  grupEkle(ad: string, ustGrupId?: string): IFikoParametreGrup {
    const g: IFikoParametreGrup = { id: new FikoYTId().toString(), ad, ustGrupId };
    this.gruplar.push(g); return g;
  }
  grupSil(id: string): void {
    const g = this.gruplar.find(x => x.id === id); if (!g) return;
    for (const p of this.parametreler) if (p.grupId === id) p.grupId = g.ustGrupId;     // içindekiler üst gruba
    for (const c of this.gruplar) if (c.ustGrupId === id) c.ustGrupId = g.ustGrupId;
    this.gruplar = this.gruplar.filter(x => x.id !== id);
  }
  tasiParametre(paramId: string, hedefGrupId?: string): void { const p = this.getId(paramId); if (p) p.grupId = hedefGrupId; }
  tasiGrup(grupId: string, hedefGrupId?: string): void {
    if (grupId === hedefGrupId) throw new Error('Grup kendi içine taşınamaz');
    let u = hedefGrupId; // hedefin atalarında grupId varsa döngü
    while (u) { if (u === grupId) throw new Error('Döngüsel grup taşıma engellendi'); u = this.gruplar.find(g => g.id === u)?.ustGrupId; }
    const g = this.gruplar.find(x => x.id === grupId); if (g) g.ustGrupId = hedefGrupId;
  }
}
```

> Not: `FikoYTId` `@/src/...` alias'ı ile import edilir (agac-item ve diğer bileşenlerde kullanılan kalıp). `FikoYTId extends String` olduğundan `.toString()` ile string'e çevrilir.

- [ ] **Step 4: Test pass**

Run: `npx ng test --watch=false --browsers=ChromeHeadless --include='**/FikoParametreDeposu.spec.ts'`
Expected: PASS (5 spec). Import yolu hatası alırsan FikoYTId importunu `@/src/service/fikoyt-ui/helpers/FikoYTId` yap.

- [ ] **Step 5: Commit**

```bash
git add src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/knowledge/FikoParametreDeposu.ts src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/knowledge/FikoParametreDeposu.spec.ts
git commit -m "feat(knowledge): parametre+grup deposu (CRUD, rename-safe, intrinsic yayın)"
```

---

### Task 6: FikoKnowledgeYonetici — orkestratör

**Files:**
- Create: `knowledge/FikoKnowledgeYonetici.ts`
- Test: `knowledge/FikoKnowledgeYonetici.spec.ts`

Depo + motor + grafik. `mode: 'oto'|'manuel'`. `cozumle()` tüm parametreleri topolojik çözer (formülleri hesaplar, `deger` günceller, döngü/hata işaretler). `parametreGuncelle` oto modda rebuild callback'i tetikler. rebuild köprüsü dışarıdan enjekte edilir (test edilebilirlik + THREE bağımsızlık).

- [ ] **Step 1: Failing test**

```typescript
// knowledge/FikoKnowledgeYonetici.spec.ts
import { FikoKnowledgeYonetici } from './FikoKnowledgeYonetici';

describe('FikoKnowledgeYonetici', () => {
  let y: FikoKnowledgeYonetici; let rebuildCagrildi: number[];
  beforeEach(() => {
    rebuildCagrildi = [];
    y = new FikoKnowledgeYonetici();
    y.rebuildKopru = (idx) => rebuildCagrildi.push(idx ?? 0);
  });

  it('formül zinciri topolojik çözülür', () => {
    y.depo.parametreEkle({ ad: 'boy', tip: 'Uzunluk', deger: 100 });
    y.depo.parametreEkle({ ad: 'cap', tip: 'Uzunluk', deger: 0, formul: 'boy / 4' });
    y.depo.parametreEkle({ ad: 'cevre', tip: 'Uzunluk', deger: 0, formul: 'cap * PI' });
    y.cozumle();
    expect(y.depo.getAd('cap')!.deger).toBe(25);
    expect(y.depo.getAd('cevre')!.deger as number).toBeCloseTo(78.539, 2);
  });
  it('oto modda parametre değişince rebuild tetiklenir', () => {
    y.mode = 'oto';
    const boy = y.depo.parametreEkle({ ad: 'boy', tip: 'Uzunluk', deger: 100 });
    y.parametreGuncelle(boy.id, 50);
    expect(boy.deger).toBe(50);
    expect(rebuildCagrildi.length).toBe(1);
  });
  it('manuel modda rebuild beklenir, guncelle ile tetiklenir', () => {
    y.mode = 'manuel';
    const boy = y.depo.parametreEkle({ ad: 'boy', tip: 'Uzunluk', deger: 100 });
    y.parametreGuncelle(boy.id, 50);
    expect(rebuildCagrildi.length).toBe(0);
    expect(y.kirliVarMi).toBe(true);
    y.guncelle();
    expect(rebuildCagrildi.length).toBe(1);
    expect(y.kirliVarMi).toBe(false);
  });
  it('döngü → hata işareti, çökmeden', () => {
    const a = y.depo.parametreEkle({ ad: 'a', tip: 'Reel', deger: 0, formul: 'b + 1' });
    y.depo.parametreEkle({ ad: 'b', tip: 'Reel', deger: 0, formul: 'a + 1' });
    y.cozumle();
    expect(a.hata).toBeTruthy();
  });
});
```

- [ ] **Step 2: Test fail**

Run: `npx ng test --watch=false --browsers=ChromeHeadless --include='**/FikoKnowledgeYonetici.spec.ts'`
Expected: FAIL — modül yok

- [ ] **Step 3: Orkestratörü yaz**

```typescript
// knowledge/FikoKnowledgeYonetici.ts
import { FikoParametreDeposu } from './FikoParametreDeposu';
import { FikoBagimlilikGrafi } from './FikoBagimlilikGrafi';
import { FikoIfadeMotoru } from './FikoIfadeMotoru';
import { IFikoParametre, ParamTip } from './IFikoKnowledge';

export class FikoKnowledgeYonetici {
  depo = new FikoParametreDeposu();
  mode: 'oto' | 'manuel' = 'oto';
  private kirli = new Set<string>();        // bekleyen değişen parametre id'leri (manuel)
  /** rebuild köprüsü — FikoModellemeYonetici enjekte eder. idx: en erken etkilenen feature index. */
  rebuildKopru: (baslangicIndex?: number) => void = () => {};
  /** ad → feature index çözümü (FikoModellemeYonetici enjekte eder; intrinsic param'ın hangi feature'a ait olduğunu index olarak verir). */
  featIndexCoz: (featId: string) => number = () => 0;

  get kirliVarMi(): boolean { return this.kirli.size > 0; }

  /** Tüm parametreleri topolojik sırada çöz: formülleri hesapla, deger güncelle, hata işaretle. */
  cozumle(): void {
    const bag: Record<string, string[]> = {};
    for (const p of this.depo.parametreler) bag[p.ad] = p.formul ? FikoIfadeMotoru.referanslar(p.formul) : [];
    const grafik = new FikoBagimlilikGrafi(bag);
    const donguVar = grafik.donguVarMi();
    const sira = grafik.topolojikSira();
    const coz = (ad: string) => this.depo.getAd(ad)?.deger;
    const islenmis = new Set<string>();
    for (const ad of sira) {
      const p = this.depo.getAd(ad); if (!p) continue; islenmis.add(ad);
      p.hata = undefined;
      if (!p.formul) continue;
      try { p.deger = this.tipDonustur(FikoIfadeMotoru.degerlendir(p.formul, coz).deger, p.tip); }
      catch (e: any) { p.hata = e?.message || 'Formül hatası'; }
    }
    // döngüde kalan (topo sırada olmayan) formüllü parametreler → hata
    if (donguVar) for (const p of this.depo.parametreler) if (p.formul && !islenmis.has(p.ad)) p.hata = 'Döngüsel bağımlılık';
  }

  private tipDonustur(v: number | boolean | string, tip: ParamTip): number | boolean | string {
    if (tip === 'Tamsayi') return Math.round(v as number);
    if (tip === 'Boolean') return !!v;
    if (tip === 'Metin') return String(v);
    return v;
  }

  /** Parametre değeri/formülü değişti → çöz + (oto) rebuild ya da (manuel) kirli işaretle. */
  parametreGuncelle(id: string, yeni: { deger?: any; formul?: string } | any): void {
    const p = this.depo.getId(id); if (!p) return;
    if (yeni && typeof yeni === 'object' && ('deger' in yeni || 'formul' in yeni)) {
      if ('formul' in yeni) p.formul = yeni.formul || undefined;
      if ('deger' in yeni && !p.formul) p.deger = yeni.deger;
    } else { if (!p.formul) p.deger = yeni; }     // kısa biçim: ham değer
    this.cozumle();
    if (this.mode === 'oto') this.tetikle([p]); else { this.kirli.add(id); }
  }

  /** Manuel modda biriken değişiklikleri uygula. */
  guncelle(): void {
    if (!this.kirli.size) return;
    const degisen = [...this.kirli].map(id => this.depo.getId(id)).filter(Boolean) as IFikoParametre[];
    this.kirli.clear(); this.cozumle(); this.tetikle(degisen);
  }

  /** Değişen parametrelerden en erken etkilenen feature index'ini bulup rebuild köprüsünü çağır. */
  private tetikle(degisen: IFikoParametre[]): void {
    const adlar = degisen.map(p => p.ad);
    const bag: Record<string, string[]> = {};
    for (const p of this.depo.parametreler) bag[p.ad] = p.formul ? FikoIfadeMotoru.referanslar(p.formul) : [];
    const etkilenen = new Set<string>([...adlar, ...new FikoBagimlilikGrafi(bag).downstream(adlar)]);
    let minIdx = Infinity;
    for (const ad of etkilenen) { const p = this.depo.getAd(ad); if (p?.feat) minIdx = Math.min(minIdx, this.featIndexCoz(p.feat.featId)); }
    this.rebuildKopru(isFinite(minIdx) ? minIdx : 0);
  }
}
```

- [ ] **Step 4: Test pass**

Run: `npx ng test --watch=false --browsers=ChromeHeadless --include='**/FikoKnowledgeYonetici.spec.ts'`
Expected: PASS (4 spec)

- [ ] **Step 5: Commit**

```bash
git add src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/knowledge/FikoKnowledgeYonetici.ts src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/knowledge/FikoKnowledgeYonetici.spec.ts
git commit -m "feat(knowledge): orkestratör (cozumle, oto/manuel, akıllı rebuild köprüsü)"
```

---

## FAZ 1B — FikoModellemeYonetici entegrasyonu

### Task 7: IOzellik.id + knowledge instance + resolve geçişi

**Files:**
- Modify: `fiko-modelleme/FikoModellemeYonetici.ts`

- [ ] **Step 1: IOzellik'e id alanı ekle**

`FikoModellemeYonetici.ts` içinde `export interface IOzellik {` ilk satırına ekle (mevcut `tip:` satırından önce):

```typescript
  id?: string;                     // kararlı feature kimliği (knowledge/intrinsic backref)
```

- [ ] **Step 2: knowledge import + instance**

Dosya başındaki importlara ekle:

```typescript
import { FikoKnowledgeYonetici } from './knowledge/FikoKnowledgeYonetici';
import { featureAlanlari } from './knowledge/FikoFeatureAlanHaritasi';
```

`export class FikoModellemeYonetici {` gövdesinde alan tanımlarına ekle (örn `geometriSeti` yakını):

```typescript
  knowledge = new FikoKnowledgeYonetici();
```

Kurucuda (constructor) ya da `modBaslat()` içinde köprüleri bağla:

```typescript
    this.knowledge.rebuildKopru = (idx) => this.rebuild(idx);
    this.knowledge.featIndexCoz = (featId) => Math.max(0, this.ozellikler.findIndex(f => f.id === featId));
```

- [ ] **Step 3: id üretici yardımcı + her feature'a id garantisi**

Sınıfa özel metod ekle (örn serialize yakını):

```typescript
  /** Her feature'ın kararlı id'si olsun (yoksa üret). */
  private featIdGaranti(f: IOzellik): string {
    if (!f.id) f.id = 'f_' + Math.max(1, this.ozellikSayac) + '_' + (this.ozellikler.indexOf(f) + 1) + '_' + this.ozellikler.length;
    return f.id;
  }
```

> Not: id mutlak kararlı olmalı (index'ten türetme tek seferlik tohum, sonra sabit kalır). Üretildikten sonra `f.id` değiştirilmez.

- [ ] **Step 4: rebuild(baslangicIndex) imzası + resolve geçişi**

Mevcut `private rebuild(): void {` imzasını değiştir:

```typescript
  private rebuild(baslangicIndex: number = 0): void {
```

`rebuild` gövdesinin EN BAŞINA (mevcut ilk satırdan önce) resolve geçişini ekle:

```typescript
    // ── Merkezi parametre çözümleme: depo değerlerini feature alanlarına yaz (write-through önbellek) ──
    this.parametreCozUygula();
```

Ve şu metodu sınıfa ekle:

```typescript
  /** Tüm parametreleri çöz ve intrinsic parametre değerlerini feature alanlarına yaz. */
  private parametreCozUygula(): void {
    this.knowledge.cozumle();
    for (const p of this.knowledge.depo.parametreler) {
      if (!p.feat) continue;
      const f = this.ozellikler.find(x => x.id === p.feat!.featId);
      if (f && typeof p.deger === 'number') (f as any)[p.feat.alan] = p.deger;
    }
  }
```

> `baslangicIndex` şimdilik resolve için kullanılmıyor (geometri tam-replay sıralı); ileride feature replay başlangıcını kısaltmak için kullanılabilir. Mevcut tam-rebuild davranışı korunur (varsayılan 0).

- [ ] **Step 5: Derleme doğrula**

Run: `npx tsc --noEmit -p src/service/fikoyt-ui/tsconfig.lib.json 2>&1 | head -30` (yoksa proje kökünde `npx ng build --configuration development` ilk derleme hatalarını gösterir)
Expected: knowledge ile ilgili tip hatası YOK. (Mevcut ~40 ipucu kabul edilebilir.)

- [ ] **Step 6: Commit**

```bash
git add src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/FikoModellemeYonetici.ts
git commit -m "feat(modelleme): knowledge entegrasyonu + rebuild resolve geçişi + IOzellik.id"
```

---

### Task 8: Feature oluşturma → intrinsic yayın

**Files:**
- Modify: `fiko-modelleme/FikoModellemeYonetici.ts`

Feature geçmişe eklendiğinde alan haritasındaki sayısal alanları depoya yayınla. Merkezi kanca: `gecmisKaydet()` (komut tamamlanınca çağrılıyor — bkz satır ~1816) ve `featureNodeBagla()`. En güvenli yer: rebuild'den hemen önce tüm feature'lar için yayın senkronu.

- [ ] **Step 1: Yayın senkron metodu**

Sınıfa ekle:

```typescript
  /** Tüm feature'lar için intrinsic parametreleri depoda güncel tut (yeni feature → yayın, silinen → kaldır). */
  private intrinsikSenkron(): void {
    const k = this.knowledge.depo;
    const canliId = new Set<string>();
    this.ozellikler.forEach((f, i) => {
      const id = this.featIdGaranti(f); canliId.add(id);
      const featAd = (f.node?.name || f.tip).replace(/^◆ /, '') || `${f.tip}.${i + 1}`;
      for (const a of featureAlanlari(f.tip)) {
        const mevcut = (f as any)[a.alan];
        if (mevcut == null) continue;                        // tanımsız alan yayınlanmaz
        k.featAlanYayinla(id, featAd, a.alan, a.etiket, a.tip, mevcut);
      }
    });
    // artık var olmayan feature'ların intrinsic parametrelerini temizle
    for (const p of [...k.parametreler]) if (p.feat && !canliId.has(p.feat.featId)) k.featKaldir(p.feat.featId);
  }
```

- [ ] **Step 2: rebuild başında senkron çağır**

`rebuild()` içinde `this.parametreCozUygula();` ÇAĞRISINDAN ÖNCE ekle:

```typescript
    this.intrinsikSenkron();
```

Sıra önemli: önce yayın (yeni feature'lar depoya girsin), sonra çöz+uygula.

- [ ] **Step 3: Manuel doğrulama (uygulamada)**

Run: `npm start` → uygulamada bir Pad oluştur → ürün ağacında Pad düğümünü genişlet.
Expected: Konsol/inceleme: `modelleme.knowledge.depo.parametreler` içinde `Pad.1\Uzunluk` görünür, değeri pad derinliğine eşit. (UI chip Task 10'da görünür olacak.)

- [ ] **Step 4: Commit**

```bash
git add src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/FikoModellemeYonetici.ts
git commit -m "feat(modelleme): feature oluşunca intrinsic parametre yayını (rebuild senkron)"
```

---

### Task 9: serialize / deserialize genişletme

**Files:**
- Modify: `fiko-modelleme/FikoModellemeYonetici.ts` (`serialize()` ~6882, `deserialize()` ~6911)

- [ ] **Step 1: serialize'a knowledge ekle**

`serialize()` içinde `const ret: any = { ... }` nesnesine, `ozellikler` map'inde her feature'a `id` ekle ve kök nesneye knowledge alanlarını ekle. `ozellikler: this.ozellikler.map(f => ({ tip: f.tip, ekleme: f.ekleme, ...` başına ekle:

```typescript
        id: this.featIdGaranti(f),
```

`ret` nesnesi `};` ile kapanmadan ÖNCE (ozellikler array'inden sonra) ekle:

```typescript
      parametreler: this.knowledge.depo.parametreler.map(p => ({ ...p })),
      gruplar: this.knowledge.depo.gruplar.map(g => ({ ...g })),
      knowledgeMode: this.knowledge.mode,
```

- [ ] **Step 2: deserialize'da knowledge geri yükle**

`deserialize()` içinde feature kurma döngüsünde `const feat: IOzellik = { tip: fo.tip, ...` nesnesine ekle:

```typescript
          id: fo.id,
```

`for (const fo of o.ozellikler) { ... }` döngüsünden SONRA, `this.rebuild();` çağrısından ÖNCE ekle:

```typescript
      // ── Knowledge (parametreler/gruplar) geri yükle ──
      this.knowledge.depo.parametreler = Array.isArray(o.parametreler) ? o.parametreler.map((p: any) => ({ ...p })) : [];
      this.knowledge.depo.gruplar = Array.isArray(o.gruplar) ? o.gruplar.map((g: any) => ({ ...g })) : [];
      this.knowledge.mode = o.knowledgeMode === 'manuel' ? 'manuel' : 'oto';
      this.ozellikler.forEach(f => this.featIdGaranti(f));   // eski belgede id yoksa üret
```

> `rebuild()` zaten `intrinsikSenkron()` + `parametreCozUygula()` çağırdığından, yüklenen parametreler/formüller otomatik çözülüp feature alanlarına yazılır. Eski belge (parametre yok) → boş depo, mevcut davranış birebir korunur.

- [ ] **Step 3: Round-trip manuel doğrulama**

Run: `npm start` → Pad oluştur → `Pad.1\Uzunluk` adını `kapakKalinligi` yap, serbest `boy=100` ekle, `kapakKalinligi = boy/4` formülü ver → modeli kaydet → sayfayı yenile → modeli aç.
Expected: `kapakKalinligi` formülü ve değeri (25) korunur; pad derinliği 25; `boy`'u 200 yapınca pad 50 olur.

- [ ] **Step 4: Commit**

```bash
git add src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/FikoModellemeYonetici.ts
git commit -m "feat(modelleme): serialize/deserialize knowledge (parametre+grup+id) round-trip"
```

---

## FAZ 1C — UI

### Task 10: Ürün ağacında f(x) chip + intrinsic chip doldurma

**Files:**
- Modify: `fiko-modelleme/FikoModellemeYonetici.ts` (intrinsikSenkron içinde node'a `FikoParametreler` doldur)
- Modify: `Fiko3DComponent/agac-item/agac-item.component.html` (f(x) chip bloğu)
- Modify: `Fiko3DComponent/agac-item/agac-item.component.ts` (`paramDegisti` knowledge köprüsü)

Mevcut chip sistemi: `grp.userData.FikoParametreler = [{ isim, turu, degeri, degisti, ayar }]`. Intrinsic parametreleri bu diziye çevirip feature node'una koyacağız.

- [ ] **Step 1: intrinsikSenkron'da node chip'lerini doldur**

Task 8'deki `intrinsikSenkron()` içinde, her feature için yayından sonra node'a chip listesi yaz. `for (const a of featureAlanlari(f.tip)) { ... }` bloğundan sonra (hâlâ `forEach` içinde) ekle:

```typescript
      if (f.node) {
        const ud = (f.node.userData ||= {});
        ud['FikoParametreler'] = featureAlanlari(f.tip)
          .map(a => k.getFeatAlan(id, a.alan)).filter(Boolean)
          .map(p => ({
            isim: p!.ad.split('\\').pop(),
            turu: p!.formul ? 'Formul' : 'Sayı',
            degeri: p!.formul ? p!.formul : p!.deger,
            paramId: p!.id,
            hata: p!.hata,
            ayar: { birim: p!.tip === 'Aci' ? '°' : (p!.tip === 'Uzunluk' ? 'mm' : '') },
            degisti: (v: any) => { this.knowledge.parametreGuncelle(p!.id, v); },
          }));
      }
```

- [ ] **Step 2: agac-item.component.html — f(x) chip bloğu**

`agac-item.component.html` içinde, mevcut `<!-- Renk -->` chip `<label>`'ından SONRA (kapanış `</ng-container>`'dan önce, satır ~119) ekle:

```html
                    <!-- Formül (f(x)): değer salt-okunur + formül metni -->
                    <label class="fiko-param-chip fp-formul" *ngIf="prm.turu=='Formul'"
                           [class.fp-hata]="prm.hata" [title]="prm.hata || prm.degeri"
                           (click)="$event.stopPropagation()">
                        <span class="fp-ad">{{prm.isim}}</span>
                        <span class="fp-fx" (click)="paramFormulDuzenle($event, prm)">ƒ(x)</span>
                        <span class="fp-formul-metin" (click)="paramFormulDuzenle($event, prm)">{{prm.degeri}}</span>
                    </label>
```

- [ ] **Step 3: agac-item.component.ts — formül düzenleme köprüsü**

`agac-item.component.ts` içine, mevcut `paramDegisti` metodunun yanına ekle:

```typescript
  /** f(x) chip tıklandı → formül editörü iste (prompt MVP; Task 11 zengin editör). */
  paramFormulDuzenle($event: Event, prm: any) {
    $event.stopPropagation();
    const yeni = window.prompt(`Formül (${prm.isim}):`, typeof prm.degeri === 'string' ? prm.degeri : '');
    if (yeni == null) return;
    prm.degisti?.({ formul: yeni.trim() || undefined });   // boş → formülü çöz
    this.CD.detectChanges();
    this.FikoYT.degisti.emit();
  }
```

> `paramDegisti` mevcut: `prm.degeri = deger; prm.degisti?.(deger)`. Sayı chip'i ham değer gönderir; `parametreGuncelle` kısa biçimi (formülsüz → deger) bunu işler. Değişiklik gerekmez.

- [ ] **Step 4: f(x) chip stili**

`agac-item.component.scss` içine ekle (mevcut `.fiko-param-chip` yakını):

```scss
.fiko-param-chip.fp-formul { .fp-fx { color: #36cfc9; font-style: italic; font-weight: 700; margin: 0 3px; } .fp-formul-metin { opacity: .85; cursor: pointer; } &.fp-hata { outline: 1px solid #ff4d4f; } }
```

- [ ] **Step 5: Manuel doğrulama**

Run: `npm start` → Pad oluştur → ağaçta genişlet → `Uzunluk` chip'inde sayı değiştir → model güncellenir. f(x) chip'e tıkla → formül gir (`boy/4`) → değer salt-okunur formüle döner, model güncellenir.
Expected: oto modda anında, manuel modda Workbench "Güncelle" sonrası.

- [ ] **Step 6: Commit**

```bash
git add src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/FikoModellemeYonetici.ts "src/service/fikoyt-ui/src/lib/fiko-three-js/Fiko3DComponent/agac-item/agac-item.component.html" "src/service/fikoyt-ui/src/lib/fiko-three-js/Fiko3DComponent/agac-item/agac-item.component.ts" "src/service/fikoyt-ui/src/lib/fiko-three-js/Fiko3DComponent/agac-item/agac-item.component.scss"
git commit -m "feat(agac): intrinsic parametre chip'leri + f(x) formül chip (ürün ağacı)"
```

---

### Task 11: Parametre Workbench paneli

**Files:**
- Create: `fiko-modelleme/fiko-parametre-workbench/fiko-parametre-workbench.component.ts` (template inline)
- Modify: 3D bileşeninde panel mount (mevcut yan panel/toolbar yapısına göre — `Fiko3DComponent`)

Odaklı CAD parametre paneli: grup ağacı + parametre listesi (ad/tip/değer-veya-formül inline), ekle/sil/yeniden-adlandır, oto/manuel toggle + Güncelle butonu. `OnPush` + `signal`.

- [ ] **Step 1: Bileşeni oluştur**

```typescript
// fiko-parametre-workbench/fiko-parametre-workbench.component.ts
import { ChangeDetectionStrategy, Component, Input, signal, computed, ChangeDetectorRef, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { FikoKnowledgeYonetici } from '../knowledge/FikoKnowledgeYonetici';
import { IFikoParametre, ParamTip } from '../knowledge/IFikoKnowledge';

@Component({
  selector: 'fiko-parametre-workbench',
  standalone: true,
  imports: [CommonModule, FormsModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
  <div class="fpw" *ngIf="knowledge">
    <div class="fpw-bar">
      <strong>Parametreler</strong>
      <span class="fpw-mod">
        <button [class.aktif]="knowledge.mode==='oto'" (click)="modSet('oto')">Otomatik</button>
        <button [class.aktif]="knowledge.mode==='manuel'" (click)="modSet('manuel')">Manuel</button>
      </span>
      <button class="fpw-guncelle" *ngIf="knowledge.mode==='manuel'" [class.kirli]="knowledge.kirliVarMi" (click)="guncelle()">Güncelle</button>
      <button class="fpw-ekle" (click)="ekle()">+ Parametre</button>
    </div>
    <table class="fpw-tablo">
      <tr *ngFor="let p of liste(); trackBy: trackById" [class.fpw-hata]="p.hata" [title]="p.hata || ''">
        <td><input class="fpw-ad" [ngModel]="p.ad" (ngModelChange)="adGuncelle(p, $event)" [readonly]="!!p.feat && false" /></td>
        <td>
          <select [ngModel]="p.tip" (ngModelChange)="tipGuncelle(p, $event)">
            <option *ngFor="let t of tipler" [ngValue]="t">{{t}}</option>
          </select>
        </td>
        <td>
          <input class="fpw-deger" [ngModel]="p.formul ?? p.deger"
                 (ngModelChange)="degerGuncelle(p, $event)"
                 [placeholder]="p.feat ? 'intrinsic' : ''" />
          <span class="fpw-fx" *ngIf="p.formul">ƒ</span>
        </td>
        <td><button class="fpw-sil" *ngIf="!p.feat" (click)="sil(p)">✕</button></td>
      </tr>
    </table>
  </div>
  `,
  styles: [`
    .fpw { font-size: 12px; color: var(--py-metin, #ddd); }
    .fpw-bar { display:flex; gap:6px; align-items:center; padding:6px; flex-wrap:wrap; }
    .fpw-mod button { padding:2px 8px; border:1px solid #555; background:transparent; color:inherit; cursor:pointer; }
    .fpw-mod button.aktif { background:#177ddc; border-color:#177ddc; color:#fff; }
    .fpw-guncelle.kirli { background:#fa8c16; color:#fff; animation:fpwpulse 1s infinite; }
    @keyframes fpwpulse { 50% { opacity:.6; } }
    .fpw-tablo { width:100%; border-collapse:collapse; }
    .fpw-tablo td { padding:2px 4px; border-bottom:1px solid #333; }
    .fpw-tablo input, .fpw-tablo select { width:100%; background:transparent; color:inherit; border:1px solid transparent; }
    .fpw-tablo input:focus { border-color:#177ddc; }
    .fpw-hata { outline:1px solid #ff4d4f; }
    .fpw-fx { color:#36cfc9; font-weight:700; }
  `],
})
export class FikoParametreWorkbenchComponent {
  private cd = inject(ChangeDetectorRef);
  @Input() knowledge!: FikoKnowledgeYonetici;
  tipler: ParamTip[] = ['Uzunluk', 'Aci', 'Tamsayi', 'Reel', 'Boolean', 'Metin'];
  private tik = signal(0);
  liste = computed(() => { this.tik(); return this.knowledge?.depo.parametreler ?? []; });
  private yenile() { this.tik.update(v => v + 1); this.cd.markForCheck(); }
  trackById(_: number, p: IFikoParametre) { return p.id; }

  modSet(m: 'oto' | 'manuel') { this.knowledge.mode = m; this.yenile(); }
  guncelle() { this.knowledge.guncelle(); this.yenile(); }
  ekle() {
    let ad = 'parametre1', i = 1; while (this.knowledge.depo.getAd(ad)) ad = 'parametre' + (++i);
    this.knowledge.depo.parametreEkle({ ad, tip: 'Reel', deger: 0 }); this.yenile();
  }
  sil(p: IFikoParametre) { this.knowledge.depo.parametreSil(p.id); this.knowledge.parametreGuncelle?.(p.id, {}); this.yenile(); }
  adGuncelle(p: IFikoParametre, ad: string) { try { this.knowledge.depo.adDegistir(p.id, ad); } catch (e: any) { } this.yenile(); }
  tipGuncelle(p: IFikoParametre, t: ParamTip) { p.tip = t; this.knowledge.parametreGuncelle(p.id, { deger: p.deger }); this.yenile(); }
  degerGuncelle(p: IFikoParametre, v: string) {
    const sayi = parseFloat(v);
    const formulMu = isNaN(sayi) || /[a-zA-Z(]/.test(v);
    this.knowledge.parametreGuncelle(p.id, formulMu ? { formul: v } : { deger: sayi });
    this.yenile();
  }
}
```

- [ ] **Step 2: 3D bileşeninde panel mount**

`Fiko3DComponent` ana template'inde (yan panel/araç çubuğu yapısına uygun bir yere) ekle ve importla:

```typescript
// Fiko3DComponent .component.ts importlarına
import { FikoParametreWorkbenchComponent } from './fiko-modelleme/fiko-parametre-workbench/fiko-parametre-workbench.component';
// @Component imports dizisine: FikoParametreWorkbenchComponent
```

Template'e (modelleme paneli görünür olduğunda):

```html
<fiko-parametre-workbench *ngIf="modellemeAktif" [knowledge]="modelleme?.knowledge"></fiko-parametre-workbench>
```

> `modellemeAktif` / `modelleme` mevcut bileşendeki gerçek alan adlarıyla eşleştirilecek (execution sırasında bileşeni oku). Panel `knowledge` instance'ını alır; değişiklikler `rebuildKopru` üzerinden modele yansır.

- [ ] **Step 3: Manuel doğrulama**

Run: `npm start` → modelleme aç → Workbench panelinde `+ Parametre` ile `boy=100` ekle → bir Pad'in `Uzunluk` chip'ine f(x) `boy/4` ver → Workbench'te `boy`'u 200 yap → oto modda pad anında günceller; manuel modda "Güncelle" sonrası.
Expected: parametre listesi, oto/manuel toggle, formül + intrinsic satırlar çalışır; hata olan formül kırmızı.

- [ ] **Step 4: Commit**

```bash
git add src/service/fikoyt-ui/src/lib/fiko-three-js/IFikoThreeJS/fiko-modelleme/fiko-parametre-workbench/ src/service/fikoyt-ui/src/lib/fiko-three-js/Fiko3DComponent/
git commit -m "feat(workbench): odaklı CAD parametre paneli (liste/tip/formül, oto-manuel)"
```

---

## Bitiş Doğrulaması (Faz 1)

- [ ] **Tüm testler geçiyor**

Run: `npx ng test --watch=false --browsers=ChromeHeadless --include='**/knowledge/*.spec.ts'`
Expected: PASS (Task 2,3,5,6 = 20 spec)

- [ ] **Senaryo testi (manuel, uygulamada)**

1. Workbench'te `boy=100`, `etKalinlik=5`, `delikCap = etKalinlik*2` ekle.
2. Pad oluştur → `Pad.1\Uzunluk` chip f(x) → `boy` ver.
3. Delik aç → `Hole.1\Çap` chip f(x) → `delikCap` ver.
4. Workbench'te `etKalinlik=8` → delik çapı 16 olur, model güncellenir (oto).
5. Manuel moda al → `boy=200`, `etKalinlik=10` → "Güncelle" → tek seferde pad 200, delik 20.
6. Kaydet → yenile → aç → tüm parametreler/formüller/değerler korunur.

Expected: tüm adımlar çalışır; performans (oto modda akıllı rebuild) kabul edilebilir.

---

## Faz 2 / Faz 3 (bu planın dışında — ayrı plan)
- Faz 2: Eskiz ölçü/kısıt bağlama (`FikoKisitSolver`), feature dialoglarına gömülü f(x) editör bileşeni (prompt yerine), grup ağacı sürükle-bırak UI.
- Faz 3: Tasarım tablosu (design table), kontrol kuralları (checks), gelişmiş grup/filtre.
