# `runs/` — Run-trace convention (Harness-10 · cấu trúc **PHẲNG**)

> **Mục đích:** mỗi workflow fan-out (RUN-TRACE mode) ghi lại 1 dấu-vết hoàn-chỉnh trong `runs/<run-id>/` git **TRACKED** — plan + per-sub detail + bản-gom — cộng sổ `_ledger.md` 2-nhịp. TRACKED nghĩa là mọi write trong run-folder hiện trong `git diff` → **audit trực-tiếp**, không cần tin lời agent return. Adopt AI_INFRA Harness-10 (anh 06-18) + DELTA `h10-flat-detector-refine` (06-18). Convention cha: `../README.md`.
>
> 🆕 **REFINE 06-18 — cấu trúc PHẲNG.** Trước đây mỗi run-folder có **2 thư mục con** (`sub-md/` + `harvest/`). NAY mọi vết tích nằm **phẳng cùng MỘT cấp** trong `runs/<run-id>/`; phân biệt **bản-thô (raw, per-sub)** vs **bản-đã-soát (verified, gom)** bằng **TÊN file**, KHÔNG bằng thư mục con. Lý do: engine (`hmw.js` JS-sandbox no-fs) chỉ tạo artifact ở **một cấp** một cách tự nhiên — bỏ thư-mục-con sâu thì xoá luôn **một điểm-hay-quên** ngay tại gốc (không còn thư-mục-con-để-quên-dựng).

**run-id** = `YYYY-MM-DD-h<NN>-<slug>` (vd `2026-06-18-harness-audit-invest`). Nhiều run cùng ngày → slug phân-biệt stage (`-invest` / `-implement` / `-review`).

---

## C1 — Cấu trúc PHẲNG (mỗi `runs/<run-id>/` — file cùng MỘT cấp)

```
runs/<run-id>/
├── run.md                       ← (1) PLAN — em main @P1: meta + mục-tiêu + agents-table + guards + status OPEN→CLOSE
├── sub-<role>-<i>.md            ← (2) PER-SUB raw — 1 file/sub/turn, prefix `sub-` (vd sub-investigator-codebase-0.md)
├── sub-<role>-<i>.md                (fan-out cùng-role → đánh số -0 / -1 …)
└── <stage>-synthesis.md         ← (3) GOM verified — em main: bản-gom 1 turn, suffix `-synthesis.md` (vd invest-synthesis.md)
```

**Phân-biệt raw vs verified bằng TÊN, KHÔNG bằng subfolder:**

| Loại | Quy-ước TÊN | Ai ghi | Nội-dung |
|---|---|---|---|
| **PLAN** | `run.md` (cố định) | em main @P1 | nguồn-sự-thật của run: workflow run-id (evidence B3 `wf_…`), adap/mandate, checkpoint, opened, input-spec (nếu nối stage trước), agents-table (`# · role · task`), guards áp cho sub, output-path, status OPEN→CLOSE. |
| **PER-SUB (raw)** | **prefix `sub-`** → `sub-<role>-<i>.md` | write-sub tự ghi @P2 · read-only sub → em main scribe @P3 | full working detail 1 sub. **1 sub-MD / role / turn** (cùng-role → `-0`/`-1`). Read-only sub (CHỈ Bash) KHÔNG Bash-write MD (mojibake) → trả `findings`+`subMdPath`, **em main single-writer**. |
| **GOM (verified)** | **suffix `-synthesis.md`** → `<stage>-synthesis.md` | em main per-turn (C4) | gom `sub-*`+findings → 5-trục integrity → build-spec/synthesis. |

> 🟢 **Quy-tắc nhận-diện:** file bắt đầu bằng `sub-` = **bản-thô** (per-sub); file kết bằng `-synthesis.md` = **bản-đã-soát** (gom). `run.md` = plan. Không cần mở file/không cần subfolder để biết loại — đọc TÊN là đủ. (Khớp `../hmw.js` inject path: engine ghi-direct `runs/<run-id>/sub-<role>-<i>.md`, KHÔNG `sub-md/…`.)
>
> **Live dogfood:** `2026-06-18-harness-audit-invest/` = `run.md` + `audit-synthesis.md` **CHỈ** (KHÔNG subfolder, KHÔNG raw `sub-audit-*.md`) — vì 1 auditor fail-no-StructuredOutput + 2 truncated → em main self-gate ground-truth từ đĩa, không có raw-sub để scribe (xem `audit-synthesis.md:4`). Ví-dụ tên `sub-<role>-<i>.md` là minh-hoạ QUY-TẮC-ĐẶT-TÊN, KHÔNG phải artifact bắt-buộc mỗi run. Run `2026-06-18-harness-fix-implement/` mới có đủ raw (`sub-impl-sleepcmd.md` + `sub-impl-runsreadme.md` + `implement-synthesis.md`) = full flat set điển-hình.

---

## C2 — Scaffold `run.md` ở OPEN (em main @P1, TRƯỚC khi invoke Workflow)
`hmw.js` chạy JS-sandbox **no-filesystem** → KHÔNG tự tạo folder/file. Em main PHẢI Write @P1:
1. **Tạo run-folder + PLAN:** `runs/<run-id>/run.md` (điền plan đầy-đủ). 🆕 **KHÔNG cần `mkdir sub-md/` / `harvest/` / `.gitkeep`** — file `sub-*` và `*-synthesis.md` sẽ sinh ra **phẳng cùng cấp** trong cùng folder đó (engine + em main ghi 1 cấp).
2. **Ghi OPEN-beat** vào `runs/_ledger.md` (1 dòng, `closed=⏳`).
3. **(nối stage)** trỏ `input spec:` trong `run.md` sang `<stage>-synthesis.md` của run trước (vd implement đọc `invest-synthesis.md`).

> 🔴 **C2 là fragile-point.** Quên scaffold = run chạy nhưng KHÔNG có dấu-vết = **lỗ-hổng âm-thầm** (không lỗi, không cảnh-báo — chỉ thiếu file). 3-layer (C5) là lưới giảm-thiểu, KHÔNG khoá-cứng. Xem C7. 🆕 Cấu trúc phẳng bỏ **1 trong 2** điểm-hay-quên (không còn subfolder-to-forget); dư-lượng còn lại = **vẫn phải tự ghi `<stage>-synthesis.md` mỗi run** (close-gate C5/L3 bắt nếu thiếu).

---

## C3 — Git-tracked: VERIFY **2 nấc tách-bạch** (chỗ hay nhầm)
Run-folder PHẢI git **TRACKED** (re-include qua `.gitignore:83 !.claude/**`, last-match-wins). Verify ĐỦ **2 nấc** — đừng dừng ở nấc 1:

```bash
# Nấc (i) — GỠ-KHỎI-IGNORE (tracked-eligible): exit != 0 = KHÔNG bị ignore
git check-ignore .claude/workflows/runs ; echo "exit=$?"     # SE: exit=1 ✓ (không ignore)

# Nấc (ii) — THẬT-SỰ-COMMIT: liệt-kê ra file = đã `git add`/commit
git ls-files .claude/workflows/runs | wc -l                   # SE: 22 ✓ (non-empty = đã commit)
```

- **Nấc (i) `check-ignore` exit != 0** = thư-mục KHÔNG nằm trong ignore (tracked-**eligible**). ⚠️ Mới là *đủ-điều-kiện-track*, **CHƯA chắc đã commit**.
- **Nấc (ii) `ls-files` non-empty** = thật-sự có file trong index (đã `git add`/commit). 🔴 **eligible ≠ committed:** một folder `check-ignore` exit!=0 NHƯNG `ls-files` rỗng = **chưa hề commit** (bằng-chứng sống tại AI_INFRA chính trường-hợp này). PHẢI chạy CẢ 2 lệnh.
- 🔴 **ĐÚNG PATH mới đáng tin.** Path đúng = **`.claude/workflows/runs`** (đầy-đủ từ repo-root). Bare `runs/` (tên trần) là path SAI — ở repo khác, `git check-ignore runs` từ repo-root có thể trả **exit 0** (match nhầm rule khác / không match negation `.claude/**`) → **đọc sai thành "bị ignore"**. *(Quan-sát SE: cả `runs` lẫn `.claude/workflows/runs` đều trả exit=1 vì .gitignore SE không có rule `runs` trần — nhưng ĐỪNG dựa may-rủi đó: luôn dùng path đầy-đủ `.claude/workflows/runs`.)*
- **Bẫy exit-code khi dùng `-v`:** `git check-ignore -v <path>` trả exit 0 cho CẢ ignore lẫn negation (`!.claude/**`) → đừng đọc exit của `-v` để kết-luận; dùng dạng không-`-v` ở trên (exit!=0 = không ignore) hoặc `&& echo IGNORED || echo NOT`.

---

## C4 — Per-turn primary (gom NGAY, không đợi session-end)
Gom là **việc của turn**, không defer. Sau MỖI fan-out turn → em main đọc `sub-*`+findings → ghi `<stage>-synthesis.md` **liền trong turn đó** (chính file synthesis này là bằng-chứng). Lợi: detail tươi, không mất qua nén-context; session-end chỉ VERIFY (không tái-tạo). **Đây là đường CHÍNH (primary)**; gom toàn-bộ @session-end (L3) chỉ là **lưới an-toàn (safety-net)**, KHÔNG phải đường chính.

---

## C5 — 3-layer anti-miss (lưới chống bỏ-sót — KHÔNG khoá-cứng-cùng-lúc-fire)
| Layer | Khi | Làm gì |
|---|---|---|
| **L1 in-run ledger-check** | em-main @P1, lúc mở run mới (TRƯỚC scaffold) | đọc `_ledger.md`: nếu run TRƯỚC còn `closed=⏳` (OPEN-beat chưa CLOSE / thiếu `<stage>-synthesis.md`) → gom + CLOSE nó TRƯỚC khi mở run mới. *(Engine no-fs → KHÔNG đọc được ledger → L1 là convention **EM-MAIN**, KHÔNG phải `hmw.js` prompt. hmw.js chỉ emit C4 per-turn return-instruction cho sub tại writeGuard.)* |
| **L2 session-start orphan-scan** | đầu session (`session-start.md` §2.1.1 H2) | scan `runs/*/` tìm **orphan = OPEN-beat (ledger `closed=⏳`) mà KHÔNG có `<stage>-synthesis.md`** → báo + giải-quyết (C6). |
| **L3 session-end close-gate** | cuối session (`session-end.md` §L.b(f) H2) | VERIFY per-turn gom đã xong cho mọi `runs/<id>/` (**idempotent — KHÔNG re-APPEND**, chống DUPLICATE-HARVEST) + 5-trục GATE backstop trước khi commit. 🆕 close-gate **chấp nhận CẢ 2 dạng** (old-subfolder + new-flat) trong giai-đoạn chuyển — xem C8. |

> 3 layer **độc-lập, fire ở 3 thời-điểm khác nhau** (run-open @P1 / session-start / session-end) — **KHÔNG layer nào do engine enforce** (hmw.js no-fs), cả 3 là convention em-main/H2. → giảm xác-suất sót, KHÔNG triệt-tiêu. Sót ở P1 (C2) cùng blind-spot với L1 (đều @P1) → chỉ bắt MUỘN ở L2 (session sau) / L3 (close-gate).

---

## C6 — Ledger 2-nhịp + orphan resolution (`_ledger.md`)
Sổ tất-cả run, 1 bảng. Mỗi run **2 lần ghi (2-nhịp)**:
- **OPEN-beat** (@P1, lúc scaffold): thêm dòng `| <run-id> | <workflow> | <opened> | ⏳ | <agents> | ⏳ | <stage>-synthesis.md |`.
- **CLOSE-beat** (lúc đóng run): điền `closed` timestamp + `verdict` (PASS/FAIL + 1 dòng + `wf_…`) + `harvest` (path file synthesis ✓).

`closed=⏳` = đang chạy (OPEN chưa CLOSE).

**Orphan** = dòng ledger `closed=⏳` nhưng run thực-tế đã xong/bỏ (session bị kill, agent die-0-byte, quên CLOSE-beat). **Giải-quyết-CỨNG** (không để treo):
1. **Điều-tra đĩa THẬT:** `run.md` status + có file `sub-*`? + có `<stage>-synthesis.md`? + git-log workflow `wf_…`.
2. **Đóng tay** nếu run thật-sự xong: điền CLOSE-beat (timestamp + verdict + harvest path).
3. **Đánh-dấu aborted** nếu run bỏ-dở: verdict = `⚠️ ABORTED — <lý-do>`, ghi rõ phần nào hoàn-thành (recover qua git/disk/prod truth, **KHÔNG tin agent return-message**).

---

## C7 — 🔴 CAVEAT trung-thực (no-overclaim)
- **Engine no-fs → scaffold KHÔNG tự-động.** `hmw.js` (JS-sandbox no-filesystem) KHÔNG tạo được folder/file. Run-trace dựa **100% vào em-main**: scaffold `run.md` @P1 (C2) + tự ghi `<stage>-synthesis.md` mỗi run (C4). Không có cơ-chế nào ép tạo file. **NOT fully-autonomous** — đây là **hard-gate-trên-INPUT** (em main tự dựng khung + tự bơm tham-số đầu/cuối mỗi run), KHÔNG phải engine tự-động.
- **Cấu trúc phẳng giảm fragile từ 2 → 1, KHÔNG triệt-tiêu.** Bỏ subfolder xoá được điểm-hay-quên "dựng sẵn `sub-md/`+`harvest/`" (engine giờ ghi 1 cấp, **no subfolder-to-forget**). NHƯNG còn **dư-lượng**: em-main VẪN phải nhớ ghi `<stage>-synthesis.md` mỗi run (close-gate L3 bắt nếu thiếu). Điểm-yếu **giảm 1-trong-2**, chứ không biến mất → C2 vẫn bắt-buộc.
- **3-layer = LƯỚI, KHÔNG khoá-cứng-cùng-lúc-fire.** L1/L2/L3 bắt lỗi **sau khi** việc đã xảy ra (post-hoc) hoặc nhắc **dựa-trên-input em-main-bơm** — giảm xác-suất sót, KHÔNG chặn tại-đúng-thời-điểm-fire. Phòng-thủ-nhiều-lớp, KHÔNG bảo-đảm tuyệt-đối.
- **G-015 no-overclaim — TRACKED ≠ read-only-ENFORCED.** Run-folder git-tracked KHÔNG biến sub thành read-only: sub **vẫn giữ Bash** (write-channel mở — ghi-ngoài-repo git-diff mù / curl Qdrant). Containment THẬT (model dưới) = em-main single-writer + git-diff(in-repo) + chunk-count(RAG), defense-in-depth — KHÔNG sandbox cứng, KHÔNG claim "ENFORCED".

---

## C8 — MIGRATION (run cũ giữ subfolder · close-gate chấp-nhận CẢ 2 dạng)
🔴 **KHÔNG viết lại lịch-sử.** 5 run-folder S71 đã chốt giữ **NGUYÊN cấu trúc cũ** `sub-md/` + `harvest/` (đã commit, KHÔNG đụng):
```
2026-06-18-h10-invest/      (run.md + sub-md/ + harvest/invest-synthesis.md)     ← cũ, GIỮ
2026-06-18-h10-implement/   (run.md + sub-md/ + harvest/implement-synthesis.md)  ← cũ, GIỮ
2026-06-18-h10-review/      (run.md + sub-md/ + harvest/review-synthesis.md)      ← cũ, GIỮ
2026-06-18-h910-finalize/   (run.md + sub-md/ + harvest/finalize-synthesis.md)   ← cũ, GIỮ
2026-06-18-h910-curate/     (run.md + sub-md/ + harvest/curate-synthesis.md)     ← cũ, GIỮ
2026-06-18-harness-audit-invest/  (run.md + audit-synthesis.md)                  ← 🆕 FLAT
```
- **Run MỚI từ refine 06-18 → dùng FLAT** (`sub-<role>-<i>.md` + `<stage>-synthesis.md` cùng cấp). Run CŨ → để yên (đừng gom-lên-cấp/đừng xoá subfolder — re-writing history vô-ích + làm bẩn git-log).
- 🟢 **Close-gate (L3) ACCEPT BOTH** suốt giai-đoạn chuyển: verify gom ở **`harvest/<stage>-synthesis.md`** (dạng cũ) **HOẶC** `<stage>-synthesis.md` phẳng (dạng mới). Không fail run cũ chỉ vì thiếu file-phẳng.
- **2 nấc track (C3)** áp cho CẢ 2 dạng: `check-ignore` exit!=0 + `ls-files` non-empty.

---

## Containment model (PHẢI khớp `_ledger.md` + `../hmw.js` — đồng-bộ 3 chỗ: đây · `_ledger.md` · `../hmw.js`)
> Run-folder `runs/<run-id>/` được git **TRACKED** → mọi write **HIỆN** trong git-diff = **audit trực-tiếp**. Containment: tracked-change **NGOÀI** `runs/<run-id>/` **VÀ NGOÀI** code-disjoint đã giao = **vi-phạm** (thay model Harness-2 B6 "mọi tracked-change = vi-phạm"). G-015 no-overclaim: TRACKED ≠ read-only-enforced — sub vẫn giữ Bash (write-channel mở), containment THẬT = em-main single-writer + git-diff(in-repo) + chunk-count(RAG).

---

## Anti-bypass detector — **SE TAILORED-OUT** (quyết-định có chủ-đích, anh chốt)
> Refine 06-18 (b) đề-xuất làm-chặt một **bộ-dò chống-lách-engine** (soi nhật-ký/mã xem có run nào né engine chuẩn không) theo **3 chức-năng**. **SE quyết-định KHÔNG hiện-thực bộ-dò này** — đã CÂN-NHẮC và thấy **N/A cho threat-model của SE.** Giữ trung-thực, KHÔNG overclaim "đã có anti-bypass detector".

**Lý-do tailored-out:**
- **SE chạy workflow qua Anthropic Workflow tool (`hmw.js` invoke), KHÔNG có bề-mặt-lách kiểu CLI-launcher.** AI_INFRA threat-model giả-định một node-CLI launcher (đường-vòng chạy lén engine) cần bộ-dò canh. SE **không có launcher như vậy** → không có "đường-vòng" để dò.
- **Containment SE đã đủ bằng cơ-chế khác (G-015 defense-in-depth):** `git diff` commit-gate (mọi write tracked HIỆN trong diff) + run-folder git-tracked (audit trực-tiếp) + `_ledger.md` orphan-scan (L2/L3) — backstop THẬT (không phải prompt). Bộ-dò pattern-match thêm = trùng-lặp, không bịt lỗ mới.

**3 chức-năng refine — CÂN-NHẮC rồi N/A cho SE:**

| Chức-năng refine (b) | Áp SE? | Vì sao |
|---|---|---|
| **(1) Whitelist launcher hợp-lệ** | N/A | SE không có launcher-wrapper quanh engine → không có gì để whitelist. |
| **(2) Khớp đa-dạng biến-thể path tới engine** | N/A | SE invoke engine qua Workflow tool (1 đường duy-nhất), không có path-variant để khớp. |
| **(3) Neo KHOÁ-khởi-chạy-thật (đừng match tên-engine lẫn trong script) + acceptance theo QUAN-HỆ** | N/A | Không có log-scan detector → không có chỗ neo launch-key. Acceptance-by-relation (hợp-lệ ≠ vi-phạm, hợp-lệ tăng dần OK) là nguyên-tắc tốt nhưng chỉ áp được khi CÓ detector. |

> 🔴 **No-overclaim:** SE **KHÔNG** claim đã adopt anti-bypass detector. SE claim: đã **đọc + cân-nhắc** 3 chức-năng, **chọn tailored-out** vì threat-model SE không có CLI-launcher-bypass-surface; containment dựa **git-diff + run-folder-tracked + ledger-orphan-scan** (G-015). Nếu sau này SE thêm launcher-wrapper → re-visit quyết-định này.
