feat(v2): 9–10 Ekim çalışma ağacı — deneyim dili 1. dalga, PWA manifest, revizyon niyet modeli, listem/tadımlık bileşen denetimi
9–10 Ekim oturumlarının commit'lenmemiş işi tek parça alındı (raporlar: docs/ekip-raporlari/2026-10-09, 2026-10-10): - Landing deneyim dili 1. dalga: sayfa geçişi (ViewTransition), piksel morf/şerit, sorun sahneleri, adım akışı, sayan sayı, mobil menü, adım göstergesi, logoya tepki; DESIGN.md §11 ve AGENTS.md kuralları, tasarım denetimine yeni kurallar - Tema: tema ikonu, D kısayolu, tema geçişi - PWA: manifest.ts ve ikonlar - Danışman: revizyon niyet modeli (OPENROUTER_NIYET_MODEL), sohbet geçmişi, soru API'si - /listem ve /sonuc tadımlığı bileşen denetimi düzeltmeleri - eslint: .venv-tts/ yok sayılır tsc, eslint ve tasarım denetimi sıfır hata. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -32,7 +32,7 @@ import Database from "better-sqlite3";
|
||||
import { argOku, evalAppDbHazirla, gitDurumu, kisalt, olcerekKos, sonucYaz, tablo, TOPLAM } from "./ortak";
|
||||
import type { RaporParams, RaporSonuc, RevizyonKurali } from "@/features/rapor/types/rapor";
|
||||
import type { KayitliMesaj } from "@/lib/ai/sohbet-gecmisi";
|
||||
import type { Anket } from "@/lib/ai/sohbet-meta";
|
||||
import type { Anket, AnketCevaplari } from "@/lib/ai/sohbet-meta";
|
||||
import type { KoddanIslem } from "@/lib/ai/sohbet-akisi";
|
||||
|
||||
// ── QA kaydından alınan geçmiş parçaları (metinler birebir) ──
|
||||
@@ -114,6 +114,17 @@ type Senaryo = {
|
||||
kural?: Partial<RevizyonKurali>;
|
||||
/** Form cevabı senaryosu: cevaplanan form (route sonAnket'i verir). */
|
||||
oncekiForm?: Anket;
|
||||
/** Form cevabının yapısal hâli (route anketCevabiDogrula'dan); varsayılan formda modelsiz yol. */
|
||||
anketCevabi?: AnketCevaplari;
|
||||
};
|
||||
/** Varsayılan form (revizyon-niyeti.ts varsayilanForm ile aynı adlar/değerler; kimlik sabit). */
|
||||
const VARSAYILAN_ANKET: Anket = {
|
||||
id: "7c1f3d2e-0000-4000-8000-varsayilan01",
|
||||
baslik: "Listeni nasıl değiştirelim?",
|
||||
sorular: [
|
||||
{ ad: "degisiklik", soru: "Ne değişsin?", coklu: true, serbest: "Başka bir şey…", secenekler: [{ deger: "il-ekle", etiket: "Başka bir il de olsun" }, { deger: "devlet", etiket: "Sadece devlet üniversiteleri" }, { deger: "vakif-olmasin", etiket: "Vakıf üniversiteleri çıksın" }, { deger: "guvenli", etiket: "Daha güvenli olsun (riski azaltalım)" }, { deger: "iddiali", etiket: "Daha iddialı olsun (hayal ağırlıklı)" }] },
|
||||
{ ad: "adet", soru: "Kaç tercih etkilensin?", zorunlu: false, secenekler: [{ deger: "5", etiket: "İlk 5" }, { deger: "10", etiket: "İlk 10" }, { deger: "24", etiket: "Listenin tamamı" }] },
|
||||
],
|
||||
};
|
||||
const SON_KURAL: RaporParams["revizyon"] = { ilkTercihler: { adet: 5, iller: ["İZMİR"] }, iller: { ekle: ["ANKARA"] }, not: "İlk 5 tercih İzmir olsun." };
|
||||
const SENARYOLAR: Senaryo[] = [
|
||||
@@ -127,6 +138,15 @@ const SENARYOLAR: Senaryo[] = [
|
||||
{ id: "r08", aciklama: "2 geçmiş, il ekleme", gecmis: G2, soru: "Kocaeli de olsun", beklenen: "revizyon", revizyon: SON_KURAL, kural: { iller: { ekle: ["KOCAELİ"] } } },
|
||||
{ id: "r09", aciklama: "2 geçmiş, belirsiz istek → form", gecmis: G2, soru: "listemi biraz değiştirmek istiyorum", beklenen: "soru", revizyon: SON_KURAL },
|
||||
{ id: "r10", aciklama: "2 geçmiş, revizyon değil (kontrol)", gecmis: G2, soru: "listemdeki 1. programın kontenjanı kaç, geçen yıl kaç kişi yerleşmiş?", beklenen: "hicbiri", revizyon: SON_KURAL },
|
||||
// 10 Eki 2026 (Bilal: paketli hesapta "liste oluşturma" isteği koşmadı; QA B1 "listemi değiştir" → diger):
|
||||
// genel istek form açmalı, "diger"e düşmemeli; "yeniden oluştur" regex kapısından geçmeli.
|
||||
{ id: "r11", aciklama: "genel istek, ne değişeceği yok → form (QA B1)", gecmis: G2, soru: "listemi değiştir", beklenen: "soru", revizyon: SON_KURAL },
|
||||
{ id: "r12", aciklama: "listeyi baştan isteme → form", gecmis: [], soru: "listemi yeniden oluştur", beklenen: "soru" },
|
||||
{ id: "r13", aciklama: "tercih güncelleme dili → form", gecmis: G2, soru: "tercihlerimi güncellemek istiyorum", beklenen: "soru", revizyon: SON_KURAL },
|
||||
{ id: "r14", aciklama: "net çoklu kural tek cümlede", gecmis: [], soru: "yeni liste yap: ilk 10 tercihim İstanbul'dan olsun, vakıf olmasın", beklenen: "revizyon", kural: { ilkTercihler: { adet: 10, iller: ["İSTANBUL"] }, universiteTipi: "devlet" } },
|
||||
// Varsayılan formun cevabı modelsiz kurala çevrilir (niyet sn ≈ 0 beklenir)
|
||||
{ id: "r15", aciklama: "varsayılan form cevabı → modelsiz kural (il serbest metin + devlet, ilk 5)", gecmis: [...G2, U("listemi değiştir"), A("Birkaç kısa soru hazırladım; cevaplarsan listeni ona göre yeniden kurarım.", { anket: VARSAYILAN_ANKET })], soru: "Form cevaplarım:\n- Ne değişsin? → Başka bir il de olsun, Sadece devlet üniversiteleri, Kocaeli\n- Kaç tercih etkilensin? → İlk 5", beklenen: "revizyon", revizyon: SON_KURAL, oncekiForm: VARSAYILAN_ANKET, anketCevabi: { degisiklik: ["il-ekle", "devlet", "Kocaeli"], adet: "5" }, kural: { ilkTercihler: { adet: 5, iller: ["KOCAELİ"], universiteTipi: "devlet" }, iller: { ekle: ["KOCAELİ"] } } },
|
||||
{ id: "r16", aciklama: "varsayılan form cevabı → modelsiz kural (güvenli, tamamı)", gecmis: [...G2, U("listemi değiştir"), A("Birkaç kısa soru hazırladım.", { anket: VARSAYILAN_ANKET })], soru: "Form cevaplarım:\n- Ne değişsin? → Daha güvenli olsun (riski azaltalım)\n- Kaç tercih etkilensin? → Listenin tamamı", beklenen: "revizyon", revizyon: SON_KURAL, oncekiForm: VARSAYILAN_ANKET, anketCevabi: { degisiklik: ["guvenli"], adet: "24" }, kural: { iskelet: "guvenli-agirlikli" } },
|
||||
];
|
||||
|
||||
/** `beklenen` kural parçası `gercek`in alt kümesi mi (diziler sırasız, büyük-küçük harf duyarsız). */
|
||||
@@ -214,7 +234,7 @@ async function main() {
|
||||
let ozet: ReturnType<typeof sahteOzet> | null = null;
|
||||
const t0 = Date.now();
|
||||
const niyetOlcum = regex
|
||||
? await olcerekKos(() => revizyonNiyetiCikar({ mesaj: s.soru, params, oncekiForm: s.oncekiForm ?? null, gozlem: (g) => (niyetSaglayici = g.saglayici) }))
|
||||
? await olcerekKos(() => revizyonNiyetiCikar({ mesaj: s.soru, params, oncekiForm: s.oncekiForm ?? null, anketCevabi: s.anketCevabi ?? null, gozlem: (g) => (niyetSaglayici = g.saglayici) }))
|
||||
: null;
|
||||
const niyetMs = Date.now() - t0;
|
||||
if (niyetOlcum) {
|
||||
|
||||
@@ -281,6 +281,35 @@ const KURALLAR: Kural[] = [
|
||||
mesaj: "Hata/bildirim metninde 'Hay aksi/Eyvah/Oops' ya da 'buraya tıkla' yok; ne oldu + ne yapılacak (DESIGN.md §10.4)",
|
||||
bul: regexKurali(/Hay aksi|Eyvah|\bOops|\bUups|[Bb]uraya tıkla|[Tt]ıklayın(?:ız)?\b/),
|
||||
},
|
||||
{
|
||||
ad: "yabanci-hareket-paketi",
|
||||
seviye: "hata",
|
||||
mesaj:
|
||||
"Hareket yalnız motion ile; gsap/framer-motion isteyen dış bileşen motion ile yeniden yazılır (DESIGN.md §11.3)",
|
||||
uiDahil: true,
|
||||
bul: regexKurali(/from\s+["'](?:gsap|@gsap\/[\w-]+|framer-motion|lenis|matter-js|ogl|three)(?:\/[\w./-]+)?["']/),
|
||||
},
|
||||
{
|
||||
ad: "elle-cta",
|
||||
seviye: "uyari",
|
||||
mesaj: 'Turuncu CTA elle yazılmaz; <Button variant="cta"> (DESIGN.md §4.1)',
|
||||
bul: (icerik) => {
|
||||
const sonuc: Array<{ index: number; parca: string }> = [];
|
||||
for (const m of icerik.matchAll(/<Button\b[^>]*?>/g)) {
|
||||
const t = /\bbg-orange-500\b/.exec(m[0]);
|
||||
if (t) sonuc.push({ index: (m.index ?? 0) + t.index, parca: t[0] });
|
||||
}
|
||||
return sonuc;
|
||||
},
|
||||
},
|
||||
{
|
||||
ad: "elle-yay",
|
||||
seviye: "uyari",
|
||||
mesaj:
|
||||
"Yay/eğri kullanım yerinde uydurulmaz; src/lib/hareket.ts'teki YAY / EGRI adları ya da --egri-* değişkenleri (DESIGN.md §6)",
|
||||
uiDahil: true,
|
||||
bul: regexKurali(/\bstiffness\s*:|cubic-bezier\(/),
|
||||
},
|
||||
{
|
||||
ad: "emoji-ikon",
|
||||
seviye: "uyari",
|
||||
|
||||
Reference in New Issue
Block a user