Files
CASAN/casan-next-plans/CASAN_SLIDE_DECK.html
T
thanhnvandClaude Opus 4.8 fbcef967e5 chore(freeze): snapshot demo state before Plan-07 hardening work
Freeze current submission/demo baseline:
- casan-next-plans/: full task-level plan set (Plan 00 index + 02/04/06/07/08/09/12, QA, slide deck)
- optimize-docs/video-steps/: per-vector scene breakdown (commands/screen-text/script) + start-tmux
- run-all.sh / scorecard.sh / map-live.sh: REAL=1 live-battery wiring
- regenerated evidence + audit/telemetry logs from live REAL=1 run
- submission README + video recording guide updates
- dry-run pipeline logs for 001-okr-web-app

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 22:03:44 +09:00

396 lines
24 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CASAN Harness — FPT · AI-SDLC có kiểm soát</title>
<style>
:root{
--fpt-orange:#F37021; --fpt-blue:#00AEEF; --fpt-green:#7DBB42;
--bg:#070B16; --ink:#eaf0ff; --muted:#93a0c2;
--glass:rgba(255,255,255,.045); --glass-brd:rgba(255,255,255,.10);
--h4:#ff5a5a; --h5:#3ba7ff; --h6:#5fd08a; --h7:#b98bff;
--bad:#ff6b6b; --ok:#5fd08a;
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{
font-family:"Segoe UI",system-ui,-apple-system,"Noto Sans",sans-serif;
color:var(--ink); overflow:hidden; background:var(--bg); position:relative;
}
/* AI-era ambient background */
body::before{content:"";position:fixed;inset:0;z-index:0;
background:
radial-gradient(900px 520px at 8% -8%, rgba(243,112,33,.20), transparent 60%),
radial-gradient(900px 520px at 100% 0%, rgba(0,174,239,.18), transparent 55%),
radial-gradient(700px 500px at 60% 120%, rgba(125,187,66,.12), transparent 60%);}
body::after{content:"";position:fixed;inset:0;z-index:0;opacity:.35;
background-image:radial-gradient(rgba(255,255,255,.05) 1px,transparent 1px);
background-size:26px 26px;}
.deck{position:fixed;inset:0;z-index:1}
.slide{position:absolute;inset:0;display:none;flex-direction:column;
justify-content:center;padding:6vh 7vw;animation:fade .4s ease}
.slide.active{display:flex}
@keyframes fade{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}
.eyebrow{font-family:"Cascadia Code",Consolas,monospace;color:var(--fpt-orange);
font-weight:700;letter-spacing:3px;text-transform:uppercase;
font-size:clamp(11px,1.2vw,15px);margin-bottom:1em;display:flex;align-items:center;gap:.6em}
.eyebrow::before{content:"";width:26px;height:2px;background:var(--fpt-orange)}
h1{font-size:clamp(30px,5.2vw,64px);line-height:1.06;letter-spacing:-1px;font-weight:800}
h2{font-size:clamp(23px,3.6vw,44px);line-height:1.12;font-weight:800;margin-bottom:.4em}
.grad{background:linear-gradient(100deg,var(--fpt-orange),#ffb46b 40%,var(--fpt-blue));
-webkit-background-clip:text;background-clip:text;color:transparent}
.sub{color:var(--muted);font-size:clamp(15px,1.9vw,23px);margin-top:.8em;line-height:1.55}
ul{margin-top:1.1em;list-style:none;display:grid;gap:.7em}
li{font-size:clamp(15px,1.95vw,24px);line-height:1.42;padding-left:1.7em;position:relative}
li::before{content:"›";position:absolute;left:0;color:var(--fpt-orange);font-weight:800}
b,strong{color:#fff}
code{font-family:"Cascadia Code",Consolas,monospace;background:rgba(255,255,255,.06);
padding:.05em .4em;border-radius:6px;font-size:.9em;color:#ffd8b8}
.tag{display:inline-block;font-family:monospace;font-size:.6em;font-weight:800;padding:.2em .7em;
border-radius:999px;vertical-align:middle;letter-spacing:.5px}
.tag.real{background:rgba(125,187,66,.18);color:#b6f08a;border:1px solid #7dbb4266}
.tag.warn{background:rgba(243,112,33,.16);color:#ffc07a;border:1px solid #f3702166}
/* glass card */
.cards{display:grid;gap:1rem;margin-top:1.3em}
.c4{grid-template-columns:repeat(4,1fr)} .c3{grid-template-columns:repeat(3,1fr)}
.c2{grid-template-columns:repeat(2,1fr)}
.card{background:var(--glass);border:1px solid var(--glass-brd);border-radius:18px;
padding:1.1em 1.15em;backdrop-filter:blur(10px);box-shadow:0 10px 40px rgba(0,0,0,.3)}
.card h3{font-size:clamp(14px,1.7vw,21px);margin-bottom:.4em;display:flex;align-items:center;gap:.4em}
.card p{color:var(--muted);font-size:clamp(12px,1.35vw,16px);line-height:1.42}
.card.on4{border-top:3px solid var(--h4)} .card.on5{border-top:3px solid var(--h5)}
.card.on6{border-top:3px solid var(--h6)} .card.on7{border-top:3px solid var(--h7)}
/* before/after */
.ba{display:grid;grid-template-columns:1fr auto 1fr;gap:1.1rem;align-items:stretch;margin-top:1.2em}
.col{border-radius:18px;padding:1.15em 1.25em;backdrop-filter:blur(8px)}
.before{background:rgba(255,90,90,.07);border:1px solid rgba(255,90,90,.28)}
.after{background:rgba(125,187,66,.08);border:1px solid rgba(125,187,66,.32)}
.col .lbl{font-family:monospace;font-weight:800;letter-spacing:1px;font-size:.8em;margin-bottom:.6em}
.before .lbl{color:var(--bad)} .after .lbl{color:var(--ok)}
.col ul{margin-top:.2em;gap:.5em} .col li{font-size:clamp(13px,1.55vw,20px);padding-left:1.4em}
.before li::before{content:"✕";color:var(--bad)} .after li::before{content:"✓";color:var(--ok)}
.mid{display:flex;align-items:center;font-size:clamp(22px,3vw,40px);color:var(--fpt-orange)}
table{width:100%;border-collapse:collapse;margin-top:1.2em;font-size:clamp(13px,1.65vw,21px)}
th,td{text-align:left;padding:.52em .7em;border-bottom:1px solid var(--glass-brd)}
th{color:var(--fpt-blue);font-family:monospace;font-size:.82em;text-transform:uppercase;letter-spacing:1px}
td.big{font-weight:800;color:#fff} td.was{color:var(--muted)}
.flow{display:flex;align-items:center;gap:.6rem;margin-top:1.4em;flex-wrap:wrap}
.node{background:var(--glass);border:1px solid var(--glass-brd);border-radius:14px;
padding:.75em 1.05em;font-weight:700;font-size:clamp(13px,1.55vw,20px);text-align:center;backdrop-filter:blur(8px)}
.node.in{border-color:rgba(0,174,239,.5)} .node.out{border-color:rgba(125,187,66,.5);background:rgba(125,187,66,.08)}
.arrow{color:var(--fpt-orange);font-size:clamp(18px,2.3vw,30px)}
.chips{display:flex;gap:.5rem;margin-top:1.1em;flex-wrap:wrap}
.chip{padding:.5em .9em;border-radius:11px;font-weight:700;font-size:clamp(11px,1.3vw,17px);
border:1px solid var(--glass-brd);background:var(--glass)}
.chip.s{border-color:var(--h4);color:#ffb4b4} .chip.g{border-color:var(--h5);color:#a9d5ff}
.chip.o{border-color:var(--h6);color:#a6f0c0} .chip.r{border-color:var(--h7);color:#dcc0ff}
.chip.y{border-color:var(--fpt-orange);color:#ffc99b}
.term{margin-top:1.2em;background:rgba(3,5,12,.7);border:1px solid var(--glass-brd);border-radius:14px;
padding:1em 1.25em;font-family:"Cascadia Code",Consolas,monospace;
font-size:clamp(12px,1.45vw,19px);line-height:1.75;backdrop-filter:blur(8px)}
.term .dim{color:#5a678c} .term .ok{color:#8ff0b0;font-weight:700} .term .no{color:#ff9a9a;font-weight:700}
.quote{font-size:clamp(21px,3.3vw,44px);line-height:1.22;font-weight:800}
.quote .hl{color:transparent;background:linear-gradient(100deg,var(--fpt-orange),var(--fpt-blue));
-webkit-background-clip:text;background-clip:text}
/* score meters */
.meters{display:grid;grid-template-columns:1fr 1fr;gap:.7em 1.6em;margin-top:1.2em;max-width:1050px}
.meter{display:grid;grid-template-columns:180px 1fr 44px;align-items:center;gap:.8em}
.meter .m-l{font-size:clamp(12px,1.4vw,18px);color:var(--muted)}
.track{height:10px;border-radius:999px;background:rgba(255,255,255,.07);overflow:hidden}
.fill{height:100%;border-radius:999px;background:linear-gradient(90deg,var(--fpt-orange),var(--fpt-blue))}
.meter .m-v{font-weight:800;text-align:right;font-family:monospace}
/* chrome */
.bar{position:fixed;top:0;left:0;height:4px;z-index:9;transition:width .35s;
background:linear-gradient(90deg,var(--fpt-orange),var(--fpt-blue),var(--fpt-green))}
.brand{position:fixed;top:20px;right:26px;z-index:9;display:flex;align-items:center;gap:10px}
.brand .fpt{font-weight:900;font-size:20px;letter-spacing:1px;
background:linear-gradient(100deg,var(--fpt-orange),var(--fpt-blue),var(--fpt-green));
-webkit-background-clip:text;background-clip:text;color:transparent}
.brand .ai{font-family:monospace;font-size:11px;color:var(--muted);border:1px solid var(--glass-brd);
padding:2px 8px;border-radius:999px}
.foot{position:fixed;bottom:16px;left:0;right:0;display:flex;justify-content:space-between;
align-items:center;padding:0 26px;color:var(--muted);font-size:13px;z-index:9;font-family:monospace}
.dots{display:flex;gap:6px} .dot{width:8px;height:8px;border-radius:50%;background:#2a3358;cursor:pointer;transition:.2s}
.dot.on{background:var(--fpt-orange);transform:scale(1.3)}
.hint{position:fixed;bottom:14px;left:50%;transform:translateX(-50%);color:#48557d;font-size:11px;z-index:9;font-family:monospace}
.kbd{border:1px solid var(--glass-brd);border-radius:6px;padding:1px 7px}
</style>
</head>
<body>
<div class="bar" id="bar"></div>
<div class="brand"><span class="fpt">FPT</span><span class="ai">AI-SDLC · CASAN</span></div>
<div class="deck" id="deck">
<!-- 1 TITLE -->
<section class="slide active">
<div class="eyebrow">FPT · CASAN Harness · Thời đại AI</div>
<h1>Cải thiện <span class="grad">H4 · H5 · H6</span><br/>cho AI-SDLC <span class="grad">có kiểm soát</span></h1>
<p class="sub">Chúng tôi không hỏi "AI viết code nhanh cỡ nào" — mà hỏi:
<b>khi AI làm bậy, ai chặn? và làm sao chứng minh đã chặn?</b></p>
<p class="sub">Chạy có bằng chứng · Chặn thật · Trung thực về giới hạn · Hướng đóng gói production.</p>
</section>
<!-- 2 GOAL / 3 AXES -->
<section class="slide">
<div class="eyebrow">Mục tiêu trình bày</div>
<h2>Ba trục chúng tôi chứng minh</h2>
<div class="cards c3">
<div class="card on4"><h3>🔴 1 · Cải thiện H4·H5·H6</h3><p>Before → After cho từng harness, kèm <b>bằng chứng đo thật</b> (exit-code, số test).</p></div>
<div class="card on5"><h3>🧠 2 · Hiểu hệ thống sâu</h3><p>Truy đến <b>gốc lỗi</b> (fail-open), threat-model đường lọt, nguyên tắc "harness thấp nhất quyết định trần".</p></div>
<div class="card on6"><h3>📦 3 · Hướng production</h3><p>Thang điểm sẵn sàng, <b>đóng gói package</b> tái dùng, lộ trình hardening có test.</p></div>
</div>
<p class="sub">Mọi con số gắn nhãn <span class="tag real">ĐO THẬT</span> — giám khảo chạy lại ra đúng.</p>
</section>
<!-- 3 DEPTH: root cause -->
<section class="slide">
<div class="eyebrow">Hiểu sâu · Truy gốc lỗi</div>
<h2>Điểm mù chí mạng: <span style="color:var(--h4)">fail-open</span></h2>
<ul>
<li>Bản trước dùng <code>grep "[:space:]"</code> — cú pháp <b>sai</b> trên grep hiện đại → thoát <b style="color:var(--bad)">rc=4</b>.</li>
<li>Hệ quả: bộ lọc "tưởng đang chặn" nhưng thực chất <b style="color:var(--bad)">chặn = 0</b> — lọt cả injection kinh điển.</li>
<li>Bài học: <b>"có prompt-filter.yaml" ≠ "chặn được thật"</b>. Chỉ tính điểm khi chặn được tấn công thật.</li>
</ul>
<p class="sub">Phát hiện được lỗi fail-open này = hiểu hệ thống ở tầng thực thi, không dừng ở tài liệu.</p>
</section>
<!-- 4 OVERVIEW -->
<section class="slide">
<div class="eyebrow">Kiến trúc CASAN</div>
<h2>Mọi lời gọi agent đi xuyên <span class="grad">7 harness</span></h2>
<div class="flow">
<div class="node in">📥 INPUT</div><div class="arrow">→</div>
<div class="node">🧠 Boss · 13 bước AI-SDLC</div><div class="arrow">→</div>
<div class="node out">📤 code + BẰNG CHỨNG</div>
</div>
<div class="chips">
<span class="chip y">H1 Context</span><span class="chip y">H2 Tool</span><span class="chip y">H3 Eval</span>
<span class="chip s">H4 Security</span><span class="chip g">H5 Governance</span>
<span class="chip o">H6 AgentOps</span><span class="chip r">H7 Orchestration</span>
</div>
<p class="sub">Gate trả <b>APPROVE / REJECT / BLOCK</b> — kiểm soát bọc mọi bước, không đứng bên lề.</p>
</section>
<!-- 5 H4 -->
<section class="slide">
<div class="eyebrow">Trục 1 · 🔴 H4 Security</div>
<h2>Từ fail-open → <span class="grad">chặn thật đa lớp</span></h2>
<div class="ba">
<div class="col before"><div class="lbl">BEFORE</div><ul>
<li>grep sai → fail-open rc=4, chặn = 0</li>
<li>chỉ regex tiếng Anh</li>
<li>mù với câu diễn đạt mới</li></ul></div>
<div class="mid">→</div>
<div class="col after"><div class="lbl">AFTER</div><ul>
<li>BLOCK <b>rc=2</b> + normalize leetspeak</li>
<li>+ <b>semantic model-as-judge</b></li>
<li>artifact-scan (indirect) · secret/PII block</li></ul></div>
</div>
<p class="sub"><span class="tag real">ĐO THẬT</span> Adversarial <b>43/43 PASS</b> · injection paraphrase → <code>rc=2</code>.</p>
</section>
<!-- 6 H5 -->
<section class="slide">
<div class="eyebrow">Trục 1 · 🔵 H5 Governance</div>
<h2>Từ log sửa được → <span class="grad">audit bất biến, ký số</span></h2>
<div class="ba">
<div class="col before"><div class="lbl">BEFORE</div><ul>
<li>log thường, sửa được</li>
<li>không chống giả mạo</li>
<li>không truy vết ký số</li></ul></div>
<div class="mid">→</div>
<div class="col after"><div class="lbl">AFTER</div><ul>
<li><b>hash-chain + ký RSA</b> HEAD</li>
<li>sửa 1 ký tự → <b>AUDIT_HASH_MISMATCH</b></li>
<li>secrets-scan · quét no-bypass</li></ul></div>
</div>
<p class="sub"><span class="tag real">ĐO THẬT</span> tamper 1 ký tự → <code>AUDIT_HASH_MISMATCH line=1</code> · chain hợp lệ → <code>AUDIT_CHAIN_VALID</code>.</p>
</section>
<!-- 7 H6 -->
<section class="slide">
<div class="eyebrow">Trục 1 · 🟢 H6 AgentOps</div>
<h2>Từ mù chi phí → <span class="grad">giám sát & chặn spike</span></h2>
<div class="ba">
<div class="col before"><div class="lbl">BEFORE</div><ul>
<li>không đo chi phí</li>
<li>không telemetry token</li>
<li>không phát hiện bất thường</li></ul></div>
<div class="mid">→</div>
<div class="col after"><div class="lbl">AFTER</div><ul>
<li><b>cost-spike 3× → exit=2</b></li>
<li>telemetry token thật · drift-detect</li>
<li>circuit-breaker chặn đốt tiền</li></ul></div>
</div>
<p class="sub"><span class="tag real">ĐO THẬT</span> spike → <code>COST_SPIKE_DETECTED exit=2</code> · bình thường → <code>exit=0</code>.</p>
</section>
<!-- 8 EVIDENCE -->
<section class="slide">
<div class="eyebrow">Bằng chứng tổng hợp <span class="tag real">ĐO THẬT</span></div>
<h2>Kiểm chứng ngay trên máy — không nói suông</h2>
<div class="term">
<div><span class="dim">$</span> run-casan4-harness-tests.sh → <span class="ok">35/35 PASS</span></div>
<div><span class="dim">$</span> adversarial-harness-tests.sh → <span class="ok">43/43 PASS</span></div>
<div><span class="dim">$</span> security-check (injection) → <span class="no">BLOCKED rc=2</span></div>
<div><span class="dim">$</span> tamper audit → <span class="no">AUDIT_HASH_MISMATCH line=1</span></div>
<div><span class="dim">$</span> cost 3× → <span class="no">COST_SPIKE_DETECTED exit=2</span></div>
</div>
<p class="sub">👉 Chiếu terminal thật 1–2 dòng này khi trình bày để tạo sức nặng.</p>
</section>
<!-- 9 DEPTH: threat model + principle -->
<section class="slide">
<div class="eyebrow">Trục 2 · Hiểu sâu</div>
<h2>Chúng tôi biết <span class="grad">còn lọt ở đâu</span></h2>
<div class="cards c3">
<div class="card on4"><h3>🔴 H4</h3><p>Semantic SKIP khi vắng model · injection đa ngôn ngữ · homoglyph/encoding.</p></div>
<div class="card on5"><h3>🔵 H5</h3><p>Telemetry chưa vào chuỗi ký · mutation ngoài wrapper · quản lý khóa.</p></div>
<div class="card on6"><h3>🟢 H6</h3><p>Slow-boil (median trôi) · lạm dụng dưới ngưỡng · cold-start.</p></div>
</div>
<p class="sub">Chuẩn tham chiếu: OWASP LLM/Agentic · CSA MAESTRO · MITRE ATLAS.
Nguyên tắc: <b>"harness thấp nhất quyết định trần"</b> — cái yếu nhất quyết định cả pipeline.</p>
</section>
<!-- 10 MODEL -->
<section class="slide">
<div class="eyebrow">Triết lý model · local-first</div>
<h2 class="quote">"Model sinh <span class="hl">bản nháp</span>; harness quyết định bản nháp có được <span class="hl">tin</span> hay không."</h2>
<ul>
<li>Local (Ollama) đủ cho harness — <b>không API key, offline, dữ liệu không rời máy</b> (Sovereign AI).</li>
<li>Đổi model <b>không đổi</b> mức an toàn — harness giữ đảm bảo, bất kể model.</li>
<li>H6 cost-spike chặn đốt token → điểm cộng là <b>kiểm soát chi phí</b>, không phải "model xịn nhất".</li>
</ul>
</section>
<!-- 11 PRODUCTION READINESS -->
<section class="slide">
<div class="eyebrow">Trục 3 · Hướng production</div>
<h2>Thang sẵn sàng: <span class="grad">~3.0/5</span> → mục tiêu ≥ 4.0</h2>
<div class="meters">
<div class="meter"><div class="m-l">Phủ phát hiện</div><div class="track"><div class="fill" style="width:60%"></div></div><div class="m-v">3</div></div>
<div class="meter"><div class="m-l">Fail-safe</div><div class="track"><div class="fill" style="width:80%"></div></div><div class="m-v">4</div></div>
<div class="meter"><div class="m-l">Chống giả mạo</div><div class="track"><div class="fill" style="width:60%"></div></div><div class="m-v">3</div></div>
<div class="meter"><div class="m-l">Kiểm soát chi phí</div><div class="track"><div class="fill" style="width:60%"></div></div><div class="m-v">3</div></div>
<div class="meter"><div class="m-l">Đa domain / i18n</div><div class="track"><div class="fill" style="width:40%"></div></div><div class="m-v">2</div></div>
<div class="meter"><div class="m-l">Phủ kiểm thử</div><div class="track"><div class="fill" style="width:80%"></div></div><div class="m-v">4</div></div>
</div>
<p class="sub"><span class="tag warn">TRUNG THỰC</span> Demo ✅ đủ mạnh · Track A → <b>~3.8–4.0</b> (production nội bộ) · <b>Track B+C</b> → production nghiêm túc. Mỗi vá kèm test đối kháng.</p>
</section>
<!-- 11b TRACK C-1 -->
<section class="slide">
<div class="eyebrow">Trục 3 · Track C-MVP · Kín đòn phản biện</div>
<h2>Kiểm soát <span class="grad">ngoài</span> H4·H5·H6 — nhóm ưu tiên</h2>
<p class="sub">"Scan prompt tốt — nhưng agent vẫn có thể <b>thêm dependency độc</b> / <b>ghi file nhạy cảm</b>?" → đây là <b>minimum bar</b> trước khi cho agent ghi code trong môi trường production-like.</p>
<div class="cards c4">
<div class="card on4"><h3>🔐 Tool authorization</h3><p>Gate cả <b>hành động</b> (ghi file/network/lệnh nguy hiểm), không chỉ tên tool.</p></div>
<div class="card on6"><h3>📦 Supply-chain</h3><p>Scan dependency mới · chặn typo-squat/postinstall · SBOM + CVE.</p></div>
<div class="card on4"><h3>🕵️ Data exfiltration</h3><p>Chặn secret/PII rò qua cloud model · log · artifact.</p></div>
<div class="card on7"><h3>🧪 Runtime sandbox</h3><p>Cô lập code/test sinh ra: file/network/CPU/mem/timeout.</p></div>
</div>
<p class="sub">Outcome chuẩn hoá: <code>ALLOW · WARN · REQUIRE_APPROVAL · BLOCK</code> (thêm dependency → duyệt; ghi <code>.env</code> → chặn).</p>
</section>
<!-- 11c TRACK C-2 -->
<section class="slide">
<div class="eyebrow">Trục 3 · Track C · Governance & Ops</div>
<h2>Quản trị & vận hành <span class="grad">mức enterprise</span></h2>
<div class="cards c4">
<div class="card on5"><h3>📝 Policy approval</h3><p>Versioning + reviewer bắt buộc · audit actor · rollback policy.</p></div>
<div class="card on5"><h3>🗄️ External audit</h3><p>Append-only (WORM) ngoài runtime · alert khi chain đứt.</p></div>
<div class="card on6"><h3>🚨 Incident response</h3><p>Severity CRIT→MED · owner · kill-switch theo project/model.</p></div>
<div class="card"><h3>✓ Benign / FP</h3><p>≥30 mẫu/ngôn ngữ · FP ≤ 3% · adversarial block ≥ 95%.</p></div>
</div>
<p class="sub">Reviewer role: Security · Tech lead · Ops owner · Project owner — mỗi loại policy có người duyệt riêng.</p>
</section>
<!-- 12 PACKAGING -->
<section class="slide">
<div class="eyebrow">Trục 3 · Đóng gói & tái sử dụng</div>
<h2>Giá trị ở <span class="grad">harness tái dùng</span>, không ở app OKR</h2>
<ul>
<li>Đóng gói <b>package có version</b> — <code>fpt-casan-sdd-harness</code>.</li>
<li>Registry + <code>verify-harness-reuse</code> → nhiều dự án dùng chung.</li>
<li>Áp dự án mới = cắm golden/corpus/input + đăng ký, <b>không sửa gate</b>.</li>
<li>App OKR chỉ là <b>testbed cố định</b> để so sánh công bằng.</li>
</ul>
</section>
<!-- 13 ROADMAP -->
<section class="slide">
<div class="eyebrow">Lộ trình · kế hoạch task-level</div>
<h2>Tư duy sản phẩm, không chỉ demo</h2>
<div class="cards c4">
<div class="card"><h3>01</h3><p>Tái cấu trúc thư mục</p></div>
<div class="card"><h3>02</h3><p>Nối LLM sinh source</p></div>
<div class="card"><h3>03</h3><p>Patch cloud (bỏ stub)</p></div>
<div class="card"><h3>04</h3><p>Khép vòng tự cải tiến</p></div>
<div class="card"><h3>05</h3><p>CI/CD + phát hành</p></div>
<div class="card"><h3>06</h3><p>Onboard dự án 2</p></div>
<div class="card on4"><h3>07</h3><p>Hardening H4·H5·H6</p></div>
<div class="card"><h3>✓</h3><p>Verify từng task</p></div>
</div>
</section>
<!-- 14 CLOSE -->
<section class="slide">
<div class="eyebrow">Chốt</div>
<h1 class="quote">Không phải AI <span class="hl">xịn nhất</span> —<br/>mà AI <span class="hl">có kiểm soát, có bằng chứng, tái dùng được</span>.</h1>
<ul>
<li>H4·H5·H6 cải thiện có <b>before/after + exit-code đo thật</b>.</li>
<li>Hiểu hệ thống tới gốc lỗi + threat-model đường lọt.</li>
<li>Hướng production: đóng gói package + lộ trình hardening có test.</li>
</ul>
</section>
</div>
<div class="foot">
<div id="who">FPT · CASAN Harness</div>
<div class="dots" id="dots"></div>
<div id="num">1 / 16</div>
</div>
<div class="hint"><span class="kbd">←</span> <span class="kbd">→</span> / <span class="kbd">Space</span> chuyển slide · <span class="kbd">F</span> toàn màn hình</div>
<script>
const slides=[...document.querySelectorAll('.slide')];
const dotsBox=document.getElementById('dots');
const bar=document.getElementById('bar'),num=document.getElementById('num');
let i=0;
slides.forEach((_,k)=>{const d=document.createElement('div');d.className='dot'+(k===0?' on':'');
d.onclick=()=>go(k);dotsBox.appendChild(d);});
const dots=[...document.querySelectorAll('.dot')];
function go(n){
i=Math.max(0,Math.min(slides.length-1,n));
slides.forEach((s,k)=>s.classList.toggle('active',k===i));
dots.forEach((d,k)=>d.classList.toggle('on',k===i));
bar.style.width=((i+1)/slides.length*100)+'%';
num.textContent=(i+1)+' / '+slides.length;
}
document.addEventListener('keydown',e=>{
if(['ArrowRight','ArrowDown',' ','PageDown'].includes(e.key)){e.preventDefault();go(i+1);}
else if(['ArrowLeft','ArrowUp','PageUp'].includes(e.key)){e.preventDefault();go(i-1);}
else if(e.key==='Home')go(0); else if(e.key==='End')go(slides.length-1);
else if(e.key.toLowerCase()==='f'){if(!document.fullscreenElement)document.documentElement.requestFullscreen();else document.exitFullscreen();}
});
let sx=0;
document.addEventListener('touchstart',e=>sx=e.touches[0].clientX,{passive:true});
document.addEventListener('touchend',e=>{const dx=e.changedTouches[0].clientX-sx;
if(Math.abs(dx)>50)go(i+(dx<0?1:-1));},{passive:true});
go(0);
</script>
</body>
</html>