Files
solution-erp/.claude/workflows/runs/2026-07-24-S149-hoi-tu-bookend/sub-impl-M2-tick.md
pqhuy1987 398d343403
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m48s
[CLAUDE] Docs: S149-S150 closeout — bookend lượt đầu end-to-end (hình B) + 2 arc đóng + tally #53 chốt 45
- Arc-1 hội-tụ-bookend (rename 5 vai + 4 máy PS1 + C9-C12) + arc-2 adap-backlog 23/23 + probe Opus-5 khép (S149)
- Bookend @close S150: wave 6 vai (4 đo + 2 KIỂM) → 20 FLAG disposition từng-dòng; ring1 31/34-ĐẠT, ring2 10/10 + M-1 view-stale-role-desc
- Khoá _frozen_until_owner CẮM trước h24-signal-write (frozen-held in thật); 9 class FIRE; JUMP dải {5,4,4,3}+asym{1,4} chờ anh #21
- STATUS bump CURRENT S149-S150 + 2 khối Recently Done; HANDOFF segment mới + re-stamp đủ-slug + (17)(18)(19) lật + (14) viết lại + 6 slot #21-#26
- K2 posture-A @engine PHẦN K + K5 luật TÁCH vòng-đo-mới @session-end (O-2/O-3 OWNER-DELEGATED execute)
- sleep-doc derive-monitor-set (bỏ hardcode 4-tên); spawn-model-audit desc/comment fix; h24-signal-write param-default fix (first-live bug)
- M9 on-behalf ×3 (tooling + inv-cb F3 + reviewer F4); ring1/ring4 nhà seed; ring2 3 cite-dead vá
- Sổ garble #53 derive-from-body chốt 45 + sub-class ngược-#53/skeleton-ruột-rỗng (ghi-đĩa CẦN-KHÔNG-ĐỦ)
- Session-log S149-S150 (Sàn-5 đăng-ký 6 run=) + closeout-synthesis dạng-1

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 12:17:05 +07:00

9.8 KiB

sub-impl-M2 — scripts/session-counter-tick.ps1 (cơ-khí-hoá _contract)

Vai: implementer (hmw). Task: M2 + FIX-1(CRITICAL) + checklist K2, spec spec-hoi-tu-bookend-24-07-2026.md. Nguồn contract = .claude/governance/.session-counter.json khối _contract (đọc nguyên văn, THI HÀNH đúng). Khuôn hub tham khảo = AI_INFRA/scripts/h17_cadence.py (_save_atomic :174-184 atomic tmp+replace). VERDICT: DELIVERED — 8/8 fault-inject PASS + 2 teeth-check PASS + 0 tokenize-error.

0. Trạng thái

  • Đọc spec M2/FIX-1/K2 + contract _contract verbatim
  • Đo môi trường (encoding/PS-version) — quyết định chiến-lược ghi
  • Viết script scripts/session-counter-tick.ps1 (250 dòng, pure-ASCII, LF)
  • Fault-inject K2 (a)-(e) + bonus (f) trên cây tạm — 8/8 PASS
  • Teeth-check (head-match NO-OP · diff-minimality · emoji preserve) — PASS
  • Incident: lỡ tick file THẬT khi smoke-test → REVERT sạch, verify byte-exact
  • Verdict + return

1. Contract _contract — THI HÀNH đúng (không sáng tác)

Khối _contract có 5 mệnh (đọc nguyên văn ở .session-counter.json:5-11). Script map 1-1:

  1. tick(session, head): OR-guard NO-OP nếu head==last_ticked_head HOẶC session==last_ticked_session; else counter+1 + set 4 field.
  2. fail_loud_on_regress (v2, 2 trigger 2 verdict): (1) counter LÙI → FAIL-LOUD; (2) last_ticked_head không-reachable → phân-loại TRƯỚC báo: object CÒN (cat-file -t=commit) + counter không-lùi ⇒ squash-benign (ghi 1 vết + đi tiếp); object MẤT hoặc counter lùi ⇒ FAIL-LOUD.
  3. overdue / 4. jump / 5. writer — KHÔNG thuộc phạm-vi M2 (không đụng).

FIX-1 (CRITICAL) yêu cầu KHÔNG đụng class_repeat, KHÔNG reset — script chỉ chạm 4 field + append history[]. (chứng: K2-b class_repeat + last_audit UNTOUCHED.)

2. Quyết định thiết-kế (evidence-based, đo trên cây tạm trước khi code)

Quyết định Đo được Vì sao
WRITE = surgical string-replace, KHÔNG ConvertTo-Json PS5.1 ConvertTo-Json round-trip 25900→28495 byte (CRLF + char-escape); surgical 25900→25995 (chỉ +history) Chỉ có PowerShell 5.1 (không pwsh7 ⇒ không System.Text.Json). ConvertTo-Json 5.1 chèn CRLF + reformat = diff bẩn trên file governance committed
READ+parse = ConvertFrom-Json (guard-logic) parse OK, đọc counter=23/S149/head/8-key class_repeat Parse robust cho guard, tách khỏi write
encoding = UTF-8 no-BOM, LF file THẬT: no-BOM, LF-only, 4-space indent, 8 emoji 🔴 = 32 non-ascii byte Bẫy E-010: thiếu -Encoding đọc ANSI. [IO.File]::ReadAllText(UTF8) + WriteAllText(UTF8NoBom) giữ byte-exact, KHÔNG dịch EOL
git call bọc EAP='Continue' (Invoke-GitLines :112) cat-file -t <missing> ghi stderr → dưới EAP=Stop PS5.1 gói NativeCommandError = terminating (crash exit 1) kể cả 2>$null Hạ EAP quanh git-call ⇒ non-zero-exit là DATA để phân-loại, không phải crash. (Bắt được vì fault-inject (f) crash exit1 thay exit3)
last_ticked_at = git HEAD commit-date (log -1 --format=%cd --date=short) khớp format 2026-07-24 sẵn có; fallback wall-clock nếu git fail Theo nguyên-tắc M1 "máy chạm đĩa không wall-clock"; deterministic
regression guard = SỐ-PHIÊN parse từ label (Parse-SessionNum :82) K2-c: stored S200 > input S150 ⇒ FAIL-LOUD counter(23) ≠ session-num(149) ⇒ so-sánh phải là session↔session (chuẩn hub sid < sid_last_tick). "counter" trong contract = tiến-trình-phiên loose-wording

3. Script — cấu trúc + evidence file:line (scripts/session-counter-tick.ps1, 250 dòng)

  • param :63-68-Session (bắt buộc, validate ^S\d+), -RepoRoot (default = parent của scripts/), -FaultStopAfterTemp (switch fault-inject K2(e) ONLY, :67).
  • helpers: Fail-Loud :76 (stderr 1 dòng + exit≠0) · Parse-SessionNum :82 · Replace-LineOnce :91 (count-assert = 1, shape-drift⇒fail-loud #7) · Json-Escape :101 · Invoke-GitLines :112 (EAP-Continue git wrapper).
  • (1) HEAD :148Invoke-GitLines rev-parse HEAD; không resolve ⇒ exit 4.
  • (2) OR-guard :157-164$Session -eq $storedSession -OR $headSha -eq $storedHead ⇒ NO-OP exit 0, in 1 dòng.
  • (3) classify TRƯỚC tick :165-193:
    • (3a) regression :166-171inNum < storedNum ⇒ FAIL-LOUD exit 2 (trigger-1), KHÔNG ghi.
    • (3b) missing-object :177-181cat-file fail ⇒ FAIL-LOUD exit 3 (trigger-2 missing).
    • (3c) not-reachable + object-còn + không-lùi :184-192merge-base --is-ancestor exit≠0 ⇒ $eventClass='squash-benign', đi tiếp.
  • (4) tick :195-236counter+1, event-string (clean :209 / squash-benign :206), 4 surgical Replace-LineOnce (counter/session/head/at), append history[] qua EOF-anchored regex (count-assert=1), re-parse ConvertFrom-Json TRƯỚC khi chạm đĩa (:233, fail⇒exit 9 no-write).
  • (5) atomic write :238-249WriteAllText($tmp, UTF8NoBom) → nếu -FaultStopAfterTemp STOP exit 42 (file gốc nguyên) :243 → else Move-Item -Force -LiteralPath $tmp $counterFile :248 (khuôn hub _save_atomic).

Exit codes: 0 ok/noop · 2 regress · 3 missing-obj · 4 no-HEAD · 5 no-file · 6 bad-json · 7 shape-drift · 8 usage · 9 post-edit-unparse · 42 fault-stop.

4. Fault-inject K2 — cây tạm git-repo (C1→C2 HEAD, C3 orphan) + copy .session-counter.json THẬT

Harness scratchpad/k2-faultinject.ps1 (Start-Process, exit-code sạch). 8/8 PASS:

Ca Kịch bản Kỳ vọng Kết quả đo
a cùng-label S149 NO-OP exit0, không ghi exit0, counter 23 giữ, bytes unchanged, out="NO-OP: session-label 'S149' already ticked"
b HEAD-mới(C2)+label-mới S150 +1 đúng 1 exit0, counter 23→24, sess S150, head=C2, hist 14→15 CLEAN
b' class_repeat/last_audit sau tick UNTOUCHED gap-owner-specifics=4, light_at_counter=22 (nguyên)
c stored S200 > input S150 FAIL-LOUD exit≠0 KHÔNG ghi exit2, stderr có REGRESSION, file byte-identical, 0 .tmp sót
d head=C3 orphan (exists, unreachable) squash-benign history+1 đi tiếp exit0, counter→24, hist+1 event=squash-benign, sess S150
e -FaultStopAfterTemp (kill trước move) file gốc parse OK nguyên exit42, gốc unchanged, parse OK, counter=23, .tmp tồn tại
e' recovery: chạy lại không-fault hoàn tất move exit0, counter→24, .tmp consumed
f (bonus) head=40-zeros MISSING FAIL-LOUD trigger-2-missing exit3, stderr có "MISSING object", file unchanged

Teeth-check (scratchpad/k2-verify2.ps1, anti-Goodhart) — PASS:

  • head-match NO-OP: label MỚI S151 nhưng storedHead==HEAD ⇒ NO-OP exit0 (chứng OR-guard nhánh head, không chỉ nhánh label). out="NO-OP: HEAD ... already ticked".
  • diff-minimality: clean tick đổi ĐÚNG L14-17 (4 field in-place) + L109 }}, + chèn 1 entry. MỌI dòng khác BẤT BIẾN.
  • emoji/EOL preserve: non-ascii 32→32, f09f94b4 present, 0 CRLF, stderr rỗng trên success.

5. 🔴 INCIDENT (khai thẳng) — lỡ tick file THẬT + revert sạch

Smoke-test default--RepoRoot với giả-định "HEAD==2f39a7e (từ session-start) ⇒ NO-OP an-toàn". SAI: trong fan-out song-song, Stop-hook wal-flush.ps1 auto-commit mỗi turn-boundary ⇒ HEAD đã dời 2f39a7e→611b41f. Script chạy ĐÚNG (head≠stored ⇒ CLEAN tick 23→24, ghi file THẬT). Đây là process-error của em, KHÔNG phải bug script (thực ra nó CHỨNG script chạy đầu-cuối đúng trên file thật: giữ 25900+ byte/emoji, append entry chuẩn, atomic).

  • REVERT (scratchpad/revert-real.ps1): reverse 3 field (24→23, S150→S149, 611b41f→2f39a7e; last_ticked_at không đổi vì 611b41f commit-date=2026-07-24) + xoá entry S150 (EOF-anchored, S150-pinned). Sanity-guard confirm state=S150-của-em trước khi chạm.
  • VERIFY restore: bytes 25900, non-ascii 32, counter 23, sess S149, head 2f39a7e, history 14, last=S149-W3-kiem. git diff --stat = 18 ins / 6 del (khớp pre-smoke). Residue: S150=0, session-counter-tick=0, "counter": 24=0. Pre-existing S149 change (counter 22→23) còn nguyên.
  • BÀI HỌC: CẤM chạy script tick trên repo THẬT để "smoke" — HEAD dời bất-kỳ lúc nào trong fan-out. Mọi test = temp-repo -RepoRoot. (Đã có trong prompt "cây tạm copy" — em vi-phạm ở bước smoke, đã sửa sạch.)

6. Isolation + housekeeping

  • Code-file DUY-NHẤT tạo: scripts/session-counter-tick.ps1 (pure-ASCII 0 non-ascii, LF-only 250 dòng, khớp .gitattributes * text=auto eol=lf). Không .cs/.csproj ⇒ KHÔNG cần update SolutionErp.slnx.
  • .session-counter.json THẬT: đã REVERT về pre-smoke — không phải deliverable của em (thay đổi trong đó là S149 pre-existing).
  • Không đụng class_repeat (M3), không đụng agent-memory/MD-canonical/sub-MD khác. Mọi test-artifact nằm trong scratchpad/ (ngoài repo).
  • PSParser tokenize: 0 error. Invalid -Session 'garbage' ⇒ exit 8 fail-loud.

7. Verdict

M2 DELIVERED + K2 FULL PASS. scripts/session-counter-tick.ps1 cơ-khí-hoá _contract verbatim: OR-guard idempotent (2 nhánh), classify-3-nhánh (regress/missing/squash-benign) fail-loud-đúng-trigger, tick 4-field + history-append, atomic tmp+Move-Item-Force, KHÔNG đụng class_repeat, KHÔNG reset. 8/8 fault-inject + 2 teeth + tokenize-clean. 1 incident lỡ-tick-real đã revert byte-exact (25900/counter23, diff 18+/6-, 0 residue).