Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6.0 KiB
sub-task-2 — H22 hook PS1 + FLIP policy workflow (D2+D6)
Role: implementer-backend (Write/Edit-capable) · Run: 2026-07-12-h22-wal-adopt · Spec: D2 (hook PS1) + D6 (FLIP RUN-TRACE policy)
Scope (3 deliverables, isolation-clean)
- CREATE
.claude/hooks/wal-flush.ps1(folder mới) - EDIT
.claude/workflows/README.md— FLIP 2-mode policy - EDIT
.claude/workflows/runs/README.md— add## taskList snapshotsection
Không đụng file nào khác. git status cho thấy nhiều file M/?? khác = việc của sibling-task trong cùng fan-out song-song (session-.md, .gitattributes, pause.md, tiep.md, settings.json, CLAUDE.md, rules.md, harness-11-engine.md, broadcasts/) — KHÔNG phải write của tôi.
(1) .claude/hooks/wal-flush.ps1 — port bash→PS 5.1, persist-only
Port nguyên semantics từ broadcast §3 (bash wal-flush.sh, dòng 82-97) sang PowerShell 5.1 dùng skeleton lead-verified. Chỉnh cosmetic DUY-NHẤT: 2 em-dash — trong comment → hyphen - để đạt pure-ASCII (lesson #30 — tránh mojibake .ps1). KHÔNG thêm logic ngoài skeleton.
Nội dung final (788 bytes, pure-ASCII, LF, no-BOM):
# wal-flush.ps1 - WAL persist-only Stop hook (H22 S111). KHONG sinh noi dung, KHONG push, KHONG block.
# Fail-open MOI nhanh: exit 0 ke ca loi (Stop-hook exit!=0 co hanh vi rieng - CAM).
# Per-path add + CAND commit (X3: add/commit da-pathspec = all-or-nothing khi 1 path vang).
try {
$root = if ($env:CLAUDE_PROJECT_DIR) { $env:CLAUDE_PROJECT_DIR } else { "." }
Set-Location $root
$paths = @(".claude/WAL.md", ".claude/agent-memory/", ".claude/workflows/runs/")
$cand = @()
foreach ($p in $paths) {
git add -- $p 2>$null
git diff --cached --quiet -- $p 2>$null
if ($LASTEXITCODE -ne 0) { $cand += $p }
}
if ($cand.Count -gt 0) {
$ts = Get-Date -Format "yyyyMMddTHHmm"
git commit -q -m "wal: flush $ts" -- $cand 2>$null | Out-Null
}
} catch { }
exit 0
Semantics-fidelity bash→PS (từng dòng khớp):
| bash §3 | PS 5.1 | Khớp |
|---|---|---|
cd "${CLAUDE_PROJECT_DIR:-.}" || exit 0 |
$root = if($env:CLAUDE_PROJECT_DIR){...}else{"."}; Set-Location $root (trong try/catch→exit 0) |
✅ fail-open |
P=".claude/WAL.md .claude/agent-memory/ .claude/workflows/runs/" |
$paths = @(3 path y-hệt) |
✅ 3 path |
git add -- "$p" per-path |
git add -- $p 2>$null per-path |
✅ per-path (X3 all-or-nothing tránh) |
git diff --cached --quiet || CAND+=p |
git diff --cached --quiet; if($LASTEXITCODE -ne 0){$cand+=$p} |
✅ exit!=0 = có staged |
[ -z "$CAND" ] && exit 0 |
if($cand.Count -gt 0){commit} |
✅ chỉ commit khi non-empty |
git commit -q -m "wal: flush $(date +%Y%m%dT%H%M)" -- $CAND |
$ts=Get-Date -Format "yyyyMMddTHHmm"; git commit -q -m "wal: flush $ts" -- $cand |
✅ (T literal trong .NET format, khớp %Y%m%dT%H%M) |
exit 0 cuối |
exit 0 cuối |
✅ fail-open mọi nhánh |
Guard-rail đã verify (dead-ends né):
- X2 (0 content-gen / 0 push / 0 block): grep Set-Content/Out-File/Add-Content/echo/
>>= NONE-OK ·git push= NO-GIT-PUSH-OK (chỉ 'push' trong commentKHONG push) ·exit [1-9]= NONE-OK (chỉexit 0). - X8: hook chỉ add/commit path cố-định (WAL + agent-memory + runs), message
wal: flush <ts>KHÔNG Co-Authored-By, KHÔNG secret. - lesson #30: byte-scan = 788 bytes, 0 non-ASCII; BOM =
23 20 77(# w, no BOM); PSParser.Tokenize = PARSE-OK.
(2) .claude/workflows/README.md — FLIP 2-mode policy
Vùng ## 2 MODE memory (mode-table + câu chốt). 2 edit:
- +1 row bảng sau "Khi dùng":
| Bắt buộc? (sàn H22 S111) | mode-A hợp lệ cho fan-out NHỎ | **BẮT BUỘC khi run ≥3 task HOẶC ước chạy >5 phút** (args.run → per-agent-file sub-<role>-<i>.md) |— 3-col khớp header, table structure intact. - Sửa "Khi dùng" DEFAULT cell: "fan-out NHẸ (~2-3 phút…)" → "fan-out NHỎ (<3 task VÀ ước ≤5 phút; read/analyze — vd recon)".
- Thay câu chốt
> Mặc định DEFAULT…→> **Sàn H22 (S111) — FLIP policy:** mode-A return-delta-only VẪN hợp lệ cho fan-out NHỎ, NHƯNG run ≥3 task HOẶC ước chạy >5 phút = BẮT BUỘC RUN-TRACE mode… Lý-do: chết giữa run vẫn còn vật-liệu trên đĩa để /tiep nối — per-TURN ≠ per-TASK: returns mất khi chết giữa turn, file trên disk còn.
GIỮ nguyên 3 row còn lại (Sub ghi file / Lead làm / Rủi ro) + heading. KHÔNG renumber (X10).
(3) .claude/workflows/runs/README.md — add taskList snapshot section
Chèn section mới ## taskList snapshot (H22 — ghi TRƯỚC launch) GIỮA C2 và C3 (anchor = C3 heading; KHÔNG renumber C1-C8, X10). Nội dung: template ## taskList snapshot trong run.md (role · label · prompt-gist 1-dòng) ghi TRƯỚC launch + dòng WAL wf: 5-trường (wf:<mục-đích> run= runId=<id|pending> args= done=<k>/<N> harvested=<yes|no|partial>) ghi NGAY SAU launch + memory-pack KHÔNG snapshot (X6, re-derive tươi lúc /tiep).
ACCEPTANCE (row #6+#7) — evidence
| Tiêu-chí | Verdict | Evidence |
|---|---|---|
ps1 exit 0 ≥1 dòng cuối |
PASS | grep -c 'exit 0'=2 (comment+final); tail -1=exit 0 |
| ps1 try/catch present | PASS | L4 try { · L18 } catch { } |
| ps1 per-path foreach | PASS | L9 foreach ($p in $paths) |
| ps1 $cand present | PASS | 4 occurrences |
| ps1 0 push / 0 content-gen / 0 block | PASS | no git push; no Set-Content/Out-File/echo/>>; no exit [1-9] |
| workflows/README có 'BẮT BUỘC'+'≥3'+'5 phút', mode-A-nhỏ hợp lệ | PASS | L27+L32 'BẮT BUỘC'; 'run ≥3 task'; '>5 phút'/'≤5 phút'; L32 'VẪN hợp lệ cho fan-out NHỎ' |
| runs/README 'taskList snapshot'≥1 + 'wf:'≥1 | PASS | grep -c 'taskList snapshot'=3; grep -c 'wf:'=2 |
Verify commands run
PSParser.Tokenize→ PARSE-OK- byte-scan → 788 bytes / 0 non-ASCII / no-BOM / no-CRLF
- grep sweep 3 file → tất cả tiêu-chí PASS
git status --porcelain→ 3 file của tôi (+ sibling-task files KHÔNG phải của tôi)