Files
solution-erp/.claude/workflows/runs/2026-07-12-h22-wal-adopt/sub-task-2.md

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)

  1. CREATE .claude/hooks/wal-flush.ps1 (folder mới)
  2. EDIT .claude/workflows/README.md — FLIP 2-mode policy
  3. EDIT .claude/workflows/runs/README.md — add ## taskList snapshot section

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 comment KHONG 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 ướ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)