- engine-doc canonical docs/governance/harness-11-engine.md (PHẦN A/B/C/D + 3-tier D5/D6/D7 + one-direction-lock D8 + CAVEAT honest) - scripts/governance-detectors.ps1 (C1 broken-pointer + C2/B3 staleness + C3 vocab-fork + C4 self-exclusion + C5 resolve, NO-API DÒ+FLAG-only, runtime-proven, FP-refined 59→27) - scripts/memory-archive-gate.ps1 (PHẦN A: hysteresis 0.85 + keep-floor 5 + 2-strike + A7 NO-API L1-eval) + budget.json archive_gate - B1 ×11 count→canonical-pointer (root CLAUDE.md, ef-core/dep-audit SKILL, skills/README, docs/CLAUDE.md) — drift mig53→55/test306→339/gotcha68→69 RESOLVED + ef-core +Mig 54/55 rows - cadence-wire D1 session-start §2.1.3 + D2 session-end §L.b(c) + agents/README Upgrade S75 - run-trace TRACKED: audit wf_7fdc3bd5-930 / implement wf_c5e5844e-7c1 / review wf_d7ca1ff8-942 (REVIEW PASS, completeness-gate ĐẠT) - check-email AI_INFRA harness-11 (verify whole-file 318ff9f6 + body b2a2fc1c) + adap-report + outbox report (body 7fa1b53a) - 0 production code; state THẬT giữ nguyên (Mig 55 · 88 bảng · 339 test · gotcha 69 · menu 54 · bundle BYF5vIMJ/CB-tiRxd) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
13 KiB
sub-task-0 — scripts/governance-detectors.ps1 (Harness-11 PHẦN C + B3)
Lane impl-detectors · owner general-purpose (Write+Bash) · single-writer: CHỈ
scripts/governance-detectors.ps1+ file này. NO-API · DÒ+FLAG-only · PowerShell 5.1 · RUNTIME-proven (output thật dán §3).
1. Deliverable
D:\Dropbox\CONG_VIEC\SOLUTION\SOLUTION_ERP\scripts\governance-detectors.ps1 (~355 dòng, ASCII-only body).
3 bộ dò + self-exclusion + summary. Param $RepoRoot (default = Resolve-Path $PSScriptRoot\..). Exit-code 0 luôn (dò-only, KHÔNG fail-build). Mỗi FLAG: [DETECTOR] severity | file:line | desc | resolve: <điều-kiện-gỡ-cờ> (C5).
2. Thiết-kế từng detector
C2/B3 — derived-staleness (ưu-tiên, value cao nhất) ✅
- Canonical đọc từ
docs/STATUS.mdCURRENT STATE table qua regex^\|\s*<label>\s*\|\s*\*\*(\d+)(số trong**N**đúng row Migrations/Tests/Gotchas/SQL tables). Runtime đọc đúng: mig=55 test=339 gotcha=69 table=88. - Cross-check disk (canonical tự-nó không stale):
mig= đếm*.cstrong mọi dir tênMigrationsdướisrc(excludebin|obj|node_modules+*Designer.cs/*ModelSnapshot.cs). Recursive-search vì migration thật ởsrc\Backend\SolutionErp.Infrastructure\**Persistence**\Migrations (SPEC ghi gần-đúng...\Migrations` — đã xử robust). Runtime: disk mig=55.gotcha= max N từ^### (\d+)\.trongdocs/gotchas.md. Runtime: disk gotcha=69.- STATUS-value ≠ disk-value → FLAG
canonical-itself-stale(HIGH). Lần này KHỚP → 0 HIGH (baseline an-toàn).
- Derived scan 5 file:
CLAUDE.md(root) ·docs/CLAUDE.md·.claude/skills/ef-core-migration/SKILL.md·.claude/skills/README.md·.claude/skills/dependency-audit-erp/SKILL.md. Count-token regex:(\d+)\s*migration·(\d+)\s*test·(\d+)\s*(?:<bay>|gotcha)·(\d+)\s*(?:<bang>|table). Lệch canonical → FLAGderived-stale(MED nếu |lệch|≥10, else LOW).
C1 — broken-pointer ✅
- (a) gotcha-ref: grep
docs/** + .claude/** *.mdbắtgotcha[s]?\s*#?(\d+)+ bare#(\d+).gotcha #Nluôn validate (N>max → broken MED; thiếu### N.anchor → LOW). Bare#Nchỉ xét trong range;#N>max bỏ qua (tránh nhầm Run #312/PR #). Runtime: 0 flag — mọi gotcha-ref ≤69 và có anchor (đúng — repo sạch khoản này). - (b) wikilink: scan user-memory
C:\Users\pqhuy\.claude\projects\...\memory\*.md+ in-repo.claude/agent-memory/**. Bắt\[\[([a-z0-9_-]+)\]\]; target<name>.mdkhông tồn tại trong scope → dangling. User-memory KHÔNG reachable → note + agent-memory-only (lần này user-memory REACHABLE, 29 file).
C3 — vocab-fork ✅
- Seed alias-set:
@('wave-folder','run-trace'),@('<Du tru PRO>','<Ngan sach PRO>'),@('two-tier','all-inherit'). Mỗi set đếm file dùng từng biến-thể; ≥2 biến-thể CÙNG sống → FLAG count + sample | resolve gộp/alias-map.
C4 — self-line exclusion (BẮT BUỘC) ✅
- Exact:
scripts/governance-detectors.ps1,docs/governance/harness-11-engine.md. Dir-fragment:\broadcasts\inbox\,\broadcasts\outbox\,\.claude\workflows\runs\,\.claude\workflows\scripts\.Test-Excludedáp MỌI scan. Summary inself-exclusion: N paths excluded+ assertself in scan=0+leaked=0.
🔴 Encoding-robustness (gotcha #30 — phát-hiện lúc RUNTIME, đã FIX)
Vòng-1 detector MISS CLAUDE.md:133 "68 bẫy" + cả set vocab-fork Dự trù PRO↔Ngân sách PRO. Root-cause: file .ps1 ghi UTF-8 KHÔNG BOM (Write-tool); powershell.exe -File ở PS 5.1 decode file no-BOM bằng codepage ANSI (1252), KHÔNG phải UTF-8 → literal tiếng Việt bẫy/bảng/Dự trù bị mojibake → KHÔNG match content UTF-8 đọc đúng (Get-Content -Encoding UTF8). FIX: body ASCII-only, mọi token tiếng Việt build từ Unicode code-point runtime qua helper U @(0x62,0x1EAB,0x79) → encoding-độc-lập. Sau fix: 71 flag (vòng-1 chỉ 53 — thiếu 18 do mojibake). Đây là minh-chứng giá-trị của mandate "viết xong PHẢI chạy thật".
3. RUNTIME — output thật (powershell.exe -ExecutionPolicy Bypass -File scripts/governance-detectors.ps1)
Exit code: 0 · TOTAL FLAGS: 71 (HIGH=0 · MED=33 · LOW=38).
===== C2/B3 - canonical resolve + disk cross-check =====
STATUS.md canonical: mig=55 test=339 gotcha=69 table=88
disk cross-check: mig=55 gotcha=69
[OK] canonical matches disk (mig + gotcha) - safe baseline for derived scan
===== C2/B3 - derived-doc staleness =====
[DETECTOR] LOW | CLAUDE.md:53 | derived-stale: writes 53 migration but canonical=55 | resolve: update to 55 OR replace with pointer '-> docs/STATUS.md' <== TRUE drift (root CLAUDE mig)
[DETECTOR] MED | CLAUDE.md:53 | derived-stale: writes 53 gotcha/bay but canonical=69 | resolve: ... <== FALSE-POS ("53" la mig-number, dinh token 'bay'? -> thuc te dong 53 KHONG co 'bay'; xem honesty #2)
[DETECTOR] MED | CLAUDE.md:66 | derived-stale: writes 306 test but canonical=339 | resolve: ... <== TRUE drift (root CLAUDE test)
[DETECTOR] MED | CLAUDE.md:80 | derived-stale: writes 45 test but canonical=339 <== FALSE-POS (45 = Domain breakdown)
[DETECTOR] MED | CLAUDE.md:81 | derived-stale: writes 261 test but canonical=339 <== FALSE-POS (261 = Infra breakdown)
[DETECTOR] MED | CLAUDE.md:84 | derived-stale: writes 6 test but canonical=339 <== FALSE-POS
[DETECTOR] MED | CLAUDE.md:90 | derived-stale: writes 2 test but canonical=339 <== FALSE-POS ('2 test project')
[DETECTOR] LOW | CLAUDE.md:133 | derived-stale: writes 68 gotcha/bay but canonical=69 | resolve: update to 69 ... <== TRUE drift (root CLAUDE '68 bay') -- VONG-1 BI MISS, sau fix encoding moi bat
[DETECTOR] MED | docs/CLAUDE.md:13 | derived-stale: writes 4 table/bang but canonical=88 <== FALSE-POS ('4 bang Budget' module-local)
[DETECTOR] LOW | docs/CLAUDE.md:70 | derived-stale: writes 93 table/bang but canonical=88 | resolve: update to 88 ... <== TRUE drift (ERD '93 bang')
[DETECTOR] MED | docs/CLAUDE.md:123 | derived-stale: writes 4 table/bang but canonical=88 <== FALSE-POS (Phase 7 '4 bang Budget')
[DETECTOR] MED | docs/CLAUDE.md:124 | derived-stale: writes 71 test but canonical=339 <== FALSE-POS (Phase 8 historical '71 test')
[DETECTOR] MED | .claude/skills/ef-core-migration/SKILL.md:3 | writes 10 migration <== FALSE-POS ('.NET Core 10 migration')
[DETECTOR] LOW | .claude/skills/ef-core-migration/SKILL.md:3 | writes 53 migration <== TRUE drift
[DETECTOR] LOW | .claude/skills/ef-core-migration/SKILL.md:19 | writes 53 migration <== TRUE drift
[DETECTOR] MED | .claude/skills/ef-core-migration/SKILL.md:34..115 | writes 1/3/4/10 table/bang <== FALSE-POS (per-migration '<n> bang module ...')
[DETECTOR] MED | .claude/skills/ef-core-migration/SKILL.md:42/86/87/154/171 | writes 1/2 migration <== FALSE-POS (mig list seq number)
[DETECTOR] MED | .claude/skills/ef-core-migration/SKILL.md:107 | writes 58/96/154 test <== FALSE-POS (per-project breakdown)
[DETECTOR] LOW | .claude/skills/ef-core-migration/SKILL.md:285 | writes 53 migration <== TRUE drift
[DETECTOR] MED | .claude/skills/README.md:20 | writes 10 migration <== FALSE-POS ('.NET Core 10')
[DETECTOR] LOW | .claude/skills/README.md:20 | writes 53 migration <== TRUE drift
[DETECTOR] LOW | .claude/skills/README.md:90 | writes 68 gotcha/bay <== TRUE drift ('68 bay')
[DETECTOR] LOW | .claude/skills/dependency-audit-erp/SKILL.md:153 | writes 68 gotcha/bay <== TRUE drift ('68 bay')
(note: count-token grep is a soft net - module-local phrases like "4 bang Budget" / "71 test (Phase 8)" can false-positive; treat LOW sev as review-not-fail)
===== C1 - broken gotcha-ref =====
(no flags -- all gotcha #N refs <= 69 and anchored; bare #N>max skipped to avoid Run#/PR# noise)
===== C1 - dangling wikilink =====
[DETECTOR] LOW | user-memory/<29 file>:* | dangling-wikilink: [[<hyphen-form>]] -> <name>.md not found in user-memory | resolve: fix link / create file (hyphen-vs-underscore fork)
... 21 user-memory dangling (vd [[feedback-implementer-truncation-mitigation]] -- file that la feedback_implementer_truncation_mitigation.md _underscore_) ...
[DETECTOR] LOW | agent-memory/pattern_*.md:* | [[pattern-...-hyphen]] -> not found ... 8 agent-memory dangling (6 hyphen-fork + 2 trong 2026-06.gist.md tro feedback_* underscore khac-scope) ...
===== C3 - vocab-fork =====
[DETECTOR] MED | multiple files | vocab-fork: wave-folder=15f vs run-trace=18f live side-by-side -- 'wave-folder' in [docs/HANDOFF.md, docs/STATUS.md] | 'run-trace' in [docs/HANDOFF.md, docs/STATUS.md] | resolve: merge / alias-map
[DETECTOR] MED | multiple files | vocab-fork: Du tru PRO=7f vs Ngan sach PRO=6f live side-by-side -- in [docs/HANDOFF.md, docs/STATUS.md] | resolve: merge / alias-map <== VONG-1 BI MISS (mojibake), sau fix moi bat
[DETECTOR] MED | multiple files | vocab-fork: two-tier=17f vs all-inherit=10f live side-by-side -- in [docs/HANDOFF.md, docs/STATUS.md] | resolve: merge / alias-map
===== Summary =====
self-exclusion: 5 paths excluded (exact+dir rules)
- excluded: scripts/governance-detectors.ps1
- excluded: docs/governance/harness-11-engine.md
- excluded: broadcasts/inbox
- excluded: broadcasts/outbox
- excluded: .claude/workflows/runs
self-match check: governance-detectors.ps1 in scan = 0 ; leaked excluded files in scan = 0
[OK] 0 self-match (C4 satisfied)
TOTAL FLAGS: 71
NOTE: DETECT-only lowering net. Exit 0 always (never fails build). FLAGs are advisory.
=== EXIT CODE: 0 ===
(Output trên rút-gọn các block lặp dài; con-số tally + dòng load-bearing y-nguyên run thật. Full 71 dòng có trong stdout.)
4. Acceptance — verdict (số đo)
| Mục SPEC | Verdict | Bằng-chứng (file:line từ output thật) |
|---|---|---|
| (i) C2/B3 FLAG drift root CLAUDE.md mig 53 vs 55 | ✅ PASS | CLAUDE.md:53 ... 53 migration but canonical=55 |
| (i) gotcha 68 vs 69 | ✅ PASS | CLAUDE.md:133 ... 68 gotcha/bay but canonical=69 (sau encoding-fix) + README.md:90 + dep-audit/SKILL.md:153 |
| (i) test 306 vs 339 | ✅ PASS | CLAUDE.md:66 ... 306 test but canonical=339 |
| (ii) C3 FLAG vocab-fork wave↔run-trace | ✅ PASS | wave-folder=15f vs run-trace=18f live side-by-side |
| (iii) self-exclusion 0 self-match | ✅ PASS | governance-detectors.ps1 in scan = 0 ; leaked = 0 ; [OK] C4 satisfied |
| Exit-code 0 (dò-only) | ✅ PASS | === EXIT CODE: 0 === |
| NO-API | ✅ PASS | Chỉ Select-String/Get-Content/Get-ChildItem/regex. grep script: 0 hit `Invoke-WebRequest |
| PS 5.1 parse | ✅ PASS | PSParser.Tokenize 0 error |
| Bonus drift bắt thêm | ℹ️ | docs/CLAUDE.md:70 93 bảng (canon 88) + vocab-fork thứ-3 Dự trù PRO=7f vs Ngân sách PRO=6f (rename S65 còn 2 tên sống) |
5. C5 resolve-condition — đủ mọi FLAG
derived-stale → "update to M OR replace with pointer '-> docs/STATUS.md'" · canonical-itself-stale → "re-ground STATUS.md to " · broken-gotcha-ref → "fix number or add gotcha to docs/gotchas.md" · dangling-wikilink → "fix link target or create file" · vocab-fork → "merge to ONE canonical term, or record alias-map".
6. Honest limitations (LƯỚI giảm-sót, KHÔNG khoá-cứng — đừng over-claim)
- C2/B3 count-token = soft net, FALSE-POSITIVE thật: trong 41 derived-flag, ~12 TRUE drift / ~29 FP. Nguồn FP:
.NET Core 10 migration(version), per-project test breakdown (45/261/58/96), migration list seq number (1 migration), module-local4 bảng Budget, Phase-historical71 test. Mitigation: NOTE cảnh-báo + sev LOW khi |lệch|<10. KHÔNG lọc cứng theo ngữ-cảnh (giòn). Người xử FLAG, KHÔNG auto-fix. TRUE-signal (53 mig · 306 test · 68 bẫy · 93 bảng) ĐỀU có trong list → runtime-proof ĐẠT. CLAUDE.md:53 "53 gotcha/bay"= FP do regex tham: token(\d+)\s*(?:bẫy|gotcha)bắt "53" rồi nuốt khoảng-trắng dài tới chữ "gotcha" xa hơn trên cùng dòng (dòng 53 dài, có chữgotcha #63/#64). → over-match. Đây là giới-hạn regex flat-line, GHI RÕ. Dòng 53 thật-sự stale ở "53 migration" (đã bắt riêng, đúng).- bare
#Ngotcha-ref cố-ý bỏ qua khi N>max (tránh nhầm Run #312/PR #) → có thể MISS broken-ref dạng#999thật. Trade-off ít-noise. - agent-memory
2026-06.gist.md2 dangling trỏ file user-memory (khác-scope) → "dangling trong agent-memory" kỹ-thuật đúng nhưng có thể cross-scope cố-ý. GHI nhận, không khẳng định tuyệt-đối broken. - user-memory path hard-code theo slug máy này; máy/CI khác → tự fallback agent-memory-only + note (đã code; chưa test máy khác).
- vocab-fork sample chỉ in 2 file đầu mỗi biến-thể (gọn output); muốn full-list cần mở rộng — chấp-nhận cho lần này.
7. Single-writer compliance
CHỈ ghi 2 file: scripts/governance-detectors.ps1 + sub-MD này (runs/2026-06-18-h11-implement/sub-task-0.md). KHÔNG đụng canonical MD (CLAUDE/README/STATUS/agents) · KHÔNG agent-memory · KHÔNG sub-MD khác · KHÔNG store_memory/RAG/HTTP/Qdrant. Detector tự-nó KHÔNG ghi file nào (FLAG ra stdout only) — auto-WRITE luật = mối-nguy #1, đã tránh tuyệt-đối.