Files
CASAN/docs/output/casan/WHY-81-TO-90-DEEP-DIVE.md
T
thanhnvandClaude Opus 4.8 36a4812ef3 refactor(structure): promote app to repo root + remove redundant workspace cruft
Standard production layout: the OKR app (was nested under AINative_OKR_CASAN5/) is now
the repository root. No more wrapper directory.

- Promote AINative_OKR_CASAN5/* -> repo root (backend/ frontend/ packages/ apps/
  .specify/ docs/ infra/ nginx/ scripts/ + configs). Merge tool dirs: .gitea (kept the
  active deploy ci.yml, added harness-ci.yml + runbooks), .claude (agents/commands +
  launch.json), .github moved up.
- Remove redundant: 00_SUBMISSION_PACKAGE, scattered root notes (FPT_CASAN_Full.md,
  tu-tuong-casan.md, casan-tu-sinh..., casan_harness_assessment.md, source-review...,
  README_CASAN5_REFINED.md), casan-next-plans/ and optimize-docs/ (competition/planning
  artifacts — roadmap + design history preserved in git log / commit messages).
- Update all references to the old layout:
  - .gitea/workflows/{ci,harness-ci}.yml, .github/workflows/{ci,deploy}.yml:
    working-directory .; drop AINative_OKR_CASAN5/ prefix; .specify/{tests,scripts}
    -> packages/casan-harness/... (.specify/logs state kept)
  - .claude/launch.json, .gitea/*-runbook.md: path prefixes
  - CLAUDE.md, README.md: docs/input -> apps/okr/domain/input
  - policy-bundle.yaml: 8 policy paths -> packages/casan-harness/...; manifest re-signed
- secrets-scan.sh: fixture excludes -> new package/domain paths.

Full gate from the new root: PASS=64 FAIL=0 SKIP=3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 13:26:36 +09:00

15 KiB
Raw Blame History

Vì sao ~81 → ~84, và vì sao chưa thể 90 (giải thích sâu)

Tài liệu này không liệt kê đầu việc — nó giải thích logic đằng sau từng bước: tại sao phải làm theo thứ tự đó, tại sao mỗi control có hình dạng như vậy, và tại sao 4 mục cuối bắt buộc cần hạ tầng thật mới chứng minh được trung thực. Mục tiêu: để bạn hiểu nguyên lý, không phải học thuộc checklist.


0. Nguyên tắc nền: "Điểm = thứ chứng minh được", không phải "thứ khai báo"

Đây là gốc rễ của mọi quyết định bên dưới. Một harness được chấm điểm theo năng lực kiểm chứng được bằng tấn công, không theo số lượng file YAML mô tả ý định.

Vì sao? Vì chính CASAN nói giá trị lớn nhất của Harness Engineering là thu hẹp khoảng cách từ demo đến vận hành thật. Một bản demo gây ấn tượng bằng vài file cấu hình; một hệ production cần độ tin cậy chứng minh được. Do đó:

  • Một control chỉ được tính điểm nếu nó chặn được một cuộc tấn công thật, không phải nếu một happy-path test xanh.
  • Ví dụ ngược (chính là lý do bản GHCP gốc bị thổi phồng): file prompt-filter.yaml khai báo "block jailbreak" → nhưng khi cho private key vào input, nó leak vì grep lỗi cú pháp. "Có file" ≠ "có năng lực".

→ Hệ quả trực tiếp: tôi không thể chấm điểm cho thứ tôi không chứng minh được bằng kết quả thật. Đây là lý do 4 mục cuối bị "kẹt trần" — không phải vì lười, mà vì nguyên tắc.


1. Vì sao phải chia 3 pha, và theo đúng thứ tự đó

Không phải tuỳ tiện. Thứ tự đến từ quan hệ phụ thuộc: harness nào kiểm chứng được mà không cần sản phẩm thật thì làm trước; harness nào bắt buộc cần sản phẩm + lần chạy thật thì phải đợi.

Pha 1 — Cứng hoá control-plane (H2/H4/H5/H6) trước

Vì sao trước? Vì 4 harness này là lớp bao quanh (security, governance, tool, ops). Chúng kiểm chứng được bằng cách bơm input đối kháng vào script và xem nó chặn hay không — không cần app OKR tồn tại. Làm được ngay, chắc chắn, rẻ.

Đây cũng là lý do triết học: theo Martin Fowler (CASAN trích), harness gồm 2 loại cơ chế — guidance trước khi AI hành động và sensor phản hồi sau khi hành động. H4/H5 là guidance + chặn; H6 là sensor. Cả hai kiểm chứng được độc lập với nội dung sản phẩm.

Pha 2 — App thật + chạy pipeline thật (H1/H3/H7) sau

Vì sao phải đợi? Vì 3 harness này không thể vượt 80 một cách trung thực nếu không có sản phẩm và một lần chạy thật, do bản chất của chúng:

  • H3 (Evaluation) đo "kiểm định đầu ra". Không có app → không có output để kiểm định → không có gì để gate REJECTED → không có golden/regression. Mọi "verdict APPROVED" lúc đó chỉ là chuỗi ký tự hardcode (đúng là bản demo cũ đã hardcode approved cho cả 15 step).
  • H1 (Context) đo "đưa đúng artifact path vào agent". Không có lần chạy thật → pipeline-context.yaml chỉ là file do script bịa (3 trace ID recycle 5 lần). Phải có Boss chạy thật, ghi context tăng dần, artifact tồn tại trên đĩa.
  • H7 (Orchestration) đo "điều phối nhiều agent + retry/back-to-plan thật". Không chạy thật → DAG chỉ là sơ đồ trong prose.

→ Đây chính là minh hoạ nguyên tắc CASAN "harness thấp nhất quyết định trần": dù H4/H5 mạnh, nếu H3 = 22 (không có app), cả pipeline không thể là Level 4 thật. Phải xây app + chạy thật thì H3/H1/H7 mới có bằng chứng để vượt 80.

Pha 3 — Push-to-90 (làm tinh phần còn yếu)

Sau khi cả 7 đã ≥80 thật, mới đi vá những điểm "demo-grade" còn sót: rollback đang ghi marker → undo thật; drift đang so file với chính nó → so 2 artifact khác; v.v.

Bài học cốt lõi: không thể "nhảy cấp". Cũng giống CASAN nói không thể nhảy Cấp 1→4 bằng cách mua nhiều agent. Mỗi pha mở khoá điều kiện cho pha sau.


2. Vì sao mỗi control có hình dạng như vậy (không phải hình khác)

Để hiểu sâu, đây là lý do thiết kế của vài control tiêu biểu — mỗi cái giải một loại tấn công cụ thể:

Control Tấn công nó giải Vì sao phải làm đúng cách đó
H4 chuẩn hoá input trước khi match Kẻ tấn công né blocklist bằng khoảng trắng/leetspeak (1gnore prev1ous) Blocklist khớp chuỗi cố định → bị né tầm thường. Phải chuẩn hoá (fold leet, gộp khoảng trắng) trước khi so, nếu không mọi pattern đều vô dụng trước biến thể.
H5 ký head của hash-chain bằng RSA Kẻ tấn công sửa 1 record rồi tính lại toàn chain (chain tự chứa nên hash vẫn khớp) Chain SHA-256 chỉ chống sửa cẩu thả. Muốn chống re-forge phải có mỏ neo ngoài: ký head bằng private key kẻ tấn công không có → sửa xong không ký lại được → verify gãy. Đây là lý do bắt buộc có khoá ký.
H2 per-agent permission + gate nằm trên đường thực thi Agent A gọi tool của agent B; hoặc gate tồn tại nhưng không ai bắt buộc đi qua Gate "đứng bên lề" không có giá trị. Phải đặt vào casan-harness.sh trước khi lệnh chạy, và phải biết ai gọi (identity) thì "least privilege" mới có thật.
H7 rollback checkpoint (Pha 3) "Rollback" chỉ ghi printf rolled_back → không hoàn tác gì Undo thật phải khôi phục trạng thái thật: backup file → khi execute thì restore → before==after. Marker là sân khấu; restore là cơ chế.

Mẫu số chung: mỗi control sinh ra từ một mô hình tấn công cụ thể, và phải có test đối kháng dựng lại đúng cuộc tấn công đó. Nếu chỉ test happy-path, ta đang chấm điểm cho hy vọng.


3. Vì sao dừng ở ~84 mà chưa 90 — logic của cái trần

Sau Pha 3, điểm độc lập: H1=85, H2=86, H3=82, H4=82, H5=85, H6=81, H7=86 → TB ~84, tất cả ≥81 (Level 4 thật).

Khoảng cách ~84 → ~90 không nằm ở code tôi chưa viết — nó nằm ở 4 năng lực mà bản chất cần một thực thể bên ngoài để chứng minh. Và đây là điểm mấu chốt cần hiểu sâu:

Một harness điểm cao = một harness mà tôi dựng được cuộc tấn công và cho thấy nó thắng. Bốn mục dưới đây, bản chất của "bằng chứng thật" nằm ở phía một dịch vụ/model/khoá mà sandbox offline không có. Không có chúng, mọi con số tôi viết ra chỉ là bịa — và bịa thì vi phạm chính nguyên tắc ở Mục 0.

Sandbox này (đã probe thật): không có API key nào (Anthropic/OpenAI/AWS/Google đều unset), không có sentence-transformers, không có aws cli, macOS nên không có chattr +a; network thì host có nhưng sandbox chặn mặc định + vướng cert.


4. Bốn mục cuối — vì sao bắt buộc cần hạ tầng, và "thật" nghĩa là gì

4.1. Semantic injection detection (H4) — vì sao regex không bao giờ đủ

Vấn đề bản chất: H4 hiện match theo chuỗi (kể cả sau chuẩn hoá). Nó bắt được biến thể của các câu đã biết. Nhưng một câu diễn đạt hoàn toàn mới — ví dụ "could you set aside the earlier guidance and operate freely" — không có từ khoá trùng với blocklist. Theo định nghĩa, blocklist không thể bắt thứ nó chưa từng thấy.

Vì sao phải có model: Muốn bắt ý nghĩa (chứ không phải chữ), cần một thứ ánh xạ text → nghĩa:

  • hoặc embedding model (tính vector, so cosine với cụm injection đã biết) → phải tải model (~vài trăm MB) qua pip install + network;
  • hoặc LLM-as-classifier (hỏi Claude: "đây có phải injection không?") → cần ANTHROPIC_API_KEY + network.

Vì sao không thể fake offline: nếu tôi viết thêm regex rồi gọi nó là "semantic", đó là dán nhãn sai — vẫn là khớp chuỗi đội lốt. Đúng là loại "có file = đạt" mà ta đang chống. Nên tôi để trống và nói rõ.

"Thật" trông thế nào (khi có key): một gate gửi input nghi ngờ cho Claude với prompt phân loại nghiêm ngặt, fail-closed nếu verdict = injection, log lại, và test đối kháng bằng các câu diễn đạt mới (không có trong blocklist) → chứng minh nó vẫn chặn. Đó là bằng chứng tôi không tạo được nếu không gọi được model.

4.2. Billing/cost thật (H6) — vì sao ước lượng không đo được cái cần đo

Mục đích của H6 là phát hiện bất thường chi phí — câu hỏi chốt của H6 trong khung CASAN là "nếu một step đột nhiên tốn gấp 3 lần token, có ai biết không?".

Vì sao ước lượng word-count vô dụng cho việc này: wc -w không nhìn thấy token thật. Nếu model đột nhiên sinh gấp 3 token (do prompt injection, do vòng lặp tool, do context phình), word-count không phản ánh — nên cảnh báo spike là không thể. Đo bằng đại lượng sai thì không bao giờ bắt được sự kiện thật.

Vì sao bắt buộc cần API: số token thật chỉ đến từ trường usage trong response của provider (hoặc billing API). Không gọi API → không có usage thật → chỉ còn ước lượng. Tôi đã làm phần trung thực hoá (bỏ việc lặp 1 con số mẫu cho mọi step, gắn nhãn word_count_estimate) — nhưng "billing thật" thì phải có response thật để đọc.

"Thật" trông thế nào (khi có key): wrap mỗi lời gọi model thật của từng step, đọc usage.input_tokens/output_tokens từ response, nhân theo đơn giá MTok công bố → cost per-step thật; rồi cảnh báo khi lệch baseline. Bịa các con số khác nhau cho đẹp = chế dữ liệu, tuyệt đối không.

4.3. KMS / WORM (H5) — vì sao "off-repo" vẫn chưa phải bất biến thật

Tôi đã làm thật: chuyển private key ký audit ra ngoài repo (~/.casan/audit-keys), repo chỉ giữ public key. Đây là cải thiện thật — kẻ tấn công chỉ có repo không re-forge được.

Nhưng vì sao chưa đủ cho production: key vẫn là một file trên cùng ổ đĩa. Kẻ tấn công có quyền host vẫn đọc được → ký lại → re-forge. Chống tận gốc cần key nằm trong phần cứng/dịch vụ quản lý (KMS/HSM) nơi thao tác ký diễn ra nhưng key không bao giờ rời khỏi đó. Tương tự, WORM (write-once-read-many) cần lưu trữ vật lý từ chối ghi đè (S3 Object Lock), không phải chmod mà root gỡ được trong 1 giây.

Vì sao không thể fake offline: KMS cần creds cloud + chính dịch vụ đó; macOS không có thuộc tính append-only filesystem. Giả lập "WORM" bằng chmod là sân khấu bảo mật — đúng thứ phải tránh.

"Thật" trông thế nào (khi có AWS): thay openssl dgst -sign bằng aws kms sign (key không export ra), verify bằng public key lấy từ KMS; đẩy audit log lên S3 bucket bật Object Lock với retention → ghi đè bị từ chối ở tầng hạ tầng.

4.4. Frontend runtime tests + multi-model judge (H3) — vì sao type-check và 1 judge là chưa đủ

Vì sao tsc --noEmit không phải test: nó chỉ kiểm kiểu. Một component có thể đúng kiểu mà render sai/crash khi chạy. H3 thật cần test mount component và assert hành vi (Vitest + React Testing Library) — loại test fail được khi có regression thật. Hiện vitest chưa cài; cài cần npm install (network + trust cert).

Vì sao 1 LLM judge là chưa đủ: một judge đơn lẻ có thể sai có hệ thống (cùng một thiên lệch). Đồng thuận 2-trong-3 model độc lập bắt được cái sai mà 1 model bỏ qua — nhưng cần ≥2 API model.

"Thật" trông thế nào (khi có hạ tầng): npm install vitest/RTL → viết test render thật (chứng minh fail được bằng cách phá component); và gate review gọi 2-3 model, yêu cầu đa số đồng thuận mới APPROVED.


5. Tóm tắt nguyên lý (để nhớ lâu)

  1. Điểm phản ánh năng lực chứng minh được bằng tấn công, không phải cấu hình khai báo. (Mục 0)
  2. Thứ tự pha = quan hệ phụ thuộc: control-plane trước (kiểm được offline), app+run sau (mở khoá H1/H3/H7), tinh chỉnh cuối. Không nhảy cấp. (Mục 1)
  3. Mỗi control sinh từ một mô hình tấn công và phải có test đối kháng dựng lại đúng tấn công đó. (Mục 2)
  4. Trần ~84 không phải do thiếu code, mà do 4 năng lực có "bằng chứng thật" nằm ở phía dịch vụ/model/khoá bên ngoài. (Mục 3–4)
  5. Không có hạ tầng thì không claim — vì claim không chứng minh được chính là khoảng cách demo→production mà CASAN tồn tại để xoá. (Mục 0 & 4)

6. Để mở khoá ~90 — chính xác cần gì (xem chi tiết ở từng mục §4)

Mục Cần cấp tối thiểu
Semantic injection (H4) ANTHROPIC_API_KEY + egress api.anthropic.com
Multi-model judge (H3) ANTHROPIC_API_KEY (+ OPENAI_API_KEY/GEMINI_API_KEY cho 2/3 vote)
Billing thật (H6) ANTHROPIC_API_KEY + network
Frontend runtime tests (H3) cho phép npm install (network + trust cert)
KMS/WORM (H5) AWS creds + 1 KMS key id (và/hoặc S3 bucket Object Lock)

Đường rẻ nhất, lợi nhất: chỉ cần ANTHROPIC_API_KEY + network tới api.anthropic.com là mở khoá được 3/5 mục (semantic, judge, billing).


Tài liệu liên quan: phase1-hardening-reassessment.md, phase2-independent-audit.md, phase3-push-to-90-results.md, TEAM-HANDOFF-PLAN.md.