- 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>
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.jsonkhố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
_contractverbatim - Đ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:
- tick(session, head): OR-guard NO-OP nếu
head==last_ticked_headHOẶCsession==last_ticked_session; elsecounter+1+ set 4 field. - 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. - 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ủascripts/),-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
:148—Invoke-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-171—inNum < storedNum⇒ FAIL-LOUD exit 2 (trigger-1), KHÔNG ghi. - (3b) missing-object
:177-181—cat-filefail ⇒ FAIL-LOUD exit 3 (trigger-2 missing). - (3c) not-reachable + object-còn + không-lùi
:184-192—merge-base --is-ancestorexit≠0 ⇒$eventClass='squash-benign', đi tiếp.
- (3a) regression
- (4) tick
:195-236—counter+1, event-string (clean :209 / squash-benign :206), 4 surgicalReplace-LineOnce(counter/session/head/at), appendhistory[]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-249—WriteAllText($tmp, UTF8NoBom)→ nếu-FaultStopAfterTempSTOP exit 42 (file gốc nguyên):243→ elseMove-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,
f09f94b4present, 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_atkhô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 updateSolutionErp.slnx. .session-counter.jsonTHẬ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 trongscratchpad/(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).