Compare commits
62 Commits
c98030f27c
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| f0e616fd5a | |||
| 7886fd03dd | |||
| 095fb492cd | |||
| e823694202 | |||
| 424131d0b1 | |||
| fa6654b8f4 | |||
| b5aa72d005 | |||
| e29391ec9e | |||
| e42d103694 | |||
| 94e0e12f77 | |||
| 3b98845976 | |||
| 398b01d0a6 | |||
| 8e68ed1892 | |||
| 8f780b6237 | |||
| 21d1f4ec43 | |||
| e33481efb6 | |||
| 70c13d4ac8 | |||
| aa09e99061 | |||
| ae957c4e35 | |||
| e70c0462d7 | |||
| 462bfbc854 | |||
| 8655ebf1ba | |||
| e7e99d10f2 | |||
| 6aa4dcb525 | |||
| 1d86abcdc5 | |||
| 77ad219361 | |||
| 39d55d4402 | |||
| 18fced6695 | |||
| 4e4b5d47d1 | |||
| 7875b39861 | |||
| 7436803db4 | |||
| 8c47bd0f0c | |||
| f3ad1a2ae0 | |||
| c3ee6cb326 | |||
| f36aab8934 | |||
| 9941e352bc | |||
| ebd7e1c42f | |||
| 1f8947e763 | |||
| c556f6cfa2 | |||
| a8bbdaeeea | |||
| 764fe7024b | |||
| 37752eb914 | |||
| 6983609c7b | |||
| 11bc96dff1 | |||
| 231a7b0c36 | |||
| 536dd6b569 | |||
| 6df1b2d7c1 | |||
| 91aaf058fb | |||
| bcd619dbdb | |||
| 292d64d843 | |||
| ab4e681e8e | |||
| 53f1d29e44 | |||
| 4e09413fdb | |||
| c34bb51f71 | |||
| fe28ca3993 | |||
| 456c7a721b | |||
| ec517f7174 | |||
| 5a0aaa4e83 | |||
| 318860a38e | |||
| 6ce5803bfa | |||
| 8c8179cda0 | |||
| 0f44d9754d |
File diff suppressed because one or more lines are too long
59
.claude/agent-memory/cicd-monitor/archive/2026-05.gist.md
Normal file
59
.claude/agent-memory/cicd-monitor/archive/2026-05.gist.md
Normal file
@ -0,0 +1,59 @@
|
||||
---
|
||||
distill-gen: 1
|
||||
period: 2026-05 (S21→S37)
|
||||
source-verbatim: archive/2026-05-q2.md (1) · 2026-05-q3.md (3) · 2026-05-q4.md (7) · 2026-05-runs.md (20 run-records + 6 [meta] curate-headers)
|
||||
pointer-style: substring (Ctrl-F) back-resolve; each line names its FILE then → substring:"<unique>"
|
||||
fields-per-line: VIỆC · KẾT-LUẬN(+commit/Mig) · BÀI-HỌC · BẤT-NGỜ · [confidence]
|
||||
note: compression is a REPORTED number, not a target. Markers tagged ‹M#›. Even-older verbatim → git d2f52ba.
|
||||
---
|
||||
|
||||
# CI/CD Monitor — GIST 2026-05 (4-field distilled)
|
||||
|
||||
> Distilled from the four 2026-05 archive files. Open the named file + Ctrl-F the trailing substring for the full block. `‹M#›` = checklist-marker survival tag.
|
||||
|
||||
## ★ gotcha #48 SQLite tie-break (the critical catch+fix pair) — q3 + runs.md
|
||||
|
||||
- VIỆC ‹M1›: Run #215 FAIL → #216 PASS pair (S25 t1-t2, Plan AB changelog visibility refactor). KẾT-LUẬN ‹M1›: #215 `cdfd542` FAIL at test_infra (51/53) — 2 ReturnMode tests `Expected changelog.ContextNote not to be <null>`; #216 `8c05947` PASS. ROOT CAUSE: `ApplyReturnModeAsync` adds NEW Changelog (EntityType=Workflow, NO ContextNote) in same SaveChanges as LogTransitionAsync (has ContextNote); test `.OrderByDescending(c=>c.CreatedAt).First()` with **SQLite + frozen test-clock → both rows SAME CreatedAt → tie-break returns WRONG (Workflow) entry**. FIX: add `.Where(c=>c.Summary!.Contains("Chuyển phase"))` discriminator BEFORE OrderByDescending (Plan AB code NOT reverted). BÀI-HỌC ‹M1›: **discriminator filter BEFORE OrderBy on frozen-clock ties**; UAT skip-test risky for >100-LOC BE refactor — CI caught BEFORE prod. BẤT-NGỜ: 8-min turnaround; test gate spared prod broken audit-trail. [cao] → file: archive/2026-05-q3.md · substring:"Run #215 FAIL → Run #216 PASS (gotcha #48 SQLite tie-break catch+fix pair)"
|
||||
- VIỆC ‹M1›: [meta] curate-note for #215 (S25 t1 Plan AB FAIL). KẾT-LUẬN: records the gotcha #48 birth + 3 fix approaches. BÀI-HỌC ‹M1›: multi-Changelog.Add() in one txn + OrderByDescending(CreatedAt) tie risk in SQLite frozen-clock. BẤT-NGỜ: none (curate log). [vừa] → file: archive/2026-05-runs.md · substring:"added S25 t1 Plan AB Run #215 entry"
|
||||
|
||||
## Per-NV opt-in wire — the 10-point checklist births (S23-S25) — runs.md
|
||||
|
||||
- VIỆC: Run #194 (S23 t1 Plan K, Mig 31 per-Approver-slot refactor). KẾT-LUẬN: **PARTIAL** `098baa6` — Mig 31 + schema OK BUT K3 DTO-mirror INCOMPLETE: `AwLevelDto` missing `AllowApproverSkipToFinal` (only in a comment, not a record-param) + `ToDto` ctor missing it → FE Designer 7th checkbox ships but BE never returns value → no round-trip. BÀI-HỌC: schema-OK ≠ wired; check Application DTO + Handler expose the new column. BẤT-NGỜ: 7th-checkbox silently no-op. [cao] → file: archive/2026-05-runs.md · substring:"Run #194 id=308 sha=`098baa6`"
|
||||
- VIỆC: Run #195 (S23 t1 K10 hotfix). KẾT-LUẬN: PASS `0062fcb` — AwLevelDto +AllowApproverSkipToFinal + ToDto + CreateAwLevelInput + handler init. BÀI-HỌC: **8-surface-point checklist** (AwLevelDto + CreateAwLevelInput were the 2 gaps em-main+Reviewer missed). BẤT-NGỜ: round-trip needs 4 edits across DTO/Input/Handler. [cao] → file: archive/2026-05-runs.md · substring:"Run #195 id=309 sha=`0062fcb`"
|
||||
- VIỆC: Run #201 (S23 t4 Plan N HOTFIX). KẾT-LUẬN: PASS `fb3c22c` — per-NV lookup discrimination at `PurchaseEvaluationFeatures.cs:765` (`FirstOrDefault(Order==X && ApproverUserId==actor) ?? admin-fallback`). Live: NV-Test 7/7 TRUE vs admin 1/7 (fallback). BÀI-HỌC: **surface-point 9 = lookup discrimination in lookup-site handler**; bug 2 days prod silent (Mig 29 deploy → S23 t4 catch). BẤT-NGỜ: 3× refactor (Mig 29/30/31) all missed point 9. [cao] → file: archive/2026-05-runs.md · substring:"Run #201 id=315 sha=`fb3c22c`"
|
||||
- VIỆC: Run #202 (S23 t5 Plan O HOTFIX). KẾT-LUẬN: PASS `a1c8386` — cascade 4 lookup-sites same pattern (Service:204, :258-260, DetailFeatures:75-76, Features:314-315). Live: NV-Test POST transition → **409 mode-mismatch NOT 403 actor-mismatch = discrimination active**. BÀI-HỌC: scan ALL `grep -n "FirstOrDefault.*Order.*==" *.cs` after OR-of-N refactor; surfaced pre-existing TransitionPeBody schema gap. BẤT-NGỜ: 3-day prod-bug latency. [cao] → file: archive/2026-05-runs.md · substring:"Run #202 id=316 sha=`a1c8386`"
|
||||
- VIỆC: Run #203 (S23 t6 Plan P HOTFIX). KẾT-LUẬN: PASS `1727bd5` — Controller `TransitionPeBody` record +3 fields (ReturnMode/ReturnTargetUserId/SkipToFinal). Live: admin numeric `4` → 204; string `"Drafter"` → 400. BÀI-HỌC: **point-10 = Controller body record param-count MUST mirror Command record**; **Discovery#4 = ASP.NET10 record-with-enum needs NUMERIC input (no JsonStringEnumConverter registered)**. BẤT-NGỜ: missing body fields silently `default` (no 400) → FE wire dead 2 prod days. [cao] → file: archive/2026-05-runs.md · substring:"Run #203 id=317 sha=`1727bd5`"
|
||||
- VIỆC: Run #200 (S23 t3 Plan M, Service F1 OneLevel/OneStep edge-case Bước1 reset). KẾT-LUẬN: PASS `f4055a1`, Mig31 unchanged. BÀI-HỌC ‹M3›: **Discovery#3 anomaly — docs-only TIP `f4055a1` STILL triggered CI** (reinforces push-RANGE eval not just tip). BẤT-NGỜ: anomaly is BENEFICIAL (catches verify on docs commits). [cao] → file: archive/2026-05-runs.md · substring:"Run #200 id=314 sha=`f4055a1`"
|
||||
|
||||
## S25 FE audit-recovery + S24 filter/menu (runs.md)
|
||||
|
||||
- VIỆC: Run #221 (S25 t7 Plan AF FE userMap fallback). KẾT-LUẬN: PASS `506cada`, bundle rotate `C8TvDy7r`/`BvcWrq2z`. BÀI-HỌC: FE-only fallback Map<userId,name> from embedded bundle (drafter+approvals+approvers+opinions); **Discovery: 503 mid-deploy expected (app-pool recycle ~5-15s, self-recovers); auth route `/api/auth/login` field `accessToken` NOT `token`**. BẤT-NGỜ: /api/v1/auth/login = 404. [cao] → file: archive/2026-05-runs.md · substring:"Run #221 id=335 sha=`506cada`"
|
||||
- VIỆC: Run #220 (S25 t6 Plan AE Changelog UserName 9-sites batch). KẾT-LUẬN: PASS `9ea62be`, BE-only bundle FROZEN. BÀI-HỌC: preventive batch-fix ALL Changelog.Add() sites (PE=9), not just the 1 reported; `UserName = currentUser.FullName ?? Email`. BẤT-NGỜ: stale empty-userName rows are pre-fix history (forward-only fix). [vừa] → file: archive/2026-05-runs.md · substring:"Run #220 id=334 sha=`9ea62be`"
|
||||
- VIỆC: Run #219 (S25 t5 Plan AD) + #218 (S25 t4 Plan AC2). KẾT-LUẬN: both PASS (`0aaf2df`, `25837b6`), bundles rotate. BÀI-HỌC: FE synthetic-rows from Changelog entityType=5 + ContextNote keyword; parse comment for semantic next-target hint (reusable Contract/Budget audit recovery). BẤT-NGỜ: dual-phase badges mostly self-loop noise (ChoDuyet→ChoDuyet). [vừa] → file: archive/2026-05-runs.md · substring:"Run #219 id=333 sha=`0aaf2df`"
|
||||
- VIỆC: Run #210 (S24 t1 Plan AA, IsUserSelectable filter + Pe_DuyetNcc_WfView menu + sidebar). KẾT-LUẬN: PASS `ac2c859`, 4/4 wire, bundle rotate `Dmk--X6w`/`Bd4gh3Tp`. BÀI-HỌC: new-filter-param + new-menu-key + Order-shift-idempotent + **non-admin Read 200 NOT 403** (gotcha #44 any-auth read). BẤT-NGỜ: none. [vừa] → file: archive/2026-05-runs.md · substring:"Run #210 id=324 sha=`ac2c859`"
|
||||
- VIỆC: Run #214 (Plan AA wrap fix sidebar), #209 (Plan U truncate), #208/#207 (Plan T DemoSeed disable + wipe ~720 rows), #204 (Plan Q FE banner). KẾT-LUẬN: all PASS (`ee0902a`,`86d8806`,`7b7b28f`,`0b97840`,`108268a`). BÀI-HỌC: **DemoSeed:Disabled flag gates 5 demo-seed methods (NOT infra seed) — proven end-to-end via force-recycle no-reseed**; #214 = baseline before Plan AB fail. BẤT-NGỜ: appsettings.Production.json is .gitignore'd → flag default committed in appsettings.json. [vừa] → file: archive/2026-05-runs.md · substring:"Run #207 sha=`0b97840`"
|
||||
|
||||
## Foundation + Discovery births (S21-S22) — runs.md + q3
|
||||
|
||||
- VIỆC ‹M3›: S22-chốt cumulative verify. KẾT-LUẬN: PASS, 104 test, Mig30. BÀI-HỌC ‹M3›: **Discovery#3 push-range eval — `b079b27`(BE Mig30)+`b04a11a`(FE) pushed batched → single Run on TIP**; #190 CANCELLED by concurrency-supersede (next push within 3min) = NORMAL not fault. BẤT-NGỜ: `**/*.md` glob matches `.claude/agent-memory/*.md` at any depth → those commits DO skip (gotcha #47 disproven for .md, kept preventive for non-.md state files). [cao] → file: archive/2026-05-runs.md · substring:"Verify S22 chốt cuối cumulative (push range `3d725c4..cc8a7d3` 12 commits) VERDICT=PASS"
|
||||
- VIỆC: Run #188 (S22 Plan D/C/E). KẾT-LUẬN: PASS `a74e671`, 103 test, Plan E strict-V2-scope (nv.test 8 < admin 17). BÀI-HỌC: **Discovery#1 rate-limit 429 at ~5 login/min → cache token across endpoints**; **Discovery#2 `.claude/agent-memory/**` NOT in paths-ignore** (later refined — `**/*.md` still catches .md). BẤT-NGỜ: token-cache pattern avoids 429. [vừa] → file: archive/2026-05-runs.md · substring:"Run #188 id=302 sha=a74e671"
|
||||
- VIỆC: Run #187 (S21 t5 Mig 29 Allow*-per-NV refactor + Designer 5-checkbox). KẾT-LUẬN: PASS `c0af9e0`, Mig29 applied (Levels +5 Allow*, Users +1, Workflows −6 dropped). BÀI-HỌC: **Discovery — Gitea task-table `updated_at` stale ~2min → cross-check VPS file mtime** (gotcha #46). BẤT-NGỜ: backfill 48/48 Levels correct, 0/13 Users (preserve). [vừa] → file: archive/2026-05-runs.md · substring:"Run #187 id=301 sha=c0af9e0"
|
||||
- VIỆC: Run #186 (S21 t3+t4 gotcha#45 Trả-lại + F1/F2/F3 advanced-options + Mig 28). KẾT-LUẬN: PASS `eea86fd` baseline 3m32s, 84 test. BÀI-HỌC: **Discovery — Gitea Actions list endpoint = `/api/v1/repos/.../actions/tasks` NOT `/actions/runs` (404); public no-auth read OK**. BẤT-NGỜ: none. [cao] → file: archive/2026-05-runs.md · substring:"Run #186 id=300 sha=eea86fd"
|
||||
- VIỆC: Setup baseline (agent init) + Verify-S22 (q3 copy, Discovery#3 first-surfaced). KẾT-LUẬN: init/PASS. BÀI-HỌC: 5-stage checklist + bundle-hash-verify pattern established; S22-verify also lives in q3 (cross-file dup with runs.md — keyed distinctly). BẤT-NGỜ: none. [thấp] → file: archive/2026-05-q3.md · substring:"2026-05-12 — Setup baseline"
|
||||
|
||||
## Plan B Contract V2 + Hrm/Office (S29-S37) — q2 + q4
|
||||
|
||||
- VIỆC ‹M2›: Run #231 Plan B Contract V2 wire kick-off. KẾT-LUẬN: **PARTIAL** `3e92584` — PASS deploy (Mig 32+33 applied, bundles rotate) + seed-gap: `SeedSampleContractWorkflowV2` nested in DemoSeed gate → QT-HD-V2-001 NOT seeded → FE dropdown EMPTY → V2 not UAT-testable. Resolved Run #232 (`38f1c4d`, carve out of gate). BÀI-HỌC ‹M2›: **gotcha #51 birth — infra seed must not sit under demoSeedDisabled**. BẤT-NGỜ: ~150 LOC ApproveV2Async shipped with 0 test (gotcha #48 high-risk pattern). [cao] → file: archive/2026-05-q2.md · substring:"Run #231 (id=345) Plan B Contract V2 wire kick-off VERDICT=PARTIAL"
|
||||
- VIỆC ‹M2›‹M22›: Run #350 (S33 Mig 34 EmployeeProfile + Plan C BW1-BW7 +9 test). KẾT-LUẬN: PASS `48a99e1`, test_infra 62. BÀI-HỌC ‹M2›: **gotcha #51 INFRASTRUCTURE-seed verify — SeedDemoEmployeeProfilesAsync correctly NOT gated; EmployeeProfiles=33 (16 demo+14 Solutions+3 admin)**. BẤT-NGỜ ‹M22›: **mem-labeled "#350" ≠ real Gitea run_number** (this is a memory-id; reconcile via run_number). [cao] → file: archive/2026-05-q4.md · substring:"Run #350 (S33 Plan B G-H1 Mig 34 EmployeeProfile"
|
||||
- VIỆC: Run #237 (Hrm CQRS /api/employees + FE×2) + #238 (Danh-bạ /api/directory G-O1). KẾT-LUẬN: both PASS (`79a8343`,`ea440da`), bundles rotate. BÀI-HỌC: **BE namespace mới `SolutionErp.Application.Office` → MediatR auto-discovery WORKS (no manual registration, no gotcha #1)**; menu-seed Off/Off_DanhBa + Permissions auto-grant. BẤT-NGỜ: directory 34 rows @solutions.com.vn populated. [vừa] → file: archive/2026-05-q4.md · substring:"Run #238 (S34 Plan 2 G-O1 Danh bạ nội bộ"
|
||||
- VIỆC: Run #222-#227 (S26 Plan AG PE-List tree-view UI iteration). KẾT-LUẬN: PASS `0bf6c7e`, bundle rotate. BÀI-HỌC: **hybrid-verify — spawn CICD-monitor 1× at Phase-wire start (ROI good for solo-dev iteration); polish chunks em-main self-verify (avoid re-spawn ~150K × N)**. BẤT-NGỜ: none. [vừa] → file: archive/2026-05-q4.md · substring:"Run #222-#227 cumulative — Plan AG series PE List tree view"
|
||||
- VIỆC: S33 startup health-check [audit] + S32 wrap [meta] + S32 startup [audit]. KẾT-LUẬN: HEALTHY/no-run. BÀI-HỌC: **Discovery#7 — `eval/**` MISSING from paths-ignore → RAG eval-JSON commits trigger wasteful ~3m30s deploy**; cert api.solutions.com.vn notAfter 2026-07-23 (auto-renew ~06-23). BẤT-NGỜ: RAG-healthy 2949 chunks. [thấp] → file: archive/2026-05-q4.md · substring:"S33 startup health-check — em main spawn read-only verify, VERDICT=HEALTHY"
|
||||
|
||||
## Curate-note meta-headers (the remaining 5 [meta]) — runs.md
|
||||
|
||||
- VIỆC: 5 S40-curate notes (S23 t5 #202, t6 #203, S24 t1 #210, S25 t3 #217, t5 #219). KẾT-LUẬN: [meta] curate logs (thin — point to the rich run-record above). BÀI-HỌC: #217 note carries **Discovery#5 — sqlcmd Windows-auth over ssh needs `\\\\SQLEXPRESS` 4-backslash** (`\\SQLEXPRESS` = 0 silent output). BẤT-NGỜ: none. [thấp] → file: archive/2026-05-runs.md · substring:"added S25 t3 Plan AC Run #217 entry"
|
||||
|
||||
## ‹M7› ‹M19› cross-period markers (live in L1 foundation + spanning)
|
||||
|
||||
- VIỆC ‹M7›: gotcha #25 IIS WebSocket. KẾT-LUẬN: `notification-hub/negotiate` 401/404 prod → fix = enable WebSocket module in `web.config` site api (skill iis-deploy-runbook). BÀI-HỌC ‹M7›: SignalR negotiate failure = IIS WebSocket module not enabled. BẤT-NGỜ: this pattern lives in L1 MEMORY.md foundation (Stage 4), not in a dated 2026-05 record — recorded here for marker survival. [cao] → file: (L1) MEMORY.md · substring:"notification-hub/negotiate"
|
||||
- VIỆC ‹M19›: BUNDLE-HASH lineage chain admin/user. KẾT-LUẬN: rotate chain spans #247→#308 across both gist periods; each record's bundle-hash transition is load-bearing ship-proof. BÀI-HỌC ‹M19›: every bundle-hash transition MUST survive — admin/user FE hashes are the canonical ship-evidence (e.g. early `CzesdXLh`/`DP-gH4LW` → … → live `BgNCjwsG`/`CBvh0vtf`). BẤT-NGỜ: prod CI hash ≠ local-build hash is NORMAL (CI rebuild). [cao] → file: archive/2026-05-runs.md · substring:"Run #186 id=300 sha=eea86fd"
|
||||
65
.claude/agent-memory/cicd-monitor/archive/2026-06.gist.md
Normal file
65
.claude/agent-memory/cicd-monitor/archive/2026-06.gist.md
Normal file
@ -0,0 +1,65 @@
|
||||
---
|
||||
distill-gen: 1
|
||||
period: 2026-06 (+ tail of late-05 runs that live in 2026-06.md)
|
||||
source-verbatim: archive/2026-06.md (39 records, FROZEN — do NOT re-compress this gist)
|
||||
pointer-style: substring (Ctrl-F) back-resolve into archive/2026-06.md; each line ends → substring:"<unique>"
|
||||
fields-per-line: VIỆC · KẾT-LUẬN(+commit/sys-truth) · BÀI-HỌC · BẤT-NGỜ · [confidence]
|
||||
note: compression is a REPORTED number, not a target — every marker below is preserved in full-enough words to re-identify. Markers tagged ‹M#›.
|
||||
---
|
||||
|
||||
# CI/CD Monitor — GIST 2026-06 (4-field distilled, read before opening verbatim)
|
||||
|
||||
> Distilled from `archive/2026-06.md`. Open that file + Ctrl-F the trailing substring to read the full run-record. `‹M#›` = checklist-marker survival tag (Stage-C gate, 22 total).
|
||||
|
||||
## PE cross-stack + budget (S60-S62)
|
||||
|
||||
- VIỆC: PE "vượt ngân sách" soft-warn, PeWorkItemBudgets per-gói-thầu (Mig 50 replaces Budget module). KẾT-LUẬN: PASS, commit `7926c21`; **sys.tables 93→88** post-S61 Budget-drop, `sys.tables(is_ms_shipped=0, excl mighist)=88` correct. BÀI-HỌC: when commit touches no schema, 88 is right — don't FAIL on the 88-vs-93 (88↔93) ambiguity, always cross-ref COMMIT-scope vs ambient count. BẤT-NGỜ ‹M20›: table-count 88-vs-93 — narrative-93 is STALE pre-S61; S61 Budget-replace DROPPED 93→88; `sys.tables(is_ms_shipped=0)=88` correct post-S61. [cao] → substring:"Run #286 (run_number 286, id400)"
|
||||
- VIỆC: PE guard 4-thông-tin mục 3 cross-stack. KẾT-LUẬN: PASS commit `37122f0`. BÀI-HỌC: cross-stack guard verify. BẤT-NGỜ ‹M21›: **PE code column = `MaPhieu` NOT `Code`** (custom naming — sqlcmd must SELECT MaPhieu). [vừa] → substring:"Run #283 (run_number 283) sha=`37122f0`"
|
||||
- VIỆC: S59 series — đợt6 cross-stack BE (`9c330d2`), đợt5 FE×2 (`faed59f`), đợt3 BE-only DbInitializer (`bbd1554`), đợt2 FE×2 PE-list tree (`0eafcd3`), CLOSE đóng sổ (`69997da`). KẾT-LUẬN: all PASS. BÀI-HỌC: multi-đợt same-session FIFO; BE-only=both-bundle-frozen, FE×2=both-rotate. BẤT-NGỜ: none. [vừa] → substring:"Run #280 (run_number 280) sha=`69997da`"
|
||||
- VIỆC ‹M18›: S59 FE×2 PE-list tree regroup (`56882ac`), in the post-wipe window after the DemoSeed tables were cleared. KẾT-LUẬN: PASS commit `56882ac`. BÀI-HỌC ‹M18›: **post-wipe deploy = verify BOTH halves — demo tables stay 0 after app-pool recycle (no accidental re-seed) AND infrastructure/master untouched** (don't check only one side). BẤT-NGỜ: recycle did NOT re-populate demo rows (DemoSeed:Disabled holds). [cao] → substring:"Run #273 (run_number 273) sha=`56882ac`"
|
||||
- VIỆC: S59-đợt4 rename WorkItems → 71 hạng mục via prod-SQL + idempotent-seed. KẾT-LUẬN: PASS commit `c869d26`, WorkItems count=71 EXACT no-dup. BÀI-HỌC ‹M17›: **rename-via-prod-SQL + idempotent-seed = verify target count EXACT; double = re-added-dup = FAIL** (71-no-dup). BẤT-NGỜ: none. [cao] → substring:"Run #276 (run_number 276) sha=`c869d26`"
|
||||
|
||||
## #291 FAIL forensic — the CS7036 class (S?, 06-16)
|
||||
|
||||
- VIỆC ‹M6›‹M8›‹M9›: TEST-GATE COMPILE BREAK. KẾT-LUẬN: **FAIL ~64s**, commit `8c8179c`; **CS7036** — `CreateDepartmentCommand` record gained REQUIRED 5th positional `ParentId`; un-updated call site **`MasterCatalogFilteredUniqueTests.cs:63`** (old 4-arg) breaks whole slnx build → deploy GATED, prod stayed baseline (bundles FROZEN + Mig NOT applied). BÀI-HỌC ‹M6›: this is **gotcha #65** root-cause canonical = spec-change-miss class (record ctor grows positional param → stale call-site). BẤT-NGỜ: green-elsewhere but one stale test call-site fails entire build; deploy-gate protected prod. [cao] → substring:"Run #291 (run_number 291, id405) sha=`8c8179c`"
|
||||
|
||||
## Lock/auth quirks (S58)
|
||||
|
||||
- VIỆC ‹M15›: S58 FIX the #381 lock NO-OP. KẾT-LUẬN: PASS commit `5998163`; lock-noop → pw-11to12. BÀI-HỌC ‹M15›: **lock/deactivate-by-email returning 0/-1 = dump actual Users set BEFORE scoring FAIL** (don't assume target row exists). BẤT-NGỜ ‹M21›: custom Identity table = **`Users` NOT `AspNetUsers`**. [cao] → substring:"Run #382 (run_number 268) sha=`5998163`"
|
||||
- VIỆC: S57bis PE gắn WorkItemId loose-Guid no-FK (Mig 49). KẾT-LUẬN: **PASS+1PARTIAL** commit `dd117b7`. BÀI-HỌC: AddColumn+CreateIndex no-new-table. BẤT-NGỜ: partial on one axis. [vừa] → substring:"Run #381 (run_number 267) sha=`dd117b7`"
|
||||
|
||||
## Supersede-chain + bundle-frozen/asymmetric/rotate discipline (S55-S59)
|
||||
|
||||
- VIỆC ‹M10›‹M22›: SUPERSEDE-CHAIN benign. KẾT-LUẬN: same-SHA run flipped to CANCELLED mid-flight, shipped via `3ebaf…` (mem id #385/#386). BÀI-HỌC ‹M10›: **same-SHA run flipping to cancelled = Gitea concurrency-supersede by newer push, NOT fault; verify `merge-base --is-ancestor` + diff-empty + the SUCCESSFUL run, do NOT escalate**. BẤT-NGỜ ‹M22›: mem-labeled run ids (#385/#386) differ from real Gitea task ids — reconcile via run_number. [cao] → substring:"Run #385→#386 SUPERSEDE-CHAIN sha=`ea793a4`"
|
||||
- VIỆC ‹M13›: S58 FE-USER visual redesign. KẾT-LUẬN: PASS commit `e959f72`, asymmetric fe-user-only rotate. BÀI-HỌC ‹M13›: **asymmetric single-app FE: changed-app hash MUST rotate AND other-app MUST stay frozen** (#384 fe-user-only ↔ #378 fe-admin-only — direction flips). BẤT-NGỜ: none. [cao] → substring:"Run #384 (run_number 270) sha=`e959f72`"
|
||||
- VIỆC ‹M12›: S56 GOLIVE-HARDEN BE fixes. KẾT-LUẬN: PASS commit `a20cde8`. BÀI-HỌC ‹M12›: **BE-only bundle FROZEN = correct, NOT ship-fail; ship-proof = Mig-applied / test-count** (not bundle-rotate). BẤT-NGỜ: none. [cao] → substring:"Run #379 (run_number 265) sha=`a20cde8`"
|
||||
- VIỆC: S56 pre-golive re-verify prod truth. KẾT-LUẬN: [audit] no-deploy read-only. BÀI-HỌC: read-only audit pattern (no ship event). BẤT-NGỜ: none. [thấp] → substring:"S56 pre-golive verify — NO deploy"
|
||||
- VIỆC ‹M13›: S55 Phase-1 FE-Admin visual redesign. KẾT-LUẬN: PASS commit `7feb53e`, asymmetric fe-admin-only rotate / other frozen. BÀI-HỌC ‹M13›: changed-app rotate + sibling frozen (the admin-side mirror of #384). BẤT-NGỜ: none. [cao] → substring:"Run #378 (run_number 264) sha=`7feb53e`"
|
||||
- VIỆC ‹M14›: S55 HMW-P4 real master-data seed (Mig 48: 62 dự án + 71 hạng mục + 3 NCC). KẾT-LUẬN: PASS commit `69cb393`. BÀI-HỌC ‹M14›: **ungated SeedRealMasterDataAsync verify = sqlcmd COUNT spot-check + `=N'…'` EXACT for unicode** (reaches prod by design, idempotent per-code). BẤT-NGỜ: none. [cao] → substring:"Run #377 (run_number 263) sha=`69cb393`"
|
||||
|
||||
## SLA / IT-ticket / 411 (S54)
|
||||
|
||||
- VIỆC ‹M16›: S54 IT-staff self-reassign ticket. KẾT-LUẬN: PASS commit `ca4b602`. BÀI-HỌC ‹M16›: **411-vs-401 — unauth bodyless PUT/POST returns 411 from IIS BEFORE [Authorize]; resend `-d {}` (empty body) to get real 401** (paired with #367). BẤT-NGỜ: 411 ≠ unhealthy, it's Content-Length-Required pre-auth. [cao] → substring:"Run #376 (run_number 262) sha=`ca4b602`"
|
||||
|
||||
## Earlier P11 / Office / Holiday (S42-S52, dates 05-28→06-08)
|
||||
|
||||
- VIỆC ‹M3›‹M5›‹M11›: S50 HMW-Wave2 P11-C Vehicle+Driver (Mig 44/45). KẾT-LUẬN: PASS commit `30a99aa`; saw a mid-deploy transient bundle hash (`CVbyotwa`) before the final one settled. BÀI-HỌC ‹M3›: **gotcha #41 Discovery#3 — Gitea evaluates push RANGE; ≥1 non-ignored file ⟹ whole range builds** (mixed docs+tsx NOT skipped). ‹M5›: **gotcha #57 — soft-delete UNIQUE index MUST filter `WHERE [IsDeleted]=0`** (Mig 44/45 filtered-unique). ‹M11› anti-pattern timing: **NEVER trust bundle hash until status=success — mid-deploy shows a transient 3rd hash** (#371 transient `CVbyotwa`, #378); always re-confirm hash AFTER status=success + stable 2nd-fetch (anti#3). BẤT-NGỜ: 3rd transient hash exists only during the deploy window. [cao] → substring:"Run #371 (run_number 257) sha=`30a99aa`"
|
||||
- VIỆC: S48 FE-only login subtitle a11y (`350b2bf`); S45 Mig 43 filter Holiday UNIQUE (`0c5a014`); S42 P11-B LeaveBalance (`82d7fcf`); S42 P11-A workflow-picker 2-bug + SetWorkflow (`75df04e`). KẾT-LUẬN: all PASS. BÀI-HỌC: Holiday filtered-unique = same #57 family; FE-only login = bundle-rotate-only. BẤT-NGỜ: none. [vừa] → substring:"Run #368 (run_number 254) sha=`0c5a014`"
|
||||
- VIỆC ‹M22›: S42 P11-A wire ApproveV2 + LevelOpinions. KẾT-LUẬN: PASS commit `e7b66cd`. BÀI-HỌC: V2 approve wire. BẤT-NGỜ ‹M22›: **mem-labeled "#250" ≠ real Gitea id — reconcile via run_number** (here run_number not in mem label; trust git sha). [vừa] → substring:"Run #364 (mem #250) sha=`e7b66cd`"
|
||||
- VIỆC: S38 SKELETON 5-plan combo (Mig 39+40 dual). KẾT-LUẬN: PASS commit `e54a22d`. BÀI-HỌC: dual-migration single deploy. BẤT-NGỜ: none. [thấp] → substring:"Run #247 sha=`e54a22d`"
|
||||
- VIỆC ‹M4›: ⚠️ VỊ-TRÍ-LẠC entry (FIFO slot between #384/#382; carries #383 dual-role menu detail). KẾT-LUẬN: note. BÀI-HỌC ‹M4›: **gotcha #44 — dual-role menu-tree `/api/menus/me` for nv.test** (Drafter+CCM must merge both roles' menu keys; #383/#381). BẤT-NGỜ ‹M22›: mem run id ≠ real Gitea — this entry was mis-placed in FIFO. [vừa] → substring:"VỊ TRÍ LẠC — entry MỚI 2026-06-11"
|
||||
- VIỆC: S37 Proposal Mig 37+38 (/api/proposals 200 + QT-DX-V2-001 AppType=4). KẾT-LUẬN: PASS [archived]. BÀI-HỌC: AppType=4 seed reach prod. BẤT-NGỜ: none. [thấp] → substring:"Archived Run #246 (S37 Proposal Mig 37+38"
|
||||
- VIỆC ‹M2›: S29 gotcha #51 catch. KẾT-LUẬN: PASS [archived]. BÀI-HỌC ‹M2›: **gotcha #51 — INFRASTRUCTURE seed (SampleWorkflowsV2) must NOT be nested in `if(!demoSeedDisabled)`; symptom = empty V2 dropdown; hoist fix**. BẤT-NGỜ: demo-flag silently gated an infra seed. [cao] → substring:"Archived Run #232 (S29 gotcha #51 catch"
|
||||
|
||||
## Curated-down from L1 this session (S68-S78, dates 06-16/06-17) — the 10 moved records
|
||||
|
||||
- VIỆC ‹M19›: CROSS-STACK PE HoSoLink (Mig 52) + FE-User Hồ-sơ-NS 3-panel + rename Dự-trù→Ngân-sách. KẾT-LUẬN: PASS commit `5a0aaa4`; **Mig 52 applied** (history-top advanced 51→52), `sys.columns` HoSoLink present, **no-new-table 88**; bundle BOTH rotate `BDwV5d0X`/`DXkyUjtQ`. BÀI-HỌC ‹M19›: bundle-hash lineage load-bearing (admin un-froze); nullable-AddColumn cross-stack verify = history-top advance + sys.tables-unchanged + Detail-DTO field-presence via authed GET on REAL phiếu. BẤT-NGỜ: `hoSoLink:null` for old rows proves DTO-wiring AND backward-compat. [cao] → substring:"Run #293 (run_number 293, id407) sha=`5a0aaa4`"
|
||||
- VIỆC ‹M13›: FE-User Hồ-sơ-NS 2-col layout. KẾT-LUẬN: PASS commit `456c7a7`; asymmetric user-rotate `DbVv6rsf` / admin frozen; no-mig stays Mig52. BÀI-HỌC ‹M13›: 3rd-consecutive same-page FE-user — re-rotate same page each deploy = NORMAL, sibling-frozen-when-untouched. BẤT-NGỜ: tokens-present this run (vs empty other runs) — both auth paths work. [cao] → substring:"Run #295 (run_number 295, id409) sha=`456c7a7`"
|
||||
- VIỆC ‹M13›: FE-User Hồ-sơ-NS cosmetic brand800. KẾT-LUẬN: PASS commit `ab4e681`; asymmetric user-rotate `BumgrwCJ` / admin frozen. BÀI-HỌC: **health = `/health/ready`+`/health/live` (200×2), NOT `/health`** (404 ≠ unhealthy — cross-check skill before flag). BẤT-NGỜ: `/health` literal is wrong-path. [cao] → substring:"Run #297 (run_number 297, id411) sha=`ab4e681`"
|
||||
- VIỆC ‹M13›: FE-Admin MIRROR Hồ-sơ-NS from fe-user (asymmetric NGƯỢC). KẾT-LUẬN: PASS commit `292d64d`; admin ROTATE `xkSz9BfE` / user FROZEN (direction flips vs prior deploy). BÀI-HỌC ‹M13›: deploy-2 same-session asymmetric-NGƯỢC = verify prior deploy NOT cancelled + this-app-rotate/other-frozen; admin un-froze from multi-session frozen-streak = EXPECTED. BẤT-NGỜ: **poll-parser bug — `tr,|grep -A` on JSON misanchors → re-query with python3; parser-bug ≠ run-stuck**. [cao] → substring:"Run #298 (run_number 298, id412) sha=`292d64d`"
|
||||
- VIỆC ‹M12›: TESTS-ONLY BE +23 test (→286). KẾT-LUẬN: PASS commit `bcd619d`; bundle BOTH FROZEN `xkSz9BfE`/`BumgrwCJ`; CI test-gate runs both projects BEFORE build ⟹ status=success ⟹ 23 new passed (286 INFERRED not log-count). BÀI-HỌC ‹M12›: BE-only ⟹ both bundles MUST stay frozen (rotate=anomaly); ship-proof=test-count not bundle. BẤT-NGỜ: **`python3` BROKEN on box (ZKBioTime embed SRE-module-mismatch) → use PowerShell Invoke-RestMethod**. [cao] → substring:"Run #299 (id413) sha=`bcd619d`"
|
||||
- VIỆC: FE-both-app PE Link-hồ-sơ `file://` render upgrade (keep Copy fallback). KẾT-LUẬN: PASS commit `536dd6b`; bundle BOTH rotate `CcrZqfht`/`DniDFUB_`; tables88. BÀI-HỌC: pure-FE-both-app = both rotate; `file://` browsers may block from https-origin (hence Copy fallback) — not curl-verifiable. BẤT-NGỜ: none. [vừa] → substring:"Run #302 (run_number 302, id416) sha=`536dd6b`"
|
||||
- VIỆC: FE-both-app Hồ-sơ-NS banner text-polish. KẾT-LUẬN: PASS commit `6983609`; bundle BOTH rotate `D532XZKG`/`CuFaBoWt`. BÀI-HỌC ‹M3›: **docs-files in same push-range as .tsx do NOT suppress build** (range any-non-ignored ⟹ build — Discovery#3 corollary). BẤT-NGỜ: none. [cao] → substring:"Run #303 sha=`6983609` PASS"
|
||||
- VIỆC ‹M13›: FE-both-app 1-line CSS-precedence fix (name `text-white`→`text-white!`). KẾT-LUẬN: PASS commit `37752eb`; bundle BOTH rotate `CNUv1jxY`/`CpOskeS1`; tables88. BÀI-HỌC: even 1-line both-app change → new content-hash both; Tailwind-v4 @layer precedence (unlayered h2 beat text-white). BẤT-NGỜ: SHA256-identical-between-2-apps is a SOURCE claim (git), not runtime DOM-equality. [cao] → substring:"Run #304 (run_number 304, id418) sha=`37752eb`"
|
||||
- VIỆC: S69 FE-both-app Văn-phòng-số foundation + index.css sync + BE menu-seed `Off_Dashboard` (NO-EF-mig). KẾT-LUẬN: PASS commit `a8bbdae`; bundle BOTH rotate `Bl2o_kUq`/`BImrKQNn`; **no-mig top stays Mig52** (menu-seed = DbInitializer runtime row-insert NOT migration); menu-seed verified via MenuItems SELECT; office-hidden = perm row only-Admin 1/13. BÀI-HỌC: prod CI bundle-hash ≠ local-build-hash is NORMAL (CI rebuild) — FAIL only if NOT-rotated-from-baseline. BẤT-NGỜ: sqlcmd string-literal doubled `''x''` BREAKS in PS → build via `[char]39` concat. [cao] → substring:"Run #305 (run_number 305, id419) sha=`a8bbdae`"
|
||||
- VIỆC: S70 FE-only re-skin Văn-phòng-số 10-page PURO. KẾT-LUẬN: PASS commit `c556f6c`; bundle BOTH rotate `Wt54PHYl`/`B99fMU6X`; office-API live (proposals/it-tickets/meeting-rooms/employees 200); office-hidden confirmed `Off_Dashboard` admin-only / Drafter CanRead=0. BÀI-HỌC: **CanRead=0 perm-ROWS exist but ≠ access** (menu gates on CanRead=1) — must filter CanRead=1, existence≠access; office-hidden is AMBIENT (FE-only can't touch Permissions seed). BẤT-NGỜ: `/api/workflow-apps`→404 = wrong-route-guess NOT regression (FE-only can't change BE routing). [cao] → substring:"Run #306 (run_number 306, id420) sha=`c556f6c`"
|
||||
50
.claude/agent-memory/cicd-monitor/archive/2026-06.md
Normal file
50
.claude/agent-memory/cicd-monitor/archive/2026-06.md
Normal file
File diff suppressed because one or more lines are too long
106
.claude/agent-memory/cicd-monitor/archive/_INDEX.md
Normal file
106
.claude/agent-memory/cicd-monitor/archive/_INDEX.md
Normal file
@ -0,0 +1,106 @@
|
||||
# CI/CD Monitor — Archive INDEX (table of contents, NO content)
|
||||
|
||||
> **Purpose:** L2 archive is "dark-matter" (not in RAG). This index is the map: 1 line per archived record so a future spawn can locate + open the right verbatim block on-demand without reading 220KB cold.
|
||||
> **Pointer style = SUBSTRING (Ctrl-F) primary.** Each line ends `→ <file> · substring:"<unique-string>"`. The string is grep-verified `count==1` inside its target file (76/76 unique at build time). Open the file, Ctrl-F the string → lands on the record start. Fallback if a future edit shifts text: search the bare `Run #NNN` + nearest date.
|
||||
> **Pointer key choice:** keyed on 7-char git sha OR `Run #NNN (run_number NNN, idNNN)` heading-prefix. `2026-06.md` has ZERO markdown headings (all bullet records) → anchor-slug impossible there → sha/run-number used. `2026-05-runs.md` mixes 6 meta-headers + 20 run-records under near-identical `### DATE — Run #NNN sha=… VERDICT=` headings (slug-collision) → sha disambiguates. Some `Run #NNN` appear in BOTH a heading AND a body cross-ref → pointer keyed on the record-START form (`Run #NNN (run_number…` / `sha=\`…\` PASS`) which is unique.
|
||||
> **Archives are FROZEN / additive-only.** Records are never edited in place; this index + the `.gist.md` files are the only additive layers.
|
||||
> **SORTED by DATE (newest → oldest).** Labels: `[meta]` = curate-note (not a rich run-record) · `[stub→git]` = FIFO-trim pointer-to-git, thin · `[audit]` = read-only verify, no deploy · `[FAIL]`/`[PARTIAL]` = non-PASS verdict.
|
||||
>
|
||||
> **Files indexed:** `2026-06.md` (39 records, no headings) · `2026-05-runs.md` (20 run-records + 6 meta-headers) · `2026-05-q4.md` (7) · `2026-05-q3.md` (3) · `2026-05-q2.md` (1). Total **76 records**.
|
||||
> Even-older verbatim (S29 #232 ← pre-S38) also lives in git `d2f52ba`. Gist companions: `2026-06.gist.md` + `2026-05.gist.md`.
|
||||
|
||||
---
|
||||
|
||||
## 2026-06 (file: `archive/2026-06.md`)
|
||||
|
||||
- 2026-06-17 · FE-only re-skin Văn phòng số 10-page PURO · PASS bundle-BOTH-rotate Wt54PHYl/B99fMU6X, office-hidden confirmed, tables88 · `2026-06.md` · substring:"Run #306 (run_number 306, id420) sha=`c556f6c`"
|
||||
- 2026-06-17 · FE-both-app Văn phòng số foundation + index.css + BE menu-seed · PASS bundle-BOTH-rotate Bl2o_kUq/BImrKQNn, no-mig stays Mig52, menu-seed verified · `2026-06.md` · substring:"Run #305 (run_number 305, id419) sha=`a8bbdae`"
|
||||
- 2026-06-16 · FE-both-app 1-line CSS-precedence fix (name text-white!) · PASS bundle-BOTH-rotate CNUv1jxY/CpOskeS1, tables88 · `2026-06.md` · substring:"Run #304 (run_number 304, id418) sha=`37752eb`"
|
||||
- 2026-06-16 · FE-both-app Hồ sơ NS banner text-polish · PASS bundle-BOTH-rotate D532XZKG/CuFaBoWt, docs-in-range-no-suppress · `2026-06.md` · substring:"Run #303 sha=`6983609` PASS"
|
||||
- 2026-06-16 · FE-both-app PE Link-hồ-sơ file:// render · PASS bundle-BOTH-rotate CcrZqfht/DniDFUB_, tables88 · `2026-06.md` · substring:"Run #302 (run_number 302, id416) sha=`536dd6b`"
|
||||
- 2026-06-16 · TESTS-ONLY BE +23 test (286) · PASS bundle-BOTH-frozen xkSz9BfE/BumgrwCJ, CI-gate-inferred, python3-broken-use-PS · `2026-06.md` · substring:"Run #299 (id413) sha=`bcd619d`"
|
||||
- 2026-06-16 · FE-Admin MIRROR Hồ sơ NS from fe-user · PASS bundle-asymmetric-NGƯỢC admin-rotate xkSz9BfE/user-frozen, poll-parser-bug-use-python3 · `2026-06.md` · substring:"Run #298 (run_number 298, id412) sha=`292d64d`"
|
||||
- 2026-06-16 · FE-User Hồ sơ NS cosmetic brand800 · PASS bundle-asymmetric user-rotate BumgrwCJ/admin-frozen, health-ready-not-health · `2026-06.md` · substring:"Run #297 (run_number 297, id411) sha=`ab4e681`"
|
||||
- 2026-06-16 · FE-User Hồ sơ NS 2-col layout · PASS bundle-asymmetric user-rotate DbVv6rsf/admin-frozen, no-mig Mig52, tokens-present · `2026-06.md` · substring:"Run #295 (run_number 295, id409) sha=`456c7a7`"
|
||||
- 2026-06-16 · CROSS-STACK PE HoSoLink Mig 52 + FE-User 3-panel + rename Dự trù→Ngân sách · PASS Mig52-applied + bundle-both-rotate BDwV5d0X/DXkyUjtQ, hoSoLink-null-DTO-backward-compat, no-new-table-88 · `2026-06.md` · substring:"Run #293 (run_number 293, id407) sha=`5a0aaa4`"
|
||||
- 2026-06-13 · CROSS-STACK PE PeWorkItemBudgets vượt-ngân-sách soft-warn (Mig 50 net 93→88) · PASS · `2026-06.md` · substring:"Run #286 (run_number 286, id400)"
|
||||
- 2026-06-12 · CROSS-STACK PE guard 4-thông-tin mục 3 · PASS · `2026-06.md` · substring:"Run #283 (run_number 283) sha=`37122f0`"
|
||||
- 2026-06-11 · S59-CLOSE final đóng sổ · PASS · `2026-06.md` · substring:"Run #280 (run_number 280) sha=`69997da`"
|
||||
- 2026-06-11 · S59-đợt6 CROSS-STACK BE · PASS · `2026-06.md` · substring:"Run #278 (run_number 278) sha=`9c330d2`"
|
||||
- 2026-06-11 · S59-đợt5 FE-only ×2 · PASS · `2026-06.md` · substring:"Run #277 (run_number 277) sha=`faed59f`"
|
||||
- 2026-06-11 · S59-đợt4 rename WorkItems 71 idempotent-seed · PASS rename-via-prod-SQL target-count-EXACT no-dup · `2026-06.md` · substring:"Run #276 (run_number 276) sha=`c869d26`"
|
||||
- 2026-06-11 · S59-đợt3 BE-only DbInitializer · PASS · `2026-06.md` · substring:"Run #275 (run_number 275) sha=`bbd1554`"
|
||||
- 2026-06-11 · S59 FE×2 PE-list tree regroup · PASS · `2026-06.md` · substring:"Run #273 (run_number 273) sha=`56882ac`"
|
||||
- 2026-06-11 · S59-đợt2 FE×2 PE-list tree · PASS · `2026-06.md` · substring:"Run #274 (run_number 274) sha=`0eafcd3`"
|
||||
- 2026-06-11 · SUPERSEDE-CHAIN (benign cancel→shipped) · CANCELLED-benign→PASS-via-3ebaf merge-base-ancestor-not-fault · `2026-06.md` · substring:"Run #385→#386 SUPERSEDE-CHAIN sha=`ea793a4`"
|
||||
- 2026-06-11 · S58 FE-USER visual redesign · PASS asymmetric fe-user-only · `2026-06.md` · substring:"Run #384 (run_number 270) sha=`e959f72`"
|
||||
- 2026-06-16 · TEST-GATE COMPILE BREAK (CS7036 ParentId 5th positional, call-site :63 4-arg) · **[FAIL]** ~64s deploy-gated prod-stayed-baseline, gotcha #65 root cause · `2026-06.md` · substring:"Run #291 (run_number 291, id405) sha=`8c8179c`"
|
||||
- 2026-06-11 · S58 FIX #381 lock NO-OP (lock-noop→pw-11to12) · PASS dump-Users-set-before-scoring-FAIL · `2026-06.md` · substring:"Run #382 (run_number 268) sha=`5998163`"
|
||||
- 2026-06-11 · S57bis PE gắn WorkItemId loose-Guid (Mig 49) · PASS+1PARTIAL · `2026-06.md` · substring:"Run #381 (run_number 267) sha=`dd117b7`"
|
||||
- 2026-06-09 · S56 GOLIVE-HARDEN BE fixes · PASS · `2026-06.md` · substring:"Run #379 (run_number 265) sha=`a20cde8`"
|
||||
- 2026-06-09 · S56 pre-golive re-verify prod truth · **[audit]** no-deploy read-only · `2026-06.md` · substring:"S56 pre-golive verify — NO deploy"
|
||||
- 2026-06-09 · S55 Phase-1 FE-Admin visual redesign · PASS asymmetric fe-admin-only rotate, other-frozen · `2026-06.md` · substring:"Run #378 (run_number 264) sha=`7feb53e`"
|
||||
- 2026-06-09 · S55 HMW-P4 real master-data seed (Mig 48, 62 dự án+71 hạng mục+3 NCC) · PASS ungated-seed sqlcmd-COUNT + =N'…' EXACT unicode · `2026-06.md` · substring:"Run #377 (run_number 263) sha=`69cb393`"
|
||||
- 2026-06-08 · S54 IT-staff self-reassign ticket · PASS · `2026-06.md` · substring:"Run #376 (run_number 262) sha=`ca4b602`"
|
||||
- 2026-06-08 · S50 HMW-Wave2 P11-C Vehicle+Driver · PASS Discovery#3 mixed-docs+tsx whole-range-builds · `2026-06.md` · substring:"Run #371 (run_number 257) sha=`30a99aa`"
|
||||
- 2026-06-03 · S48 FE-only login subtitle a11y · PASS · `2026-06.md` · substring:"Run #369 (run_number 255) sha=`350b2bf`"
|
||||
- 2026-06-01 · S45 Mig 43 filter Holiday UNIQUE · PASS · `2026-06.md` · substring:"Run #368 (run_number 254) sha=`0c5a014`"
|
||||
- 2026-05-30 · S42 P11-B LeaveBalance business · PASS · `2026-06.md` · substring:"Run #367 (run_number 253) sha=`82d7fcf`"
|
||||
- 2026-05-30 · S42 P11-A workflow-picker 2-bug + SetWorkflow · PASS · `2026-06.md` · substring:"Run #365 sha=`75df04e`"
|
||||
- 2026-05-30 · S42 P11-A wire ApproveV2+LevelOpinions · PASS (mem #250 ≠ real Gitea id — reconcile via run_number) · `2026-06.md` · substring:"Run #364 (mem #250) sha=`e7b66cd`"
|
||||
- 2026-05-28 · S38 SKELETON 5-plan combo Mig 39+40 dual · PASS · `2026-06.md` · substring:"Run #247 sha=`e54a22d`"
|
||||
- 2026-05-?? · S37 Proposal Mig 37+38 (/api/proposals 200 + QT-DX-V2-001 AppType=4) · PASS [archived] · `2026-06.md` · substring:"Archived Run #246 (S37 Proposal Mig 37+38"
|
||||
- 2026-06-11 · ⚠️ VỊ-TRÍ-LẠC entry (mem run id ≠ real Gitea — FIFO slot between #384/#382) · note · `2026-06.md` · substring:"VỊ TRÍ LẠC — entry MỚI 2026-06-11"
|
||||
- 2026-05-22 · S29 gotcha #51 catch (SeedSampleContractWorkflowV2 nested in demoSeedDisabled) · PASS [archived] · `2026-06.md` · substring:"Archived Run #232 (S29 gotcha #51 catch"
|
||||
|
||||
## 2026-05 late (file: `archive/2026-05-q4.md`)
|
||||
|
||||
- 2026-05-27 · BE+FE×2 Danh bạ nội bộ /api/directory (G-O1) · PASS bundle-both-rotate, menu-seed Off/Off_DanhBa, MediatR auto-discovery Office namespace · `2026-05-q4.md` · substring:"Run #238 (S34 Plan 2 G-O1 Danh bạ nội bộ"
|
||||
- 2026-05-26 · BE Hrm CQRS /api/employees + FE×2 + menu (G-H1 Phase2) · PASS bundle-both-rotate, EmployeeProfiles=33 idempotent · `2026-05-q4.md` · substring:"Run #237 (S33 Plan B G-H1 Phase 2 Task 4+5+6"
|
||||
- 2026-05-26 · BE Mig 34 EmployeeProfile + Plan C BW1-BW7 +9 test (62 Infra) · PASS gotcha#51 INFRASTRUCTURE-seed EmployeeProfiles=33 not-gated · `2026-05-q4.md` · substring:"Run #350 (S33 Plan B G-H1 Mig 34 EmployeeProfile"
|
||||
- 2026-05-26 · S33 startup health-check · **[audit]** HEALTHY, last-5-runs success, Discovery#7 eval/** missing-from-paths-ignore wasteful-trigger · `2026-05-q4.md` · substring:"S33 startup health-check — em main spawn read-only verify, VERDICT=HEALTHY"
|
||||
- 2026-05-26 · S32 wrap curate + Phase9→10 · **[meta]** no-run docs-only skip · `2026-05-q4.md` · substring:"S32 wrap — em main proxy curate + Phase 9 stabilize done"
|
||||
- 2026-05-26 · S32 startup verify (foundation freshness + 3-endpoint smoke) · **[audit]** no-CI-poll, RAG-healthy 2949-chunks · `2026-05-q4.md` · substring:"S32 startup verify — no CI poll"
|
||||
- 2026-05-21 · S26 Plan AG PE-List tree-view UI iteration (#222-#227 cumulative) · PASS hybrid-verify spawn-1×-per-phase-wire · `2026-05-q4.md` · substring:"Run #222-#227 cumulative — Plan AG series PE List tree view"
|
||||
|
||||
## 2026-05 mid (file: `archive/2026-05-runs.md`)
|
||||
|
||||
### Run-records (20)
|
||||
- 2026-05-19 · S25 t7 Plan AF FE userMap fallback resolve historical · PASS bundle-rotate C8TvDy7r/BvcWrq2z, Discovery 503-mid-deploy + auth-route /api/auth/login accessToken · `2026-05-runs.md` · substring:"Run #221 id=335 sha=`506cada`"
|
||||
- 2026-05-19 · S25 t6 Plan AE Changelog UserName 9-sites batch · PASS BE-only bundle-frozen, preventive-batch-fix-all-Add()-sites · `2026-05-runs.md` · substring:"Run #220 id=334 sha=`9ea62be`"
|
||||
- 2026-05-19 · S25 t5 Plan AD Lịch-sử-duyệt redesign next-target-hint · PASS FE-only bundle-rotate, parse-comment-semantic-hint · `2026-05-runs.md` · substring:"Run #219 id=333 sha=`0aaf2df`"
|
||||
- 2026-05-19 · S25 t4 Plan AC2 FE merge recover historical Reject · PASS bundle-rotate, synthetic-rows-from-Changelog-entityType5-ContextNote · `2026-05-runs.md` · substring:"Run #218 id=332 sha=`25837b6`"
|
||||
- 2026-05-15 · S24 t1 Plan AA wrap fix sidebar · PASS last-deploy-before-AB, baseline CZdXQ2eo/DCwhhey2 · `2026-05-runs.md` · substring:"Run #214 id=328 sha=`ee0902a`"
|
||||
- 2026-05-15 · S24 t1 Plan AA IsUserSelectable filter + WfView menu + sidebar (4/4 wire) · PASS bundle-rotate, Order-shift-idempotent, non-admin-read-200-not-403 · `2026-05-runs.md` · substring:"Run #210 id=324 sha=`ac2c859`"
|
||||
- 2026-05-15 · S23 t11 Plan U FE sidebar truncate · PASS long-label ellipsis+title-tooltip · `2026-05-runs.md` · substring:"Run #209 sha=`86d8806`"
|
||||
- 2026-05-15 · S23 t10 Plan T5+T6 docs final (DemoSeed wipe ~720 rows + force recycle no-reseed) · PASS flag-proven-end-to-end · `2026-05-runs.md` · substring:"Run #208 sha=`7b7b28f`"
|
||||
- 2026-05-15 · S23 t10 Plan T DemoSeed disable (flag in appsettings, 5 demo-seed gated) · PASS · `2026-05-runs.md` · substring:"Run #207 sha=`0b97840`"
|
||||
- 2026-05-15 · S23 t7 Plan Q FE banner mx-5 fix · PASS bundle-rotate QZIPWD-g/DaLTMGcx · `2026-05-runs.md` · substring:"Run #204 sha=`108268a`"
|
||||
- 2026-05-15 · S23 t6 Plan P HOTFIX Controller TransitionPeBody +3 fields · PASS Discovery#4 ASP.NET10-record-enum-needs-numeric (no JsonStringEnumConverter), point-10 body-record-mirror-count · `2026-05-runs.md` · substring:"Run #203 id=317 sha=`1727bd5`"
|
||||
- 2026-05-15 · S23 t5 Plan O HOTFIX cascade 4 lookup-sites (point 9) · PASS 409-mode-not-403-actor proves discrimination, Discovery#3-reinforce-3rd, grep-FirstOrDefault-Order-scan · `2026-05-runs.md` · substring:"Run #202 id=316 sha=`a1c8386`"
|
||||
- 2026-05-15 · S23 t4 Plan N HOTFIX per-NV lookup discrimination :765 · PASS 7/7-vs-1/7-per-actor, bug-2days-prod, point-9 lookup-site · `2026-05-runs.md` · substring:"Run #201 id=315 sha=`fb3c22c`"
|
||||
- 2026-05-15 · S23 t3 Plan M Service F1 OneLevel/OneStep edge-case Bước1 reset · PASS Discovery#3-anomaly docs-only-tip-triggered, Mig31-unchanged · `2026-05-runs.md` · substring:"Run #200 id=314 sha=`f4055a1`"
|
||||
- 2026-05-14/15 · S23 t1 K10 hotfix AwLevelDto +AllowApproverSkipToFinal · PASS 8-surface-point-checklist (AwLevelDto+CreateAwLevelInput were the gaps) · `2026-05-runs.md` · substring:"Run #195 id=309 sha=`0062fcb`"
|
||||
- 2026-05-14 · S23 t1 Plan K Mig 31 per-Approver-slot refactor · **[PARTIAL]** Mig+schema OK but K3 DTO-mirror INCOMPLETE (AwLevelDto missing AllowApproverSkipToFinal — comment-only not record-param) → 7th-checkbox no round-trip · `2026-05-runs.md` · substring:"Run #194 id=308 sha=`098baa6`"
|
||||
- 2026-05-13 · S22-chốt cumulative verify · PASS Discovery#3 push-range-eval (b079b27+b04a11a batched→single-run-on-tip), #190 cancelled-by-concurrency-normal, 104 test, Mig30 · `2026-05-runs.md` · substring:"Verify S22 chốt cuối cumulative (push range `3d725c4..cc8a7d3` 12 commits) VERDICT=PASS"
|
||||
- 2026-05-13 · S22 Plan D Users-toggle + Plan C 14-test + Plan E strict-V2-scope · PASS 103 test, Discovery#1 rate-limit-429-backoff, Discovery#2 agent-memory-NOT-in-paths-ignore (later disproven) · `2026-05-runs.md` · substring:"Run #188 id=302 sha=a74e671"
|
||||
- 2026-05-13 · S21 t5 Mig 29 refactor Allow*-per-NV + Designer 5-checkbox · PASS Mig29 applied, Discovery task-table-updated_at-stale-2min cross-check-VPS-mtime · `2026-05-runs.md` · substring:"Run #187 id=301 sha=c0af9e0"
|
||||
- 2026-05-13 · S21 t3+t4 gotcha#45 Trả-lại + F1/F2/F3 advanced-options + Mig 28 · PASS baseline, Discovery API-tasks-not-/runs-404 · `2026-05-runs.md` · substring:"Run #186 id=300 sha=eea86fd"
|
||||
|
||||
### Curate-note meta-headers (6) — [meta]
|
||||
- 2026-05-19 · curate-note S25 t5 Plan AD #219 · **[meta]** · `2026-05-runs.md` · substring:"added S25 t5 Plan AD Run #219 entry"
|
||||
- 2026-05-19 · curate-note S25 t3 Plan AC #217 (Discovery#5 sqlcmd 4-backslash \\\\SQLEXPRESS) · **[meta]** · `2026-05-runs.md` · substring:"added S25 t3 Plan AC Run #217 entry"
|
||||
- 2026-05-19 · curate-note S25 t1 Plan AB #215 (gotcha#48 SQLite tie-break FAIL) · **[meta]** · `2026-05-runs.md` · substring:"added S25 t1 Plan AB Run #215 entry"
|
||||
- 2026-05-15 · curate-note S24 t1 Plan AA #210 · **[meta]** · `2026-05-runs.md` · substring:"added S24 t1 Plan AA Run #210 entry"
|
||||
- 2026-05-15 · curate-note S23 t6 Plan P #203 (Discovery#4 numeric-enum) · **[meta]** · `2026-05-runs.md` · substring:"added S23 t6 Plan P HOTFIX Run #203 entry"
|
||||
- 2026-05-15 · curate-note S23 t5 Plan O #202 (4-site discrimination, TransitionPeBody gap surfaced) · **[meta]** · `2026-05-runs.md` · substring:"added S23 t5 Plan O HOTFIX Run #202 entry"
|
||||
|
||||
## 2026-05 early (file: `archive/2026-05-q3.md`)
|
||||
|
||||
- 2026-05-19 · ★ Run #215 FAIL → #216 PASS pair — **gotcha #48 SQLite tie-break** catch+fix (.Where(Summary.Contains "Chuyển phase") BEFORE OrderByDescending(CreatedAt).First()) · FAIL→PASS · `2026-05-q3.md` · substring:"Run #215 FAIL → Run #216 PASS (gotcha #48 SQLite tie-break catch+fix pair)"
|
||||
- 2026-05-13 · Verify S22 chốt cuối cumulative (Discovery#3 first-surfaced, 33 users, 104 test) · PASS · `2026-05-q3.md` · substring:"Verify S22 chốt cuối cumulative (push range `3d725c4..cc8a7d3` 12 commits VERDICT=PASS"
|
||||
- 2026-05-12 · Setup baseline (agent init, 44 gotchas + 5-stage + 3 skills) · init · `2026-05-q3.md` · substring:"2026-05-12 — Setup baseline"
|
||||
|
||||
## 2026-05 early (file: `archive/2026-05-q2.md`)
|
||||
|
||||
- 2026-05-22 · Run #231 Plan B Contract V2 wire kick-off · **[PARTIAL]** PASS-deploy + seed-gap (DemoSeed flag gated QT-HD-V2-001 → empty dropdown) → resolved Run #232 (gotcha #51) · `2026-05-q2.md` · substring:"Run #231 (id=345) Plan B Contract V2 wire kick-off VERDICT=PARTIAL"
|
||||
@ -28,7 +28,14 @@
|
||||
- Fallback khi stack chưa chạy: static component preview / screenshot `/login` — **KHÔNG bỏ soi** (FD2 cấm ship-unseen).
|
||||
|
||||
## Component inventory (built/verified — chống reinvent)
|
||||
- **3 shared UI cho Văn phòng số / E-Office (S69, 2026-06-17) — PURO-style + HRM visual language. ALL build-PASS 0 TS:**
|
||||
- `fe-user/src/components/ui/PageHeader.tsx` — **richer page header** (eyebrow/title/subtitle/icon/accent/actions/breadcrumb). ⚠️ KHÁC `@/components/PageHeader` (constrained {title,description,actions}) — module path `@/components/ui/PageHeader` riêng, KHÔNG collision. icon-chip accent-tinted + title `text-xl font-bold` accent-head. Local `ACCENT` map {chipBg,chipFg,head} (brand=text-brand-800, rest -700). Title trên nền SÁNG → KHÔNG cần `text-white!`.
|
||||
- `fe-user/src/components/ui/KpiCard.tsx` — **clickable stat card = FILTER chip** (PURO: row KpiCards thay tabs). icon-chip + `.stat-value text-2xl` accent + `.label-eyebrow`. active = `bg-{x}-50` + `border-{x}-300` + `ring-{x}-500`. a11y FULL: `onClick`→`role=button`+`tabIndex=0`+Enter/Space (`e.key===' '`)+`aria-pressed=active`+focus-visible ring; no-onClick = inert div. hover `-translate-y-0.5` + `motion-reduce:transform-none`.
|
||||
- `fe-user/src/components/ui/WidgetCard.tsx` — **dashboard widget container** (PURO HomePage). Wrap `.card-accent` (rail via inline `--accent`). Header: brand=`.app-gradient-brand text-white` · non-brand=tinted `bg-{x}-50` bar. **gotcha 66 APPLIED:** gradient `<h3>` title = **`text-white!`** (bang) — plain text-white thua unlayered h1-h4 rule. Props: title/icon/accent/stats[]/onExpand/onRefresh/children/empty/emptyText. `stats[]` = clickable StatChip row (mỗi chip a11y button khi có onClick). empty → muted icon-chip + emptyText. Header IconButton (RefreshCw/Maximize2) contrast-adapt gradient↔tinted, aria-label.
|
||||
- **3 đều:** NAMED export · `import type` (verbatimModuleSyntax) · `cn` from `@/lib/cn` · lucide-react · accent palettes stop 50/100/500/600/700 ONLY (no -800) → head/value -700 (brand -800 OK) · icon-chip recolor `['--chip-bg' as string]`/`['--chip-fg' as string]` inline (pattern từ HRM Card). NO new npm dep. Build `tsc -b && vite` PASS 0 TS, 24.87s (warning @import-order + chunk-size = pre-existing). fe-admin NOT mirrored (separate pass nếu cần). FD2 authed-screenshot SKIP (components chưa wired vào page nào — pure library; visual verify khi page tiêu thụ chúng).
|
||||
- `fe-user/src/pages/office/OfficeDashboardPage.tsx` — **E-Office landing dashboard (S69, 2026-06-17) — PURO HomePage, COMPOSES the 3 shared ui widgets. build-PASS 0 TS, 434ms.** Layout: `ui/PageHeader` (eyebrow "Văn phòng số" / title "Bảng điều khiển" / icon LayoutDashboard / accent brand) on top → `grid grid-cols-1 lg:grid-cols-3 gap-5`: LEFT `lg:col-span-2` = stack of 4 `WidgetCard` (Đề xuất brand / Đơn từ teal / Ticket CNTT violet / Phòng họp hôm nay amberx — each body = row of 3 `KpiCard` filter-chips except Phòng họp = 1 KpiCard + next-4 booking peek list) · RIGHT `lg:col-span-1` = "Công việc của tôi" WidgetCard (brand-50 hero count `myTodo` + 3 clickable `MetricRow`) + "Thao tác nhanh" `.card-accent` panel (3 Button primary/secondary/outline). Stacks 1-col <lg. **Hooks REUSED (verbatim queryKey+endpoint = shared TanStack cache, NO new API, NO new BE):** proposals `['proposals',{…}]`→`GET /proposals` (+ separate `inboxOnly:true` query for "Cần duyệt" = needs-my-action signal) · đơn-từ `['/leave-requests'|'/ot-requests'|'/travel-requests',{page:1}]`→those 3 endpoints, merged+`countByStatus` client-side · tickets `['it-tickets']`→`GET /it-tickets` · meetings `['meeting-bookings',{…}]`→`GET /meeting-bookings` windowed to today (local-midnight→+1d ISO). **Counts ALL client-side** (`countByStatus` reducer; status enums from `@/types/proposal` + `@/types/workflowApps`). **States graceful per-widget:** isError→`WidgetError` (retry btn, accent-500 chip) · isLoading→`WidgetSkeleton` (3 pulse bars, `motion-reduce:animate-none`) · empty→WidgetCard `empty` prop. NEVER blocks page. **Routing question:** only `/proposals/new` exists as real create route → quick-actions "Tạo đề xuất"→`/proposals/new`, "Tạo đơn"→`/workflow-apps/leave` (đơn-từ landing, NO standalone /new), "Tạo ticket"→`/it-tickets` (ticket landing) — every link hits an EXISTING route (no `*` fallback dead-link). onExpand per widget → its real route. a11y: KpiCard/MetricRow clickable = role=button+Enter/Space+focus-ring; reduced-motion honored. Routing/menu wiring NOT done (next agent's job — page NOT imported in App.tsx yet). fe-admin NOT mirrored. FD2 authed-screenshot SKIP (ProtectedRoute + gotcha #3 rig blocks authed; visual verify via deploy).
|
||||
- `fe-user/src/pages/LoginPage.tsx` — login (public, no auth). Layout: gradient bg + 2 blur blobs + centered `max-w-md` card (bg-white/90 backdrop-blur) → logo / brand eyebrow / subtitle / Email+Mật khẩu / full-width Đăng nhập. Uses ui/{Button,Input,Label}. Solid baseline; nearly identical in fe-admin (mirror candidate).
|
||||
- `fe-user/src/pages/hrm/EmployeesListPage.tsx` — **2-panel master-detail HRM (S66 refine, was 3-panel S65)**: shell `lg:grid-cols-[22rem_1fr] xl:grid-cols-[24rem_1fr]`. **CỘT TRÁI** = `<div flex flex-col gap-4>` chứa Org-tree (TRÊN, `lg:max-h-[44%] lg:shrink-0`, cuộn riêng) + List+filter (DƯỚI, `flex-1`, cuộn riêng). **CỘT PHẢI** = Detail 5-tab (flex-1, rộng). <lg → 1-col (tree→list→detail) + mobile tree-toggle `treeOpenMobile` (tree `hidden`→`flex`). Org tree = recursive `TreeNode` consume `GET /api/departments/tree` (DepartmentTreeNode {id,code,name,parentId,directEmployeeCount,totalEmployeeCount,children}); gốc "SOLUTION COMPANY" (`companyOpen`) → `pickDept(null)`=all; `CountBadge` (totalEmployeeCount, active=brand-600 fill) → `deptId` URL param. Detail = avatar header (`.app-gradient-brand` + initials-in-rounded-2xl) + 5-tab nav (Tổng quan/Thân nhân/Trình độ/Kinh nghiệm/Hợp đồng) count pills + brand underline. **Accent system (S66 việc 2+3):** `type Accent='brand'|'teal'|'violet'|'amberx'|'greenx'` + `ACCENT` map (chipBg/chipFg/head/rail/labelText). `Card` nhận `accent` prop → `.icon-chip` tinted (`--chip-bg`/`--chip-fg` inline) + heading `text-{x}-700` + left rail pseudo `before:content-[''] before:w-1 before:bg-{x}-500` (clip qua overflow-hidden). `Field` label = `text-{x}-700` uppercase semibold (was slate-400), value = `font-medium text-slate-900` (was slate-800). OverviewTab: 1 accent/card (Thông tin chung=brand, Sức khoẻ/Lương=greenx, Liên hệ/Ngân hàng=teal, Giấy tờ/Đoàn thể=violet, Công việc=amberx). Tab-body sections: family=violet, đào tạo=teal, kỹ năng=greenx, công tác=amberx, hợp đồng=brand. ⚠️ **accent palettes chỉ có stop 50/100/500/600/700 — KHÔNG -800** → head dùng -700 (else Tailwind v4 silent no-class). Reusable: `Avatar`(hash 5 gradients + dim), `CountBadge`, `Card`(+accent), `Field`(+accent/mono/icon/full). **ALL 5 satellite CRUD + 15 satellite api endpoint (+ top-level del + 3 reads) + 3 query keys preserved verbatim** (grep+tsc verified, layout/style-only). fe-admin NOT mirrored (separate pass).
|
||||
|
||||
## Anti-slop catches + rubric verdicts
|
||||
- **LoginPage (S47): rubric PASS.** Anti-generic ✓ (brand #1F7DC1 NOT default-blue, no emoji, lucide-ready, purposeful palette). Fix applied: subtitle "Đăng nhập để tiếp tục" `text-slate-500`→`text-slate-600` (borderline ~4.6:1 over translucent card → solid ~7.5:1, FD5 contrast floor). 1-line, no layout shift, on-scale (FD1). Screenshots: `/tmp/fd2-login-shots/login-{before,after}-{mobile-375,desktop-1440}.png`.
|
||||
@ -36,6 +43,11 @@
|
||||
- Minor noted (NOT fixed, out of bounded scope): 2 `blur-3xl` blobs barely visible at 1440 = render cost ~0 payoff; eyebrow `tracking-[0.2em]` heavy. Candidates if login redesign requested.
|
||||
|
||||
## Activity log
|
||||
- **S79 (2026-06-19) PE list fe-user → DECOUPLE chọn/mở-rộng (anh chốt, annotated screenshot):** `pages/pe/PurchaseEvaluationsListPage.tsx`. S78 cũ: bấm dòng = mở overlay full-bleed, 2 panel giữa/phải LÀ placeholder vĩnh viễn. S79 đổi: **bấm dòng = inline 3-panel detail "như cũ" (8e68ed1)** + thêm nút **"Xem mở rộng"** mỗi dòng → overlay. Mechanism = param thứ-2 **`?expand=1`** cạnh `?id`: overlay render CHỈ khi `id && expand`; inline detail (panel giữa `<PeDetailTabs readOnly onBack=closeDetail>` + panel phải `<PeWorkflowPanel readOnly={!pendingMe} onApproved>`) render khi `id && !expand`. Handlers: `selectRow`=set id+clear expand (NHƯNG `<lg`/innerWidth<1024 → set expand=1 đi thẳng overlay vì 3-panel không vừa) · `expandRow`=id+expand=1 · `collapseFocus`(Thu gọn/Esc/backdrop)=`setParam('expand',null)` GIỮ id (về inline, KHÔNG về list) · `closeDetail`(← Đóng/del)=clear id+expand → list · `onApproved`=clear cả hai → list (phiếu rời inbox). Overlay gate đổi `hasSelection`→`overlayActive=id&&expand`; Esc gọi collapseFocus (không closeFocus). Nút "Xem mở rộng" = `Maximize2` (lucide, import THÊM) icon-btn 7×7 góc phải-trên mỗi leaf row, `opacity-0 group-hover/row:opacity-100` + `opacity-100` khi selected, `title`+`aria-label`+brand focus-ring; leaf `<button>` cũ bọc trong `<div className="group/row relative">` + `pr-9` chừa chỗ nút. **ALL logic VERBATIM** (grep ✓): 3 queryKey (`approval-workflows-v2-filter`/`pe-list`/`pe-detail`) · 3 endpoint (`/inbox`+paged+detail) · `del` mutation (onSuccess giờ clear id+expand thay `setParam`) · `yearGroups` 4-tầng tree memo · `PeUrgentChips` pill · search/filter/SLA/localStorage-expand. `PurchaseEvaluationDetailPage` route GIỮ. **slide+a11y S78 giữ NGUYÊN** (mount-rAF×2, role=dialog/aria-modal, focus-trap Tab-vòng, body scroll-lock, motion-reduce). `npm run build` (tsc -b strict + vite v8) **PASS 0 TS err, 570ms** (3 warning pre-existing). **FD2: static layout-shell harness** (backend :5443 down → authed dev-rig blocked gotcha#3) — Playwright 6 shot × 3 state: (a) inline 3-panel desktop1920+laptop1280 = list+PeDetailTabs giữa+PeWorkflowPanel phải, dòng-chọn có nút Maximize2; (b) overlay 1920+1280 = full-bleed phiếu+panel cạnh; (c) mobile390 list (1-col, nút mở-rộng) + mobile390 overlay (stack). Read tất 6 PNG — đúng hết, no overflow, pill+tree intact. Harness/driver/shots ĐÃ XÓA. Rubric PASS. fe-admin NOT mirrored (implementer-frontend SHA-mirror riêng), no BE, no commit. Tag [s79, pe-decouple-select-expand, expand-param, maximize2-row-btn, inline-restore-8e68ed1, lg-straight-overlay, collapse-keeps-id, logic-verbatim, build-pass, harness-fd2].
|
||||
- **S69 (2026-06-17) OfficeDashboardPage.tsx fe-user — E-Office landing, PURO HomePage, COMPOSES 3 shared widgets:** built `pages/office/OfficeDashboardPage.tsx` (~400 LOC) over EXISTING data hooks of 4 modules. Read-first: 3 ui widgets (exact prop sigs) + 4 source pages (ProposalsListPage/WorkflowAppsListPage/ItTicketsPage/MeetingCalendarPage) to harvest queryKey+endpoint + types + App.tsx routes. **Reused hooks verbatim** → shared TanStack cache, ZERO new API/BE: `GET /proposals` (+inboxOnly query for needs-my-action) · `/leave|ot|travel-requests` (merged, countByStatus client-side) · `/it-tickets` · `/meeting-bookings` (today window). Layout = PageHeader(brand) + `lg:grid-cols-3` [LEFT col-span-2 = 4 WidgetCards w/ KpiCard filter-chip bodies | RIGHT col-span-1 = "Công việc của tôi" hero+MetricRows + "Thao tác nhanh" 3 buttons], 1-col <lg. **Routing insight (verified App.tsx):** đơn-từ + ticket have NO standalone `/new` route (creation in-page) → quick-actions point at landings `/workflow-apps/leave` + `/it-tickets` (only `/proposals/new` is a real create route) so no link hits the `*` "chưa build" fallback. Per-widget graceful states: WidgetError(retry)/WidgetSkeleton(pulse, motion-reduce)/empty — never blocks. a11y full (role=button+Enter/Space+focus-ring on clickables, reduced-motion). `npm run build` (tsc -b strict + vite v8) **PASS 0 TS err, 434ms** (only pre-existing @import-order + chunk-size + INEFFECTIVE_DYNAMIC_IMPORT warnings — none from new file). Routing/menu NOT wired (next agent; page not yet in App.tsx). fe-admin NOT mirrored. FD2 authed-screenshot SKIP (ProtectedRoute + rig gotcha #3 — verify via deploy). Tag [s69, office-dashboard, compose-3-widgets, reuse-hooks-shared-cache, no-new-be, routing-existing-only, build-pass].
|
||||
- **S69 (2026-06-17) Văn phòng số / Đơn từ fe-user RE-SKIN (PURO + HRM visual lang) — 2 file, CONSUMES shared ui (parallel fan-out, 7 agents same app):** surgical re-skin (KHÔNG rewrite) `pages/office/WorkflowAppsListPage.tsx` + `WorkflowAppDetailPage.tsx` (`:kind`-driven leave/ot/travel/vehicle via KIND_CONFIG). **LIST:** swap `@/components/PageHeader`(constrained)→`@/components/ui/PageHeader`(eyebrow "Văn phòng số · Đơn từ" / title từ KIND_CONFIG / icon per-kind / **accent teal**) + status filter = ROW 4 `ui/KpiCard` (Tất cả teal / Đã gửi duyệt amberx / Trả lại violet / Đã duyệt greenx, `grid-cols-2 sm:grid-cols-4`) + slate table chrome (thead uppercase 11px slate-500, hover teal-50). **Filter là CLIENT-SIDE view** — added `useState<StatusFilter>` + 2 `useMemo` (counts + visibleItems) DERIVED over already-fetched `items`; **KHÔNG touch query/endpoint/queryKey/navigation** (page chỉ fetch `page:1` như cũ, không có filter-state sẵn nên thêm view-layer = thuần presentation; empty-state phân biệt "chưa có data" vs "không có đơn ở trạng thái này"). **DETAIL:** ui/PageHeader teal + 4 section dùng local `Card`(accent-rail pseudo `before:bg-{x}-500` + icon-chip) + `Field`(label uppercase `text-{x}-700`, value `text-brand-800`) — copy idiom HRM EmployeesListPage (KHÔNG import HRM, helper local riêng). Accent gán: Thông tin=teal · Số dư phép=greenx · Quy trình=violet · Ý kiến=brand. Status badge giữ `WORKFLOW_APP_STATUS_BADGE`. Drop raw "⚠️" emoji trong over-budget banner→text thuần (anti-slop FD3). **ALL data logic VERBATIM** (grep-verified): 2 query `[endpoint,id]`+`['approval-workflows-v2',applicableType]` (key/endpoint/`enabled` y nguyên) · 3 mutation pinWorkflow(PUT /workflow)/submit(POST /submit)/action(POST /{k}) body+onSuccess+invalidate identical · 3 state + flags isDraft/isInWorkflow/hasWorkflow + mọi onClick/nav target bất biến. **gotcha Tailwind-v4 stop:** dùng CHỈ -50/-500/-700 cho teal/violet/amberx/greenx (no -800) — `border-l-greenx-500`/`bg-amberx-50`/`text-amberx-700` đều stop tồn tại (index.css verified). **Self-caught:** `SendHorizonal` (KpiCard "Đã gửi duyệt" icon) export-aggregation-line trong lucide d.ts → đổi `Send` (proven-safe export) tránh alias-risk. Self-review: mọi import resolve, 0 unused local (noUnusedLocals strict) — `Info`/`Wallet`/`GitBranch`/`MessageSquareText` đều dùng. **KHÔNG run npm build** (em main builds central, 7-agent parallel interference) + KHÔNG modify ui/index.css (other agents edit). FD2 authed-screenshot SKIP (ProtectedRoute + rig gotcha #3 — verify via deploy). fe-admin NOT mirrored. Tag [s69, eoffice-donutu-reskin, puro-hrm-visual, consume-ui-pageheader-kpicard, client-side-filter-view, logic-verbatim, no-build-parallel-fanout, lucide-alias-dodge].
|
||||
- **S66 (2026-06-16) HRM Hồ sơ Nhân sự fe-user REFINE từ eoffice LIVE (3 việc) — layout 3-cột→2-cột + tô màu detail:** anh góp ý sau khi xem prod. **Việc 1 (layout):** 3-cột-ngang `[tree 244 | list 352 | detail 1fr]` → **2-cột** `lg:grid-cols-[22rem_1fr] xl:[24rem_1fr]`: CỘT TRÁI = `<div flex flex-col gap-4>` ôm tree (TRÊN, `lg:max-h-[44%] lg:shrink-0`, overflow-auto) + list+filter (DƯỚI, `flex-1`, overflow-auto) — mỗi panel cuộn độc lập; CỘT PHẢI = detail (flex-1, rộng hơn nhiều, đỡ chật). <lg vẫn 1-col tree→list→detail + giữ nguyên `treeOpenMobile` toggle. **Việc 2+3 (màu detail):** thêm `ACCENT` map 5 tone (brand/teal/violet/amberx/greenx) → `Card` prop `accent` tô icon-chip nền nhạt (`--chip-bg/--chip-fg`) + heading `text-{x}-700` + rail trái pseudo-element; `Field` label uppercase `text-{x}-700` semibold (was slate-400 đơn điệu) + value `font-medium text-slate-900`; mỗi card/section gán 1 accent → có màu rõ nhưng tinh tế, brand #1F7DC1 + Be Vietnam Pro + avatar gradient brand GIỮ. **Strategy chống truncation #53 = ONE atomic `Write` cả file** (1556 LOC) → emit change-list + build status SỚM. **2 self-caught bug TRƯỚC build:** (1) `text-{teal,violet,greenx}-800` — accent palettes KHÔNG có stop -800 (chỉ 50/100/500/600/700) → Tailwind v4 silent no-class → đổi head sang -700 (all AA on white); (2) rail pseudo thiếu `before:content-['']` → ::before không render box → thêm. `npm run build` (tsc -b strict + vite v8) **PASS 0 TS err, 495ms** (warning @import-order + chunk-size = pre-existing, không phải mình). **5 satellite CRUD + 15 satellite api endpoint + top-level del + 3 reads + 3 query keys (employees-list/employee-detail/departments-tree-hrm) + cây SOLUTION COMPANY + 5 tab + search/filter preserved VERBATIM** (grep: 15 satellite api.post/put/delete + 3 queryKey + 5 form fns; tsc type-checks mọi payload shape = wiring bất biến). FD2 authed-screenshot SKIPPED per task instruction + gotcha #3 (rig chặn authed ProtectedRoute; anh xem qua deploy) → structural verify thay thế. fe-admin + BE NOT touched, no commit (em main commits). Tag [s66, hrm-2col-refine, eoffice-ref, accent-system, atomic-write-antitrunc, crud-preserved, build-pass, tailwind-v4-stop-gotcha].
|
||||
- **S65 (2026-06-16) HRM Hồ sơ Nhân sự fe-user → 3-panel master-detail NamGroup-ref:** RESTRUCTURE `EmployeesListPage.tsx` (1201→~1140 LOC) — 6 `<details>` → [Org tree | List | Detail 5-tab]. **Strategy chống truncation #53 = ONE atomic `Write` (cả file)** thay piecemeal Edit (atomic Write either fully-lands or errors, KHÔNG half-break) → emit change-list TRƯỚC build → DID BOTH Part A (avatar header+5 tab+section→tab redistribution) + Part B (org tree panel) trong 1 pass, không phải defer B. Org tree consume `/departments/tree` verified BE-side (DepartmentFeatures.cs DepartmentTreeNodeDto, controller `[HttpGet("tree")]`, class-Authorize only). Foundation màu mới DÙNG: `.app-gradient-brand` header / `.icon-chip` / accent palette teal/violet/amberx/greenx (avatar tones) — brand #1F7DC1 + Be Vietnam Pro KEPT. **5 satellite CRUD + 16 api endpoint + query keys preserved VERBATIM** (grep-verified: 16 api.post/put/delete identical payload shape, 5 form fns intact). `npm run build` (tsc -b strict + vite) **PASS 0 TS err, 6.13s**. 1 self-caught bug: typo garbage token `网络Placeholder` trong lucide import (mojibake autocomplete) → removed, all 21 icons valid (node-checked). FD2 authed-screenshot SKIPPED per explicit task instruction + gotcha #3 (rig blocks authed; anh xem qua deploy) — did static structural verify instead (grep endpoint/key preservation). fe-admin NOT touched (mirror = separate pass), no commit. Tag [s65, hrm-3panel, namgroup-ref, atomic-write-antitrunc, crud-preserved, build-pass].
|
||||
- **S58 (2026-06-11) fe-user redesign theo UI/UX guide AI_INFRA canonical — KEEP brand [em main proxy — truncated #53 giữa FD2 screenshot, 2nd consecutive]:** Mirror design-system fe-admin S55 → 14 file fe-user (index.css heading-ladder+.label-eyebrow / 6 ui primitives — Button gần SHA-identical fe-admin chỉ khác comment / 6 shell DataTable+RowActions-additive·Layout-brand-left-rail·TopBar·PageHeader·PhaseBadge-ring·EmptyState / LoginPage polish). Rubric mới = guide 13 mục `D:\Dropbox\CONG_VIEC\AI_INFRA\docs\reference\ui-ux-design-guide.md` (density 14px/h32-34/radius-8/thead-sticky/action-luôn-hiện/no-font-bold). BRAND KEPT: #1F7DC1 + Be Vietnam Pro + slate (guide cho plug hue riêng). Chết NGAY TRƯỚC with_server.py screenshot /login → em main recover: build ×2 PASS 0 TS + diff-review key-stability từng file + ship `e959f72`; authed visual qua deploy prod (rig-gotcha #3 standing). LESSON: 2 lần liên tiếp truncate ở CÙNG điểm (sau khi sửa xong, lúc bắt đầu FD2 rig) → lần sau EMIT file-list verdict TRƯỚC khi vào screenshot loop. Tag [s58, fe-user-redesign, guide-aiinfra, keep-brand, truncated-53-proxy].
|
||||
- **S55 (2026-06-09) Phase-1 fe-admin redesign — density-first NAMGROUP-ref, KEEP brand [em main proxy — designer truncated gotcha #53 trước build/MEMORY]:** Applied 14 file: index.css (density heading ladder semibold + `.label-eyebrow` 11px uppercase slate-500 + drop font-bold) + 6 ui primitives (Button `text-xs font-semibold rounded-lg` h-7/8/10 + brand focus-ring/70 — variant/size keys STABLE 51 call-sites) + 6 shell (DataTable/Layout/TopBar/PageHeader/PhaseBadge/EmptyState) + DashboardPage (KPI card `rounded-lg border-slate-200` + `bg-brand-50` icon chip h-7w7 + uppercase tracking-wider label + brand accent bar). Brand #1F7DC1 + Be Vietnam Pro KEPT (NAMGROUP density = mượn cấu trúc, brand=ours). `npm run build` 0 TS err. **Visual loop BLOCKED** by authed-rig gotcha (3) above → CHỈ chụp /login (polished, on-brand). em main recover: build ✓ + login-visual ✓ + diff-review (index.css/Button/DashboardPage high-quality, brand-consistent). User chọn commit+deploy → login prod xem authed. Tag [s55, phase1-redesign, density-namgroup, keep-brand, authed-rig-blocked].
|
||||
- **S55 (2026-06-09) Phase-1 fe-admin redesign — density-first NAMGROUP-ref, KEEP brand** [em main proxy — truncated #53]: 14 file (index.css density ladder + 6 ui primitive Button h-7/8/10 brand-ring + 6 shell + DashboardPage KPI). Brand #1F7DC1+Be Vietnam Pro KEPT. build 0 TS. Visual loop BLOCKED authed-rig#3 → chỉ /login. → S58 mirror sang fe-user. (detail archive nếu cần). Tag [s55, density-namgroup, keep-brand].
|
||||
- **S47 (2026-06-02) FD2 RIG VERIFIED ✅** — first real spawn. Ran full FD2 loop end-to-end on fe-user `/login`: read DS (Tailwind v4 CSS-first, corrected stale config-path assumption) → started Vite via `with_server.py` → Playwright screenshot 375+1440 → Read PNGs → FD4 critique → 1-line contrast fix → re-screenshot confirmed → `npm run build` 0 TS error. Closes adap-report `2026-06-02-Agent-frontend-designer-floor` FD2 runtime proof. 2 Vite gotchas captured above. Loop is REAL, not theoretical.
|
||||
|
||||
@ -36,3 +36,5 @@ H2 harvest-MD-integrity auditor **SOLUTION_ERP-self**. Read-only + **propose-onl
|
||||
- **2026-06-12 (S60 @start RE-REPORT — post-S59 đóng-TRỌN):** Verdict 🟢 CLEAN cả 5 mục — tree clean HEAD `6bf28bf` (18:49:21), 11/11 agent-memory mtime ≤18:42:45 đều TRONG closeout → 0 mồ-côi. Coverage S59 14-spawn ĐỦ: H1 tooling `:35/:36` + H2 ×2 + recon inv `:73` + cicd run-coverage 10/10 #273→#282 (6 entry `:72-77` + `:71` #279/280 + extension FINAL-v2 #281/282 — 9 spawn→8 record-unit do supersede-fold, 0 run thiếu). Wave=0 · stray=0 · 0-byte=0 ×2 nơi · user-memory 23 file = index 22 + MEMORY.md 5.6KB khớp · cicd tail `0a` sạch. **2 flag GATE S59-end RESOLVED bởi em main:** `:53` bundle-live → FINAL-v2 `B1DtNT9C`/`D6uF3Mln` #282 ✓ + #275 UTC annotate `:75` ✓. #383 vẫn lạc (`:89`→`:96` shift +7 đúng arithmetic, annotation guard intact). Chore P1 carry: **cicd 56,480B/103L over-cap 3rd-session + phình 54→56.5KB/buổi** + inv 32,931B → curate-L2 (kèm relocate #383 + FIFO swap #273↔#274); watch reviewer 30,354B + impl-be 28,585B. Tag [s60-start, clean, flags-resolved, cicd-56kb]. *(em main APPEND B3 — H2-proposed, verify: 0-byte/tree-clean/size đối chiếu độc lập ✓)*
|
||||
- **2026-06-12 (S61 @start RE-REPORT — S60 đóng KHÔNG trọn):** Verdict 🟡 — S60 2 commit (37122f0 11:53 + 6db195d 14:30) KHÔNG docs-closeout (STATUS/HANDOFF/session-log đều dừng S59). Harvest-MD: 3 delta ĐÃ COMMIT bundle 37122f0 (harvest `:34` + inv ×2 `:73-75` tag s60 đúng-hết-drift + test-spec `:56` +14→254) + 1 mồ-côi cicd dirty `:71` mtime 14:37 entry-kép #283/#284 SANE (bundle-chain B1DtNT9C→akytoBnc→DSvM8h3A + user-chain khớp #282, sha khớp git, 2 test file disk-verified mtime trước commit). Coverage 5/6: **MISS reviewer die-mid-run** (commit body 37122f0 tự khai, file intact KHÔNG 0-byte) → on-behalf APPENDED S61-start (die lần 3: S57bis ×2 + S60). Đợt2 6db195d 0 sub-delta = em-main-direct evidence (16-min turnaround, test mtime 14:27) — flag không phán. Wave=0 · stray=0 · 0-byte=0 ×2 nơi · mojibake 2 hit quoted-cũ. Flags: test-spec baseline 254 stale vs **256 thật (59 Dom+197 Infra — em main dotnet test S61-start)** · CLAUDE.md "240 test" stale · inv `:51` pointer archive nhưng S52 entry chỉ ở git (cut-honest-labeled, nit) · **cicd 61KB/104L over-cap 4th session +4.5KB/buổi → curate-L2 P1 GẤP** + inv 32.7KB. Method ⭐: commit-body = nguồn spawn-evidence khi sub die không để delta (reviewer "die mid-run" tự khai trong msg 37122f0). Tag [s61-start, s60-incomplete-close, reviewer-die-3rd, cicd-61kb]. *(em main APPEND B3 — H2-proposed, verify: commit-body 37122f0 reviewer-die ✓ + test 256 tự chạy ✓ + cicd-dirty diff đối chiếu độc lập ✓)*
|
||||
- **2026-06-15 (S63 `/session-end` 5-trục GATE — em-main-solo, 0 product-sub):** **GATE PASS 5/5, 0 MISS, 0 orphan.** Session = bootstrap + S63 closeout(S60-62) + check-email + Harness 5/6 adopt + gitattributes; sub DUY NHẤT = 2 monitor (H1+H2 start+end). Coverage: 2 monitor record-proposed (không product-sub miss). Completeness 4-field. **0-byte=0** ×8 file S63-written (session-log 10264B · adap-report H5 4930B/H6 5562B · adap-request 2779B · email 3853B · .gitattributes 348B · 2 reviewer child 1838/1701B). Placement: **fe-admin/.claude GONE** + 2 child đúng `agent-memory/reviewer/` + **pointer no-overwrite (31959B preserved)** + moved-not-cut (git log → S63 `5e6dcc1`). Corruption: U+FFFD=0 ×8. Fidelity: em-main-solo honest self-gate (đúng H6.1 governance→solo + precedent S56) → no-escalate. Wave=0 (S63 no-workflow). **3 flag non-block:** (1) email body-hash `8a247984` H2 không verify scope (3 candidate Python sai formula) — **em main ĐÃ self-verify spec-canonical recompute==stored** (PowerShell), non-issue · (2) **cicd 62KB over-cap 5th-session +2.3KB/S61 → curate-L2 P1 GẤP** (carry 41→54→56→61→62KB) · (3) "CLAUDE.md count stale" = docs/CLAUDE.md FULL (root ĐÃ fresh). Tag [s63-end, gate-pass-5/5, 0-miss, em-main-solo, cicd-62kb]. *(em main APPEND B3 — H2-proposed, verify: 0-byte ×8 + pointer-31959B + tree-clean đối chiếu độc lập ✓)*
|
||||
|
||||
- **2026-06-16 (S66 @start RE-REPORT + @end GATE — em-main-solo, 0 product-sub):** @start: 0 orphan (S65 PE fan-out fully harvested+committed incl 2 empty-return subs) · 0 corruption (AS-8 clean) · Fidelity PASS (HoSoLink+Mig51/52 ground-truthed) · **🔴 cicd-monitor L1 86.8KB 2.9× cap GẤP** → em main curate **86.8→28.9KB** byte-exact sed move (Run #286→#232 → archive/2026-06.md, incl #291 forensic [lesson=gotcha#65]; baseline + 6 runs #289-#295 giữ). @end GATE 5-trục: Coverage (2 monitor spawn-record) · Completeness (4-field) · Placement (đúng agent-memory) · Corruption (moved-not-cut + email body-hash round-trip MATCH) · Fidelity PASS. NO wave-folder (no WAVE-MODE). GATE **PASS 5/5**. on-behalf em main (H2 read-only).
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
> **Persistent diary cross-session.** Auto-injected first ~200 lines at spawn (L1 HOT).
|
||||
> Update BEFORE every stop. Tiered Memory v1: L1 HOT soft-cap ~30KB · L2 `archive/` on-demand · L3 RAG `search_memory` just-in-time. Keep entry ≤ 1.5K chars (gotcha #53).
|
||||
> Full verbatim history pre-S40 → git `d2f52ba` + `archive/2026-05-q1..q4.md`.
|
||||
> Full verbatim history pre-S40 → git `d2f52ba` + `archive/2026-05-q1..q4.md` + `archive/2026-06.md`. 🗺️ **Lookup map (Harness-9 S70): `archive/_INDEX.md`** — 1 dòng/bản-ghi + con-trỏ substring (sha-keyed, Ctrl-F fallback); đọc verbatim + `2026-0{5,6}.gist.md` (nén 4-field) theo nhu cầu.
|
||||
> **Renamed S39:** implementer → implementer-backend (.NET half). FE patterns → `implementer-frontend` MEMORY. Test patterns → `test-specialist` MEMORY.
|
||||
|
||||
---
|
||||
@ -74,34 +74,20 @@ UI `disabled={!canX}` + BE helper `EnsureCanXAsync(id, userId)` throw 403 (NOT i
|
||||
|
||||
## 📅 Recent activity (FIFO — older → archive/git)
|
||||
|
||||
- **2026-06-19 (PE chuông báo approver BE — NO migration, 1 edit/1 file, em-main spec deterministic 100% → ACCEPT Case 1):** Tra Sol (Zalo) "không thấy chuông — việc có hồ sơ cần duyệt": approver Cấp hiện tại KHÔNG nhận notify khi phiếu ENTER/ADVANCE tới ChoDuyet (chỉ drafter báo terminal). FIX = +1 block trong `LogTransitionAsync` (PurchaseEvaluationWorkflowService.cs ~line 1058) NGAY SAU drafter-notify, KHÔNG endpoint mới (Pattern 4 — notify = side-effect của Submit/Approve-advance, cả 2 đã gọi LogTransitionAsync + end ChoDuyet). **Resolution mirror EXACT canonical** `EnsureActorInLevel` (line ~301): `db.ApprovalWorkflows.AsNoTracking().Include(Steps).ThenInclude(Levels)` → `stepsOrdered[CurrentWorkflowStepIndex]` (bounds-check) → `.Levels.Where(Order==CurrentApprovalLevelOrder && ApproverUserId!=Guid.Empty && !=actorUserId).Select(ApproverUserId).Distinct()` → `NotifyManyAsync(ids, Generic, "Phiếu cần bạn duyệt: {MaPhieu??TenGoiThau}", "Có phiếu Duyệt NCC đang chờ bạn duyệt.", /purchase-evaluations/{Id}, refId:Id, ct)`. **Recon:** (1) `ApprovalWorkflowLevel.ApproverUserId` = non-null `Guid` (NOT Guid?) → "exclude null" = `!=Guid.Empty` defensive (OR-of-N rows mỗi 1 NV). (2) `NotifyManyAsync` sig verified = `(IEnumerable<Guid>, NotificationType, title, string? desc, string? href, Guid? refId, CT)` khớp urgent-feature ref. (3) actor-exclude qua `!= actorUserId` (Guid != Guid? lift OK). **Guard V2-only:** `ApprovalWorkflowId is Guid && CurrentWorkflowStepIndex is int && CurrentApprovalLevelOrder is int` → V1/no-workflow skip. **Best-effort try/catch** nuốt lỗi (phiếu đã SaveChanges; notify fail KHÔNG rollback). Block đọc pointer SAU set → Submit (idx0/lvl1) + Approve-advance (pointer advance rồi LogTransition) đều fire đúng. Build SolutionErp.slnx (2 test project, gotcha #65) **0 warn 0 err**. KHÔNG test/FE/mig/commit. Tag `[pe-approver-bell, notify-many, log-transition-hook, no-mig, v2-only, best-effort-trycatch, pattern4-side-effect]`.
|
||||
- **2026-06-19 (S77 PE +2 ghi-chú giá đề xuất BE — Mig 57 `AddPeSuggestedPriceNotes` 3-file, 6 edit/0 new file, COOKIE-CUTTER S73 suggested-price + S74 CcmNote, em-main CONTRACT-locked names 100% → ACCEPT Case 1):** PRO dải Min/Max + CCM 1 giá (S73) +ô GIẢI THÍCH vì-sao min vs max (anh Kiệt FDC + Tra Sol). 2 cột nullable, no table/index/backfill. (1) `PurchaseEvaluation.cs` +`string? ProSuggestedPriceNote`/`CcmSuggestedPriceNote` ngay sau CcmSuggestedPrice. (2) `PurchaseEvaluationConfiguration.cs` +`HasMaxLength(1000)` ×2 sau ApprovedPriceSource (KHÔNG index — free-text). (3) Mig: Up=2 AddColumn nvarchar(1000) nullable, Down=2 DropColumn, snapshot ×2 verified — clean mirror Mig 55. (4a) `UpdatePeSuggestedPriceProCommand` +trailing `string? Note=null` + validator `MaximumLength(1000)` MATCH-EF (S35) + handler **absolute-set** `pe.ProSuggestedPriceNote=request.Note` (null=clear, gia-đình text-field) + changelog part khi đổi. (4b) `UpdatePeSuggestedPriceCcmCommand` mirror + changelog suffix "(kèm ghi chú)". Role-gate KHÔNG đụng (Note gated y giá: Forbidden fail-closed TRƯỚC side-effect đã có). (5) Controller `SuggestedPriceProBody`/`CcmBody` +`string? Note` + pass `body.Note` vào cmd (4th/3rd positional). (6) `PurchaseEvaluationDetailBundleDto` +`ProSuggestedPriceNote`/`CcmSuggestedPriceNote` sau CcmSuggestedPrice + projection PEFeatures.cs positional-insert ĐÚNG order (DTO positional → order khít projection BẮT BUỘC). **Call-site grep verify (S65b lesson):** `new UpdatePeSuggestedPrice(Pro|Ccm)Command\(` repo-wide → 2 controller (đã sửa) + 18 test named-arg/positional-dừng-trước-Note → trailing-optional fully backward-compat, 0 test edit. FE 0 ref (đúng — implementer-frontend next). Build SolutionErp.slnx (2 test project, gotcha #65) **0 warn 0 err**. KHÔNG apply DB/FE/test/commit. Route: `note` camelCase qua PUT `/suggested-price/{pro,ccm}` body + GET detail bundle. Tag `[s77, pe-suggested-price-note, mig57, two-column-no-table, absolute-set, trailing-optional-callsite-safe, positional-dto-order]`.
|
||||
- **2026-06-17 (S? Off_Dashboard menu leaf BE — NO migration, 3 edit/2 file, idempotent seed mirror S53/S54-TaskD, em-main spec deterministic 100% → ACCEPT Case 1):** +1 menu key `Off_Dashboard` ("Bảng điều khiển Văn phòng số"), pattern = S53 Off_AttendanceReport EXACT. 3 insert: (1) `MenuKeys.cs` const `OffDashboard = "Off_Dashboard"` ngay sau root `Off:99` · (2) `MenuKeys.cs` All[] line `Off, OffDanhBa` → `Off, OffDashboard, OffDanhBa` · (3) `DbInitializer.cs` SeedMenuTreeAsync tuple `(OffDashboard, "Bảng điều khiển Văn phòng số", Off, **0**, "LayoutDashboard")` trước OffDanhBa=1 (Order 0 = landing đầu nhóm, KHÔNG renumber children 1-7 hiện có). **KEY recon — Off_* leaves ARE IN All[] (NOT factory-excluded):** task hint "leaf may be excluded+granted-via-factory" KHÔNG áp Off (chỉ Pe_* leaf sinh động). Off_AttendanceReport :160 in All → tôi follow SAME = +All. Admin auto 2-point verified: `SeedAdminPermissionsAsync:2001` + `Program.cs:78` both iterate `MenuKeys.All` → +All = 4 policy {Read/Create/Update/Delete} + Admin Permission row auto, NO manual grant. **Revoke verified KHÔNG sửa:** `RevokeTemporarilyHiddenModulesAsync:2170` `p.MenuKey.StartsWith("Off")` → Off_Dashboard tự nằm trong scope ẩn-non-Admin. `InReviewScope:2070` chỉ match Catalog*/Master-keys/Pe_* → Off_Dashboard KHÔNG re-grant non-admin. Idempotent: upsert loop :1909 `existingItems.TryGetValue(key)` miss→Add / hit→chỉ reconcile Order (prod DB cũ nhận leaf next boot, re-run no-op). Build SolutionErp.slnx (gồm 2 test project, gotcha #65) **0 warn 0 err**. KHÔNG touch FE (menuKeys.ts/Layout=implementer-frontend)/test/mig/commit. Tag `[s?, off-dashboard, menu-leaf, no-mig, admin-perm-via-all, order-0-landing]`.
|
||||
- **2026-06-16 (S65b PE +HoSoLink BE — Mig 51 `AddHoSoLinkToPurchaseEvaluation` 3-file, 6 file edit + 0 new file, em-main CHỐT spec 100% → ACCEPT Case 1):** Phiếu PE +1 cột `HoSoLink` = 1 hyperlink tới thư mục hồ sơ NAS (anh Kiệt paste link, FE render bấm-mở). KHÔNG entity con/bảng mới — 1 cột nullable. (1) `PurchaseEvaluation.cs` +`string? HoSoLink` sau MoTa. (2) `PurchaseEvaluationConfiguration.cs` +`HasMaxLength(1000)` (KHÔNG index — hyperlink free-text, không filter/join). (3) Mig via `dotnet ef migrations add` → Up=1 `AddColumn<string> nvarchar(1000) maxLength:1000 nullable` NO table NO index, Down=1 DropColumn; snapshot HoSoLink nvarchar(1000) verified. (4a) `CreatePurchaseEvaluationCommand` +trailing `string? HoSoLink = null` (sau WorkItemId — optional-param-after-required rule) + validator `MaximumLength(1000)` MATCH EF (S35 lesson) + handler `HoSoLink = request.HoSoLink`. (4b) `UpdatePurchaseEvaluationDraftCommand` +trailing `string? HoSoLink = null` + validator 1000 + handler **absolute-set** `entity.HoSoLink = request.HoSoLink` (Section-1 text-field family MoTa/DiaDiem pattern → null=clear, KHÔNG null-safe-keep như WorkItemId picker; deliberate: hyperlink user cần clear được). (5) `PurchaseEvaluationDetailBundleDto` +`string? HoSoLink` sau MoTa + projection `e.HoSoLink` positional-insert đúng vị trí. **RANG-CUNG grep verify (bài học CreateDepartmentCommand CS7036):** `Grep CreatePurchaseEvaluationCommand|UpdatePurchaseEvaluationDraftCommand` repo-wide gồm tests → 0 manual `new ...Command(...)` call-site (controller bind `[FromBody]` model-binding, KHÔNG manual ctor; tests dùng NAMED-ARG dừng ở WorkItemId) → trailing-optional-default fully backward-compat, KHÔNG sửa call-site nào. List DTO KHÔNG đụng (spec chỉ Detail/Get). `.slnx` KHÔNG update (chỉ 2 mig-file trong project có sẵn). KHÔNG apply DB/FE/test/commit. Build SolutionErp.slnx (gồm 2 test project) **0 warn 0 err** (DocxRenderer warn cleared). Route: `hoSoLink` camelCase qua POST/PUT body + GET detail. Tag `[s65b, pe-hosolink, mig51, one-column-no-table, trailing-optional-param, named-arg-callsite-safe]`.
|
||||
- **2026-06-16 (S65 Department hierarchy BE — Mig `AddDepartmentParentId` 3-file, 4 file edit + 0 new file, em-main schema CHỐT → ACCEPT Case 1):** Cây tổ chức nền trang Hồ sơ Nhân sự. (1) `Department.cs` +`Guid? ParentId` loose-Guid KHÔNG physical FK (convention PE.ProjectId/WorkItemId/SelectedSupplierId). (2) `DepartmentConfiguration.cs` +`HasIndex(x=>x.ParentId)` only — **KHÔNG `HasOne` self-FK** (em main chốt loose). (3) `DepartmentFeatures.cs` +`GetDepartmentTreeQuery`+`DepartmentTreeNodeDto`+Handler (append existing file, NO new .cs → `.slnx` KHÔNG cần update; slnx lists projects-not-files). (4) `DepartmentsController.cs` +`[HttpGet("tree")]`. **KEY recon finding (spec asked verify):** `EmployeeProfile` has **NO `DepartmentId`** — links via `UserId`; org-chart dept field nằm trên **`User.DepartmentId`** (Mig 11) → GROUP BY `db.Users.Where(DepartmentId!=null && IsActive).GroupBy(DepartmentId).ToDictionary` = DirectEmployeeCount (recon NOT schema-decision). Tree ráp in-mem: roots=ParentId-null OR orphan-parent (safe-root); TotalEmployeeCount=Direct+Σ(Children.Total) đệ quy rollup via `record with{}`; **cycle-guard HashSet<Guid> visited** (node đã thăm→return null cắt vòng); Children sort `OrderBy(Code, StringComparer.Ordinal)` ổn định. **Authz copy-từ-đâu:** `[HttpGet]` List = CHỈ class-level `[Authorize]` (no per-action attr) → `/tree` cũng vậy (verified read controller). Mig diff CLEAN: AddColumn ParentId nullable + CreateIndex IX_Departments_ParentId, NO new table, Down DropIndex→DropColumn (SQL 5074 order). KHÔNG apply (prod/CI). Build SolutionErp.slnx 0/0. KHÔNG touch FE/test/seed-parent/commit (em main). Route `GET /api/departments/tree`→`List<DepartmentTreeNodeDto>`. Tag `[s65, dept-hierarchy, loose-guid-no-fk, in-mem-tree-rollup, cycle-guard, user-departmentid-source]`.
|
||||
|
||||
- **2026-06-11 (S57bis PE WorkItemId BE slice — PARTIAL, on-behalf em main ghi hộ, H2-proposed):** Return-truncated #53 TRƯỚC Mig 49 + projection-3 → em main solo hoàn tất (fix CS7036 + CS8019, Mig 49 `AddWorkItemToPurchaseEvaluation` 3-file, projection ListItemDto ×3 LEFT-join WorkItems, UpdateDraft null-safe `if (request.WorkItemId is not null)` chống null-hóa bug-class S42). LEARNED: FK-guard loose-Guid `AnyAsync(w.Id==x && w.IsActive)`→Conflict (mirror S43); validator `NotEmpty` create-only, DB nullable backward-compat 4 phiếu cũ; `NotEmpty()` trên `Guid?` KHÔNG chặn `Guid.Empty` → handler guard bắt (defense-in-depth). SURPRISE: truncate 2 session liên tiếp (S55, S57bis) ở slice lớn cross-layer → cắt stage nhỏ hơn (entity+config / mig / projection tách spawn). Tag `[s57bis, truncated-53, on-behalf]`.
|
||||
|
||||
- **2026-06-10 (S57-resume spawn-test H4.8 — Harness-4 two-tier):** Mình bị DEMOTE pin `model: claude-opus-4-8` (deterministic-scaffold class, double-gate reviewer+test+cicd sau lưng). Spawn-test echo model NGAY sau edit → self-report `claude-fable-5[1m]` = SE env (CCD harness) KHÔNG fresh-read frontmatter → pin ăn SAU restart CLI. Post-restart mình chạy Opus 4.8 (effort Max giữ env-wide); task hệ-trọng giao mình qua hmw có thể override `tier:'fable'`. Tag [h4-demote, spawn-test, pending-restart].
|
||||
|
||||
- **S56 GOLIVE-HARDEN 3 BE fix (NO mig, 3 file edit, em-main spec deterministic 100% → ACCEPT Case 1):** **#3 LeaveBalance lost-update** `LeaveOtApprovalFeatures.cs` terminal DaDuyet branch → atomic-executeupdate-tx (spec chosen, KHÔNG RowVersion/Mig). Replaced in-mem `bal.UsedDays += NumDays` với: set p.Status/Updated* → `(DbContext)db` cast + `BeginTransactionAsync(ct)` (plain, NO IsolationLevel — READ COMMITTED đủ vì increment atomic) → STEP1 ensure-row (FirstOrDefault, auto-create UsedDays=0 via tracker) + SaveChanges (opinion+status+insert trong tx) → STEP2 `db.LeaveBalances.Where(...).ExecuteUpdateAsync(s=>s.SetProperty(b=>b.UsedDays, b=>b.UsedDays+p.NumDays), ct)` server-side row-lock race-free → `tx.CommitAsync` + **`return;`** (skip trailing shared SaveChanges). **STALE-TRACKED caveat (load-bearing):** ExecuteUpdate bypass tracker → tracked `bal` giữ pre-increment value; SAFE vì không đọc lại + handler return ngay; KHÔNG thêm `bal.UsedDays +=` (double-count). `using System.Data` + EF Core đã import. **#5 AssignItTicketHandler existence-oracle** `WorkflowAppsFeatures.cs:493` → moved Admin-OR-dept-IT Forbidden guard (itDeptId+isAdmin+myDeptId resolve) TRƯỚC ticket NotFound lookup → fail-closed (non-IT nhận Forbidden cho MỌI ticketId). assignee-must-be-IT Conflict + reassign giữ nguyên. **#6 DocxRenderer.cs:30,40 CS8602** → hoist `mainPart = doc.MainDocumentPart ?? throw InvalidOperationException` + `document = mainPart.Document ?? throw` (Document cũng nullable — KEY: 1st hoist chỉ fix part, vẫn còn 1 warn ở `.Document.Body`); deref qua local non-null. Build SolutionErp.slnx **0 err 0 warn** (DocxRenderer warn CLEARED — thực tế 1 warn không phải 2 như MEMORY ghi). Test 58 Domain PASS + 154/158 Infra: **4 FAIL `LeaveBalanceTests` (Approve_LastLevel_DeductsLeave.../AccumulatesExisting.../OverEntitled.../MultiLevel_NoDeductAtIntermediate)** = EXPECTED #3 stale-tracked (re-query trả tracked instance pre-increment, DB row đúng) → tests_to_update cho test-specialist (add AsNoTracking/ChangeTracker.Clear). ItTicket authz tests #5 PASS (Case5 đã expect Forbidden, NotFound case dùng Admin caller). KHÔNG touch tests/FE/commit. #4 (Travel/Vehicle smoke test) = test-specialist next stage. Tag `[s56, golive-harden, executeupdate-atomic, fail-closed-authz, cs8602, no-mig]`.
|
||||
↳ **[em main post-review S56]** Tx bumped → `IsolationLevel.Serializable` (shipped code `LeaveOtApprovalFeatures.cs:369`) per database-agent review — convention-align (codegen/Proposal/TravelVehicle) + serialize auto-create-row race. '(plain, NO IsolationLevel — READ COMMITTED đủ)' ở entry = pre-review reasoning, **superseded**. Test 228 green.
|
||||
- **S55 master-data import (Mig 48 `AddProjectMasterFields` 4 AddColumn no-table + `SeedRealMasterDataAsync` 62 Project+71 WorkItem+3 Supplier) [proxy by em main — agent return truncated gotcha #53 before MEMORY step]:** Project entity +4 prop (`Year int?`, `Investor/Location/Package string?`, maxlen 250/500/300 ProjectConfiguration). `ProjectFeatures.cs` DTO+CreateCmd+UpdateCmd+validators+handlers+List/Get projections +4 (all nullable, appended end). **`SeedRealMasterDataAsync`** = 3 tuple-loop per-code idempotent (mirror `SeedDemoMasterDataAsync:2185` `existingCodes.Contains→skip`) wired UNGATED line 118 AFTER `SeedCatalogsAsync` → reaches prod (DemoSeed:Disabled=true KHÔNG gate, by-design như SeedDemoMasterData/Catalogs). Project Name=Code khi Excel blank. WorkItem 4 Category (Vật tư16/Thầu phụ30/MEP9/Thiết bị16, gen Code VT/TP/MEP/TB-NN; divider "THIẾT BỊ" dropped). Supplier NTP→NhaThauPhu/NCC→NhaCungCap, extras→Note. **FLOCK01 collision** demo↔real → per-code skip (demo thắng, real code+year only, OK). Compile-fix `MasterCatalogFilteredUniqueTests.cs` +4 null args CreateProjectCommand (necessary build-green). **Runtime Dev proof (em main):** app-start seeded 62proj/71wi/3sup landed, CAL01.Investor col populates, 0 overflow/dup. Build 0/0, test 216. Data spec `scripts/master-import-data.generated.md`. Tag `[s55, master-import, mig48, seed-real-ungated, project-4field]`.
|
||||
- **S54 ItTicket reassign cross-stack — IT-staff self-service (NO migration, 2 BE file edit):** NEW `GetAssignableItStaffQuery`+`AssignableStaffResult(CanReassign,Staff)`+`AssignableStaffDto(Id,FullName)` capability endpoint (REGION 5 WorkflowAppsFeatures.cs) + MODIFIED `AssignItTicketHandler`: authz Admin-OR-dept-IT → `ForbiddenException`; assignee-must-be-IT → `ConflictException`. Controller `/assign` hạ `[Authorize(Roles="Admin")]`→`[Authorize]` (handler enforce fine-grained data-driven) + NEW `GET /assignable-staff`. Predicate IT = reuse round-robin S52 `Departments.Where(Code=="IT" && !IsDeleted)`. `ICurrentUser` KHÔNG có DepartmentId → query `db.Users.Where(Id==cu.UserId).Select(DepartmentId)`. 2 pattern split (em main reconciled từ stray src/Backend/.claude — cwd-relative Write mishap): [[pattern-controller-lower-authorize-handler-finegrained]] + [[pattern-scoped-capability-endpoint-anti-silent-403]]. Build 0/0, test 203→216 (test-specialist +13 authz), reviewer PASS (role-string "Admin" chain-verified real: AppRoles→SeedRoles→JWT ClaimTypes.Role→cu.Roles). Tag `[s54, it-ticket-reassign, capability-endpoint, authz-handler, no-mig]`.
|
||||
- **S54 Task D BE — promote AttendanceReport to sidebar menu leaf (NO migration, 2 file edit):** Case 1 mechanical, menu = DbInitializer idempotent seed (not schema). 3 insert: (1) MenuKeys.cs const `OffAttendanceReport = "Off_AttendanceReport"` after OffChamCong:124 · (2) MenuKeys.cs All[] Off-group line +`OffAttendanceReport` after OffChamCong:158-159 · (3) DbInitializer.cs menu tuple `(OffAttendanceReport, "Báo cáo chấm công", Off, 8, "FileBarChart")` after OffChamCong:1787 (Order 8, parent Off, mirror Vehicle/Driver S51). **adminPermAutoViaAll=TRUE verified 2-point:** `SeedAdminPermissionsAsync` DbInitializer:1916 iterates `MenuKeys.All` → full-CRUD Permission row per missing key (idempotent `existingMenuKeys.Contains`); `Program.cs:78` iterates All × Actions → policy registration. +All[] = both auto, NO manual grant. **Idempotent-add verified:** menu upsert loop DbInitializer:1845-1862 `existingItems.TryGetValue(key)` miss → `MenuItems.Add` (existing prod gets leaf on restart, existing rows only Order-reconciled — same as S51). Build 0 err (2 pre-existing DocxRenderer warn). KHÔNG touch FE (menuKeys.ts/Layout = implementer-frontend) / tests / commit. Tag `[s54, task-d, menu-leaf, no-mig, admin-perm-via-all]`.
|
||||
|
||||
- **S53 gotcha #57 EXT BE — filter 3 Master Code unique indexes + Mig 46 local catch-up (Mig 47 `FilterMasterCatalogUniqueIndexesByIsDeleted`, index-only no-table):** Test-before RED→GREEN driven (test-specialist `MasterCatalogFilteredUniqueTests`, 3 FAIL on unfiltered → must turn GREEN). gotcha #57 4th+5th+6th cumulative (S45 Holiday Mig 43, S51 HRM×3 Mig 45 → now Department/Project/Supplier). Edit 3 config Code unique: `b.HasIndex(x=>x.Code).IsUnique()` → `+.HasFilter("[IsDeleted] = 0")`. **KEY: copied EXACT filter string byte-for-byte from HolidayConfiguration:18 + LeaveTypeConfiguration:19** (spaces around `=`, NOT guessed) → snapshot+SQL Server consistent with 13 existing filtered indexes. SupplierConfiguration: ONLY Code index filtered, `HasIndex(x=>x.Type)` :25 LEFT untouched (verified snapshot 3590 Type no-filter). Mig diff CLEAN: Up=3×DropIndex+3×CreateIndex filtered, Down=3×reverse unfiltered (no drift, no extra table/col). Master entities HAVE global `HasQueryFilter(!IsDeleted)` (unlike HRM) — app-check `db.X.AnyAsync(Code==req.Code)` filters soft-deleted → passed; then bare DB index counted it → UNIQUE violation 500. Filter aligns DB index with app-check. **Mig 46 local catch-up:** S52 left local LocalDB stuck at Mig 45 (prod had 46 via CI, local gap). `database update` to BOTH DBs applied Mig 46 (`AddSlaFieldsToItTicket`) THEN Mig 47 — residual closed. Dev override `--connection SolutionErp_Dev`; Design factory-default `SolutionErp_Design` (both `(localdb)\MSSQLLocalDB`). Build 0 err (2 pre-existing DocxRenderer warn). Full suite 203 GREEN (58 Domain + 145 Infra, +3 new). KHÔNG touch FE/test/commit (em main commits). Tag `[s53, gotcha-57-ext, mig47, mig46-catchup, filter-byte-for-byte]`.
|
||||
|
||||
- **S52 P11-D Wave2 BE — ItTicket round-robin + SLA (Mig 46 `AddSlaFieldsToItTicket`, 3 AddColumn no-table) [proxy by em main: agent killed session-limit trước MEMORY step]:** Entity +SlaDueAt/SlaWarnedSent/SlaBreached. `CreateItTicketHandler`: `SlaWindow` static map (Urgent4/High8/Medium24/Low72h) **shared với `ItTicketSlaJob`** (single-source) → `e.SlaDueAt=CreatedAt+window`. Round-robin least-loaded: `db.Users.Where(DepartmentId==itDeptId && IsActive).OrderBy(db.ItTickets.Count(assigned && !Closed && !Resolved)).ThenBy(Id).First()` (itDept = `Departments.Code=="IT"`); no IT-staff → unassigned. NEW `ItTicketSlaJob:BackgroundService` mirror SlaExpiryJob (30s warmup/15min loop/scope) NHƯNG **KHÔNG auto-transition** — chỉ breach (SlaDueAt<now & !breached & open → SlaBreached=true + notify assignee) + warning (≤20% window → notify + SlaWarnedSent), idempotent qua guard. DI `AddHostedService<ItTicketSlaJob>` sau SlaExpiryJob. `AssignItTicketCommand` + PUT `/{id}/assign` `[Authorize(Roles=Admin)]`. DTO +SlaDueAt/SlaBreached + projection. **DbInitializer `SeedItDepartmentStaffAsync` KEY ordering: PHẢI chạy SAU `SeedDemoUsersAsync`** (method đó reconcile 2 user dept về PRO/CCM mỗi boot → override về IT sau, end-state deterministic) + dept "IT" thứ 10 + gán nv.cao/nv.truong (sample user, idempotent). Build 0-err. Tag `[s52, p11-d, mig46, round-robin, sla-job, seed-ordering]`.
|
||||
|
||||
- **S52 P11-E+F Wave1 BE migration-FREE (4 file new + 3 edit, NO mig):** Case 1/2 deterministic ~98% em main. **P11-F** (2 LOC): `CreateItTicketHandler` set `e.MaTicket = WorkflowAppCodeGen.GenerateMaDonTuAsync(db,"IT",clock.Now.Year,clock,ct)` TRƯỚC `db.ItTickets.Add` — gen lúc Create (kanban no-workflow, khác Leave/OT gen lúc Submit). Helper = `internal static` cùng ns Office, gọi trực tiếp no-using. Format `IT/2026/001`. **P11-E** report chấm công: NEW `AttendanceReportFeatures.cs` (DTO 3 record EXACT + GetAttendanceReportHandler) + NEW `IAttendanceReportExcelExporter`(App/Reports/Services) + NEW Infra `AttendanceReportExcelExporter`(ClosedXML mirror ContractExcelExporter, sync `Export(dto)` no-DB) + DI scoped + Controller +2 endpoint `[Authorize(Roles=Admin)]`. **KEY gotcha day-type:** DayOfWeek+holidaySet KHÔNG EF-translate → `.ToListAsync()` rồi group/classify IN-MEMORY C#. **KEY gotcha type:** `Holiday.Date`=**DateOnly** KHÔNG DateTime (spec viết HashSet<DateTime> nhầm) → `HashSet<DateOnly>` + so `DateOnly.FromDateTime(a.AttendanceDate.Date)`. OtPolicy active fallback 1.5/2.0/3.0. Day-type prio: holiday→weekend(Sat/Sun)→weekday. OtWeighted=Σ(cấp×hệ số). FullName denorm ưu tiên `Attendance.UserFullName` rồi User.FullName. DeptName LEFT JOIN. RenderResult=(Content,FileName,ContentType) ns Forms.Services. Controller inject exporter ctor. Build 0 err (2 pre-existing DocxRenderer warn). KHÔNG touch FE/test/mig/ItTicket-khác. Routes: GET `/api/attendances/report?year&month&departmentId` + `/report/excel`. Tag `[s??, p11-e-f, wave1, no-mig, day-type-in-memory, dateonly-holiday]`.
|
||||
|
||||
- **S51 P11-C HMW W1 — Vehicle+Driver catalogs HrmConfigs (Mig 44 `AddVehicleAndDriverCatalogs`, 9 add-point ~12 file/edit):** Pattern 12-bis catalog-mega 4th cumulative. 2 entity (`Vehicle`/`Driver`:AuditableEntity Domain/Hrm) + 2 EF config (mirror `HolidayConfiguration` filtered, NOT buggy bare LeaveType) + 2 DbSet (IAppDbContext+ApplicationDbContext) + Mig 3-file + HrmConfigFeatures Region5/6 (DTO+List/Create/Update/Delete CQRS mirror Region1 EXACT) + Controller +2 route group (8 endpoint, GET public + POST/PUT/DELETE `[Authorize(Roles=Admin)]`) + MenuKeys +2 const +All array + DbInitializer (menu 2 leaf + SeedHrmConfigsAsync guard&seed 2 veh+2 drv). **gotcha #57 KEY:** Code UNIQUE `.HasFilter("[IsDeleted]=0")` — Mig diff verified CLEAN 2 CreateTable + 2 filtered IX no drift. Validator MaxLength = em main schema (Code50/Name200/Plate20/Phone20/LicNum50/LicClass20/Desc500), SeatCount GreaterThanOrEqualTo(0). Admin perm AUTO-grant: `SeedAdminPermissionsAsync` + `Program.cs:78` both iterate `MenuKeys.All` → +All array = 8 policy + Admin row auto (no manual grant code). HRM no HasQueryFilter → `.Where(!IsDeleted)` manual. Applied BOTH DB. Build 0 err (2 pre-existing DocxRenderer warn). RAG/Qdrant DOWN → all Read/Grep on-disk. Spec deterministic ~98% em main → ACCEPT Case 1. KHÔNG touch FE/test/commit. Tag `[s51, p11-c, mig44, vehicle-driver, catalog-mega]`.
|
||||
- **S43 P11-B Wave 1 — LeaveBalance business logic (Mig 42 `AddLeaveBalances`, 7 file: 1 entity + 1 config + 2 DbSet edit + Mig 3-file + 1 hook edit + 1 Features + 1 Controller):** Case 1/3 deterministic ~98% em main spec. Pattern 12-ter-adjacent single-entity: entity `LeaveBalance:AuditableEntity` (UserId/LeaveTypeId/Year + EntitledDays/UsedDays/AdjustmentDays decimal(5,2), nav LeaveType). Config FK LeaveType WithMany() **Restrict** (catalog no cascade) + UNIQUE composite (UserId,LeaveTypeId,Year) + IX UserId. Mig diff CLEAN: 1 CreateTable + 3 IX, no drift. Applied BOTH DB (Dev `SolutionErp_Dev` + Design default). **Deduction hook:** insert in `ApproveLeaveRequestHandler` terminal else (DaDuyet branch) ONLY — UPSERT LeaveBalance, `bal.UsedDays += p.NumDays`, exactly-once guaranteed by early guard `Status != DaGuiDuyet throw`. OtRequest/Travel/Vehicle UNTOUCHED (only Leave has balance). CQRS `Application.Hrm`: DTO RemainingDays=Entitled+Adjustment−Used COMPUTED (not stored) + GetMy/GetUser lazy-merge (load active LeaveTypes + balances → in-memory merge, synth default when no row — KHÔNG EF LEFT JOIN translate) + AdjustLeaveBalanceCommand admin upsert (HasValue-gated). **Policy resolved:** HRM admin convention = `[Authorize(Roles="Admin")]` NOT menu policy (verified HrmConfigsController write endpoints) — used on GET-by-user + PUT /adjust; /my = `[Authorize]`. Controller injects IDateTime for year default `clock.Now.Year` (thin, no DateTime.Now hardcode). HRM no HasQueryFilter → `.Where(!IsDeleted)` manual everywhere. KHÔNG touch FE/test/commit. Build 0 err (2 pre-existing DocxRenderer warn). Tag `[s43, p11-b-w1, mig42, leave-balance, single-entity]`.
|
||||
- **S42 P11-A SEED — 4 sample ApprovalWorkflow V2 for WorkflowApps (DbInitializer.cs only, +4 method ~210 LOC + 4 call):** Case 1 mechanical mirror `SeedSampleProposalWorkflowV2Async` EXACT × 4 (Leave5/Ot6/Travel9/Vehicle7). Each: idempotent `AnyAsync(ApplicableType==X)` guard → resolve approver `binh.le@solutions.com.vn` (SAME user as Proposal/Contract seed, null→LogWarning+return) → 1 ApprovalWorkflow (Version=1, IsActive+IsUserSelectable=true, ActivatedAt=UtcNow) + 1 Step (Order=1, Name="Cấp duyệt", DepartmentId=CCM?.Id) + 1 Level (Order=1, ApproverUserId). Codes QT-NP/OT/CT/XE-V2-001. Wired 4 calls after SeedSampleProposalWorkflowV2Async (NOT gated DemoSeed, gotcha #51 infra seed). Enum verified Grep. Build 0 err 0 warn. Bash tool = bash NOT PowerShell despite env hint (use `cd && cmd | grep`). Spec deterministic 100% → ACCEPT Case 1. NOT touched App/Controller/FE/test/Mig. Tag `[s42, p11-a, seed, mirror-proposal-exact]`.
|
||||
- **S42 P11-A Wave 2b APP — wire ApproveV2 CQRS Travel+Vehicle (`TravelVehicleApprovalFeatures.cs` ~830 LOC + 2 controller edit):** Cookie-cutter mirror Wave 2a / ProposalFeatures Region 2. 1 new file ns `Application.Office`: 2 module × (DetailDto + LevelOpinionDto + GetById JOIN Step/Level metadata + UpdateDraft + Submit + Approve UPSERT+advance + Reject TuChoi + Return TraLai+RejectedFromStatus) + 1 shared `internal static TravelVehicleCodeGen.GenerateMaDonTuAsync` (Serializable tx, `WorkflowAppCodeSequences` Prefix-keyed, prefix `DT/CT/{year}` Travel & `DX/XE/{year}` Vehicle, format `{prefix}/{seq:D3}` — D3 no year segment per spec). KEY gotcha: WorkflowAppStatus enum DIFFERS from ProposalStatus int values (DaGuiDuyet=2 not 1, TraLai=3) → mirror by SEMANTIC enum member not literal. Owner = `RequesterUserId` (not DrafterUserId). Submit verify wf.ApplicableType==Travel9/Vehicle7 else Conflict. 2 controller +6 route each (GET{id}/PUT/submit/approve/reject/return) nested body records, CreatedAtAction. KHÔNG sửa WorkflowAppsFeatures.cs/Leave/Ot/FE/test/seed. Build 0 err. Spec deterministic ~98% em main → ACCEPT Case 1/2. Tag `[s42, p11-a, wave-2b, mirror-proposal-region2]`.
|
||||
- **S42 P11-A Wave 2a APP — wire ApproveV2 CQRS Leave+Ot (`LeaveOtApprovalFeatures.cs` ~770 LOC + 2 controller edit):** Pattern 4 (UPSERT in Approve, 0 opinion endpoint) + cookie-cutter mirror ProposalFeatures Region 2. 1 new file ns `Application.Office`: 2 module × (DetailDto + LevelOpinionDto + GetById JOIN Step/Level metadata + UpdateDraft + Submit + Approve UPSERT+advance + Reject TuChoi + Return TraLai+RejectedFromStatus) + 1 shared `internal static WorkflowAppCodeGen.GenerateMaDonTuAsync` (Serializable tx + `WorkflowAppCodeSequences` Prefix-keyed, prefix DT/LR & DT/OT, format `{prefix}/{year}/{seq:D3}`). Approve: flatten allLevels OrderBy Step→Level, currentSlot=allLevels[order-1], actor==ApproverUserId OR Admin, comment empty→placeholder, advance OR terminal DaDuyet. Submit verify wf.ApplicableType==Leave5/Ot6 else Conflict + gen MaDonTu nếu null. 2 controller +6 route each (GET{id}/PUT/submit/approve/reject/return) mirror ProposalsController nested body records (`WorkflowActionBody`). KHÔNG sửa WorkflowAppsFeatures.cs (Region 1 Create/List ở đó). Build 0 err (2 warn DocxRenderer pre-existing). Spec deterministic 100% em main → ACCEPT Case 1. Travel/Vehicle (Wave 2b) + test (Wave 4) deferred. Tag `[s42, p11-a, wave-2a, mirror-proposal-region2]`.
|
||||
- **S41 P11-A Wave 1 SCHEMA — wire ApproveV2+LevelOpinions 4 WorkflowApps (Mig 41 `WireWorkflowAppsApprovalV2`):** Pattern 12-bis cookie-cutter mirror Proposal Mig 38, 13× cumulative. 11 file: 5 entity (4 `{Leave,Ot,Travel,Vehicle}RequestLevelOpinion` + shared `WorkflowAppCodeSequence` Prefix-PK) + 5 EF config (auto-discover ApplyConfigurationsFromAssembly, no manual register) + edit 4 parent (nav LevelOpinions + `WorkflowAppStatus? RejectedFromStatus`) + edit enum (`TravelRequest=9`) + 2 DbSet (IAppDbContext+ApplicationDbContext, 5 each). Mig diff CLEAN: 5 CreateTable + 4 AddColumn (no drift). FK Cascade parent + Restrict Level + UNIQUE composite ({Parent}Id, ApprovalWorkflowLevelId). Applied BOTH DB (Dev + Design). Build 0 err. Wave 2 (App/Controller) + Wave 4 (test) deferred per spec. Spec deterministic 100% (em main chose schema) → ACCEPT Case 1. Tag `[s41, p11-a, 12-bis-13x, mig41]`.
|
||||
- **S35 G-H2 BE CRUD 4 catalog (HrmConfigFeatures.cs 372 LOC + Controller 134 LOC, 16 endpoint):** Pattern 12-bis 3rd application catalog-mega. 4 sub-resource × 4 verb. KEY: HRM no HasQueryFilter → `.Where(!IsDeleted)` manual; Validator MaxLength = EF source-of-truth (Code=50 not spec 20). 130 test baseline preserve. ACCEPT clean spec 95%. Tag `[s35, be-crud, hrm, 12-bis-3x]`.
|
||||
- **S29 Plan B Chunk C Contract V2 mirror (Mig 33 ContractLevelOpinions):** Pattern 12-bis 1st — 8 file +4265 LOC (Designer autogen 95%, handcraft ~232 LOC). Em main spec deterministic 100% → ACCEPT. Tag `[s29, plan-b, 12-bis]`.
|
||||
- **Archived FE/test + older BE entries → `archive/2026-05-q4.md` + git d2f52ba (S40 curate):** S35 FE inline forms 5 satellite (→ frontend domain) · S34 test bundle +10 [Fact] 130 PASS (→ test-specialist domain) · S33 Task 5 EmployeesListPage · S32 wrap/startup. KEY absorbed in Patterns above + split pointers.
|
||||
_(S56 GOLIVE-HARDEN 3 BE fix — ExecuteUpdate-atomic LeaveBalance + fail-closed AssignItTicket authz + DocxRenderer CS8602 → FIFO'd S77; verbatim git/archive. em-main post-review bumped tx → `IsolationLevel.Serializable` per database-agent. Test 228 green.)_
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Curate trigger
|
||||
- >~30KB → archive recent → L2 `archive/<period>.md`. Stale >3mo → remove.
|
||||
- **Last curate: 2026-05-29 S40 em main proxy** (30.9→~18KB): **dedup split** — removed FE patterns (5/6/13/14/15/16-bis → implementer-frontend) + test patterns (10/11/12 → test-specialist), condensed Pattern 12-bis/12-ter, refreshed stale (104/111→130 test, Opus 4.7→4.8 model). BE patterns 1-4/7-9/12-bis/12-ter foundation preserved. Prev: S34 q3 · S32 q2 · S22 q1.
|
||||
- **Last curate: 2026-06-17 S70 Harness-9 (em-main + Stage-B workflow)** (33.2→17.4KB): L2 dark-matter recovery — 14 Recent-activity entries (S55→S35) → NEW `archive/2026-06.md` + `_INDEX.md` (substring sha-keyed) + `2026-0{5,6}.gist.md` (distill-gen:1). 0-byte-loss md5 byte-exact (Stage C audit CONCERN → read-side-gap MEMORY-L5→_INDEX fixed). _(cosmetic: 2 curate-meta lines carry `S?` worker-label.)_ Prev: S40 (30.9→~18KB dedup-split BE/FE/test) · S34 q3 · S22 q1.
|
||||
|
||||
@ -10,6 +10,16 @@ Em main spec deterministic 100% ULTRA-MINIMAL scope (Phase 1 read-only, no separ
|
||||
|
||||
Pattern 16-bis 4-place mirror cross-app reinforced **4th time cumulative** (S29 Plan CA HF1 + S29 Plan B Chunk D + S33 Task 5). Pattern 12-bis cross-module FE port PE → Hrm reinforced **4th time** (Plan B Chunk C Mig 33 + Plan B G-H1 Task 4 BE + Task 5 FE).
|
||||
|
||||
---
|
||||
|
||||
## 2026-05 (S35 G-H2 BE CRUD 4 catalog — archived S65 FIFO trim)
|
||||
|
||||
S35 G-H2 BE CRUD 4 catalog (HrmConfigFeatures.cs 372 LOC + Controller 134 LOC, 16 endpoint): Pattern 12-bis 3rd application catalog-mega. 4 sub-resource × 4 verb. KEY: HRM no HasQueryFilter → `.Where(!IsDeleted)` manual; Validator MaxLength = EF source-of-truth (Code=50 not spec 20). 130 test baseline preserve. ACCEPT clean spec 95%. Tag `[s35, be-crud, hrm, 12-bis-3x]`.
|
||||
|
||||
## 2026-05 (S29 Plan B Chunk C Contract V2 mirror — archived S65 FIFO trim)
|
||||
|
||||
S29 Plan B Chunk C Contract V2 mirror (Mig 33 ContractLevelOpinions): Pattern 12-bis 1st — 8 file +4265 LOC (Designer autogen 95%, handcraft ~232 LOC). Em main spec deterministic 100% → ACCEPT. Tag `[s29, plan-b, 12-bis]`. KEY takeaways absorbed into Pattern 12-bis foundation (current MEMORY).
|
||||
|
||||
**Verify all clean:** fe-admin build PASS (21.4s 0 TS err 1429KB bundle warn expected), fe-user build PASS (9.2s 0 TS err 1345KB bundle warn expected), dotnet build PASS (1.59s 0 warn 0 err), dotnet test PASS **120/120 baseline preserve** (58 Domain + 62 Infra). Ambiguities encountered: 0 — spec deterministic 100%.
|
||||
|
||||
**Files touched:** fe-admin/src/types/employee.ts (NEW ~283 LOC), EmployeesListPage.tsx (NEW ~417 LOC), EmployeeCreatePage.tsx (NEW ~178 LOC), menuKeys.ts (+3 LOC Hrm+HrmHoSo), Layout.tsx (+4 LOC staticMap Hrm_HoSo), App.tsx (+5 LOC imports+routes), 6 fe-user files mirror identical (3 SHA256 + 3 edit mirror Hrm_HoSo).
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
distill-gen: 1
|
||||
# Gist — 2026-05 archive (q1+q2+q3+q4 · S21-S35) · 4-field distillation
|
||||
|
||||
> **distill-gen: 1** — already-distilled; do NOT re-compress. Each block = VIỆC · KẾT-LUẬN(+commit/file:line) · BÀI-HỌC · BẤT-NGỜ, ending `→ substring:"…"` that grep-resolves UNIQUE in the named verbatim file. Pointer-style = substring (Ctrl-F), git-SHA / Mig-name / phrase keyed (dates collide). value tag: cao/vừa/thấp.
|
||||
> Covers verbatim files: 2026-05-q1.md (S21-24), 2026-05-q2.md (S25-29), 2026-05-q3.md (meta wraps), 2026-05-q4.md (S32-33 + S35/S29 stubs). Companion: `2026-06.gist.md` (S41-55).
|
||||
|
||||
---
|
||||
|
||||
## q1 · S21-S24 (PE workflow V2 per-NV flags + ReturnMode + FE tree-prep)
|
||||
|
||||
**[cao] PE per-NV flag schema chain (Mig 29/30/31) + EF backfill discipline.** VIỆC: across S21-S23 wire per-NV admin opt-in flags on ApprovalWorkflowLevel (AllowDrafterEdit/AllowApproverEditSection1/AllowApproverSkipToFinal); Mig 31 swapped F2 storage Users→ApprovalWorkflowLevels via ADD-DROP no-BACKFILL (accept losing 4 prod-user values). KẾT-LUẬN: Pattern 7 reinforced; both DB (Dev+Design) applied; 6 BE file cookie-cutter. BÀI-HỌC: **EF migration BACKFILL reorder pattern** + per-NV permission scope split natural by role (Drafter→User table, Approver→Level table carry ApproverUserId FK); clarify default-behavior vs admin-opt-in BEFORE expanding default scope. BẤT-NGỜ: UserConfiguration.cs does NOT exist — User configured inline in ApplicationDbContext.OnModelCreating ~L86, EF picks prop change with no HasDefaultValue. → substring:"Mig 31 schema swap F2 storage Users → ApprovalWorkflowLevels"
|
||||
|
||||
**[cao] REFUSE-class boundary validated (S21/S22 100% refuse).** VIỆC: em main classified all S21 t3-t5 + all S22 turns as cross-stack reasoning chain (BE Mig+Service+DTO+FE Designer+test) → REFUSE criteria #3 (cross-stack >2 layer) + #4 (bug-fix reasoning chain); em main solo executed. KẾT-LUẬN: REFUSE-class = cross-stack >2 layer + bug-fix reasoning chain = em-main-solo (Smart-Friend bar held). BÀI-HỌC: **gotcha #45 = bug + reasoning** (not mechanical) drove S21 REFUSE; strict scope matched Anthropic "tightly interdependent coding" warning. BẤT-NGỜ: S22 mismatches — API auth field is `accessToken` not `token` (seed script 401); Dialog `size="xl"` unsupported (only sm/md/lg); PE.changelogs absent from PeDetailBundle (TS2339). → substring:"gotcha #45 = bug + reasoning"
|
||||
|
||||
**[vừa] FE Designer per-slot checkbox + label polish (S23 K1/K3/pre-A).** VIỆC: ApprovalWorkflowsV2Page.tsx — 7th checkbox AllowApproverSkipToFinal + banner rewrite + slot label `Quyền duyệt {fullName}` lookup from usersList query (line 873). KẾT-LUẬN: fe-admin only (fe-user has no Designer); 0 TS err, bundle 1395KB unchanged. BÀI-HỌC: EditLevelEntry lacks approverFullName → lookup `usersList.data?.find(u=>u.id===entry.approverUserId)` (Option A). BẤT-NGỜ: per-slot mirror reinforced 3× cumulative (Mig 29 F1+F3 + Mig 30 F4 + Mig 31 F2). → substring:"UI polish slot label Designer Mig 31 prep"
|
||||
|
||||
**[vừa] Zombie F2 cleanup (S23 K5).** VIỆC: drop dead F2 endpoint + UsersPage "Skip cuối" column + DTO field + stale narrative comments (Reviewer Major #1/#2 + Minor #3/#4), 9 file. KẾT-LUẬN: +42/-94 LOC; grep `AllowDrafterSkipToFinal`/`allow-skip-final`/`FastForward` = ZERO across src. BÀI-HỌC: post-refactor full cleanup atomic 1 commit. BẤT-NGỜ: none. → substring:"Cleanup zombie F2 endpoint + UsersPage column"
|
||||
|
||||
**[cao] ReturnMode test discipline (S23 K7 + S24 M2).** VIỆC: K7 delete 3 deprecated Drafter-F2 tests + add 3 Approver-F2 (`ApproveV2_SkipToFinal_*`), reuse Pattern 11 SeedApproverF2WorkflowAsync (2 Step×2 Level verify skip-to-terminal no fallthrough); M2 add 2 [Fact] reset-Bước1Cấp1-keeps-ChoDuyet. KẾT-LUẬN: 104 PASS (K7) → 106 PASS (M2); file:line `PurchaseEvaluationWorkflowServiceReturnModeTests.cs:253` was the broken K1-flagged ref fixed here. BÀI-HỌC: extend SeedWorkflowAsync helper with optional params (default false) instead of cloning — no compat break to 4 existing tests. BẤT-NGỜ: Changelog Summary is fixed `"Chuyển phase {from}→{to}"`; mode text ("không lùi được") lands in ContextNote not Summary. → substring:"Mig 31 Approver F2 service regression tests"
|
||||
|
||||
**[vừa] FE label rename + read-only matrix (S24 M3 + Plan AA-B).** VIỆC: M3 rename Phase=TraLai(98) "Trả lại"→"Cần chỉnh sửa lại" 4 file ×2 app (status-display refs only, NOT action verb); Plan AA-B new WorkflowMatrixViewPage fe-user (10-col table, GET `?isUserSelectable=true` server-filter Mig 25). KẾT-LUẬN: both builds 0 TS err. BÀI-HỌC: read-only mirror of admin Designer = drop mutations + subset DTO + filter BE-side. BẤT-NGỜ (marker #18): **shadcn fe-user lacks Card/Badge** (only Button/Dialog/Input/Label/Select/Textarea) → inline `<div className="rounded-lg border…">` + `<span className="rounded-full bg-…">` fallback. → substring:"FE user read-only matrix view workflow V2 ghim"
|
||||
|
||||
**[thấp] REFUSE cumulative log + AA wrap (S23 t4-t11, S24 AA).** VIỆC: 8 consecutive plans em-main-solo (Plan N..U), incl destructive sqlcmd cleanup prod (~720 rows) + DemoSeed:Disabled flag; AA wrap added Patterns 13/14/15. KẾT-LUẬN: REFUSE 4/4 polish chunks correct (criteria #6 <30min / #2 UX). BÀI-HỌC: audit shadcn fe-user/components/ui BEFORE import (subset lib). BẤT-NGỜ: none. → substring:"8 plan consecutive em main solo"
|
||||
|
||||
## q2 · S25-S29 (audit-log refactor + FE tree + master mirror + Contract V2 dropdown)
|
||||
|
||||
**[cao] Changelog 4-mode uniform log + FE substring filter (S25 AB-A, commit cdfd542).** VIỆC: ApplyReturnModeAsync lines 215-378 refactor — Drafter early-return → if/else common path, single Changelog.Add() at end covering 4 modes (`Trả lại ({modeName}): {summary}`); FE PeDetailTabs ×2 app filter by substring summary ("Trả lại"/"ngân sách") not enum. KẾT-LUẬN: commit cdfd542, +146/-95 LOC, 3 file; no new SaveChanges (caller TransitionAsync saves). BÀI-HỌC: refactor early-return→common-path so ONE log call covers N branches; FE filter via substring keyword (maintainable, no BE schema migrate). cross-ref Pattern 4. BẤT-NGỜ: none. → substring:"ApplyReturnModeAsync` lines 215-378 refactor"
|
||||
|
||||
**[cao] PE list tree view — Pattern 19 (S26 Plan AG, commit 0bf6c7e).** VIỆC: flat PE list → Outlook 2-level folder tree (Project>Gói thầu) via useMemo group + HTML `<details>/<summary>` + Tailwind named groups `group/proj`+`group-open/proj:rotate-90` + localStorage Set<string> persist. KẾT-LUẬN: commit 0bf6c7e, +346/-116 LOC, ×2 app SHA256 IDENTICAL. BÀI-HỌC: **Pattern 19** native details/summary + named-groups + localStorage Set = free tree UI when no Accordion lib (Space/Enter accessible, 0 JS state/node). BẤT-NGỜ: nested SAME-name `group` inherits parent state→both rotate sync (must use distinct named groups). → substring:"`0bf6c7e` 2 file +346/-116 LOC"
|
||||
|
||||
**[cao] 4 master pages byte-identical mirror — Pattern 16 (S27 CA-B, commit 06a441c).** VIỆC: move 4 master pages fe-admin→fe-user via copy (no modify) + menuKeys +5 Catalogs* + App.tsx routes. KẾT-LUẬN: commit 06a441c, 6 file, 948 LOC mirror, SHA256 byte-identical 4 file (C1760788/BDF0529E/68213D62/6F482614). BÀI-HỌC: **Pattern 16** byte-identical admin→user mirror when parity confirmed → SHA256 verify post-write, regression-safe (admin UAT-passed). saved `pattern_master_page_mirror.md`. BẤT-NGỜ: PowerShell `$_` in ForEach-Object eaten by Bash-tool shell-escape → use `Get-FileHash f1,f2 -Algorithm SHA256` list literal. → substring:"Commit `06a441c` 6 file changed"
|
||||
|
||||
**[cao] Contract V2 dropdown FE mirror — Pattern 12-bis cross-module (S29 Plan B-D, commit 62b50d1).** VIỆC: ContractCreatePage ×2 app +useQuery `approval-workflows-v2-contract` (ApplicableType=3, client filter isUserSelectable) + Select dropdown + wire ApprovalWorkflowId into CreateContractCommand (BE field pre-added Chunk E1). KẾT-LUẬN: commit 62b50d1, 2 file, +88 LOC; both builds 0 TS err. BÀI-HỌC: **Pattern 12-bis** PE→Contract clean (same useQuery/Select/filter/POST shape, only discriminator ApplicableType swap); Pattern 16-bis 4-place mirror check (Page/App.tsx/menuKeys/Layout — last 3 N/A here, route enhancement). BẤT-NGỜ: none. → substring:"Commit `62b50d1` clean 2 file"
|
||||
|
||||
**[vừa] S25 wrap — Patterns 16/17/18 NEW.** VIỆC: post-AB-A wrap; 6 follow-ups em-main-solo (synthetic Reject rows, dedupe 5s bucket, userMap fallback). KẾT-LUẬN: Pattern 16 (preventive systemic batch fix N-sites replace_all context-aware key), 17 (FE merge synthetic Changelog rows for audit recovery no-DB-write), 18 (FE userMap fallback from embedded domain data, no extra fetch). BÀI-HỌC: batch-fix 9 same-pattern sites in 1 idempotent pass. BẤT-NGỜ: none. → substring:"Plan AB Chunk A spawn 1× ~12K Case 1"
|
||||
|
||||
## q3 · meta wraps (S27-S29 retrospectives + setup) — mostly [meta]
|
||||
|
||||
**[vừa] S29 wrap — race-condition lesson + Pattern 12-bis NEW.** VIỆC: busiest S29 (5 spawn): 4 master mirror + Plan B 4 chunks (A2 Mig 32 / C Mig 33 ContractLevelOpinions / D FE dropdown / E3 stopped). KẾT-LUẬN: Pattern 12-bis NEW (cross-module entity cookie-cutter PE→Contract 4-file). BÀI-HỌC (marker #16): **race-condition em-main + Implementer parallel BE → stash em-main WIP to build-verify clean; forward SEQUENTIAL A→B→C when both touch BE, NOT parallel**; complex FE mirror w/ type-extend+new-component → em-main-solo more reliable when ambiguity >20%. BẤT-NGỜ: none. → substring:"busiest agent S29 với 5 spawn total"
|
||||
|
||||
**[meta] S28 governance + S27 retrospective.** VIỆC: Layer A distributed governance active; S27 retrospective 2/8 tasks were delegable (Case1+2) but registry empty→forced em-main-solo (~30min loss). KẾT-LUẬN: Pattern 7 proven 4× = Layer-B promote candidate; Pattern 20 (5 PS scripts mirror family). BÀI-HỌC: tag schema `[pattern, phase-<N>, <bc>]`; NO self-authorize cross-project rule. BẤT-NGỜ: none. → substring:"wrap-up retrospective REFUSE analysis"
|
||||
|
||||
**[meta] curate (S29-era) + setup baseline (S11).** VIỆC: archived 12 q1 entries (38.8KB→~22-24KB); S11 agent init w/ 5 patterns S1-S20. KẾT-LUẬN: foundation patterns preserved across curates. BÀI-HỌC: KEEP-vs-CUT §6.5 = em-main judgment (reserve from Implementer). BẤT-NGỜ: none. → substring:"Implementer agent initialized"
|
||||
|
||||
## q4 · S32-S33 + S35/S29 trim-stubs
|
||||
|
||||
**[cao] HRM catalog-mega + Contract V2 (S35 G-H2 + S29 Plan B-C stubs) — Pattern 12-bis foundation.** VIỆC: S35 BE CRUD 4 HRM sub-catalog (HrmConfigFeatures 372 LOC, 16 endpoint, 4 sub × 4 verb); S29 Mig 33 ContractLevelOpinions (8 file +4265 LOC autogen). KẾT-LUẬN: [stub→git d2f52ba] — KEY absorbed into L1 Pattern 12-bis. BÀI-HỌC (markers #14,#15): **HRM entities have NO global HasQueryFilter(!IsDeleted) (unlike Master) → list query MUST `.Where(!IsDeleted)` MANUAL**; **Validator MaxLength = EF config source-of-truth NOT spec (Code=50 not spec's 20)** — verify, don't trust spec blind. BẤT-NGỜ: 130 test baseline preserved through both. → substring:"S35 G-H2 BE CRUD 4 catalog — archived S65 FIFO trim"
|
||||
|
||||
**[vừa] FE HRM scaffold (S33 G-H1 T5).** VIỆC: 3 NEW page ×2 app (employee.ts/EmployeesListPage/EmployeeCreatePage) ultra-minimal read-only 6-section `<details>`. KẾT-LUẬN: SHA256 IDENTICAL ×2 app (CCFC7066/DC859C89/C796F25D); 120 test preserved. BÀI-HỌC: Pattern 16-bis 4-place mirror 4th + Pattern 12-bis FE port PE→Hrm 4th. BẤT-NGỜ: 0 ambiguity (spec deterministic 100%). → substring:"S33 Plan B G-H1 Task 5"
|
||||
|
||||
**[meta] S32 wrap + startup.** VIỆC: curate q2 (36.2→27.5KB); Plan G 11-module backlog documented; size FLAG 36.2KB>25KB. KẾT-LUẬN: 17 patterns confirmed (12-bis + 16-bis SAVED); RAG live rerank 0.824/0.801/0.793 post-CLI-restart. BÀI-HỌC: self-FLAG over-cap, defer curate to em-main §6.5. BẤT-NGỜ: none. → substring:"S32 startup — context verify + RAG live confirm"
|
||||
@ -0,0 +1,53 @@
|
||||
distill-gen: 1
|
||||
# Gist — 2026-06 archive (S41-S55) · 4-field distillation
|
||||
|
||||
> **distill-gen: 1** — already-distilled; do NOT re-compress. Each block = VIỆC · KẾT-LUẬN(+Mig/file:line) · BÀI-HỌC · BẤT-NGỜ, ending `→ substring:"…"` grep-UNIQUE in `2026-06.md` (14 bullets moved byte-exact from MEMORY.md L87-L104, S? Harness-9 curate). Pointer-style = substring (Ctrl-F), Mig-name / phrase keyed. value tag: cao/vừa/thấp.
|
||||
> The final subsection distills L1-HOT markers (S56/S57bis/S65) that REMAIN in MEMORY.md (not moved) — included here only so the coverage checklist resolves in one place; their verbatim lives in MEMORY.md, pointer notes say so.
|
||||
|
||||
---
|
||||
|
||||
## P11-A WorkflowApps wave (S41-S42) — schema + app + seed
|
||||
|
||||
**[cao] Mig 41 schema — 4 WorkflowApps ApproveV2 + LevelOpinions (S41 P11-A W1).** VIỆC: cookie-cutter mirror Proposal Mig 38 — 5 entity (4 {Leave,Ot,Travel,Vehicle}RequestLevelOpinion + shared WorkflowAppCodeSequence Prefix-PK) + 5 EF config (auto-discover ApplyConfigurationsFromAssembly) + 4 parent nav + enum TravelRequest=9 + 2 DbSet. KẾT-LUẬN: Mig 41 WireWorkflowAppsApprovalV2; diff CLEAN 5 CreateTable+4 AddColumn; FK Cascade parent + Restrict Level + UNIQUE composite; both DB. BÀI-HỌC: Pattern 12-bis 13× cumulative. BẤT-NGỜ: none (spec deterministic 100%). → substring:"S41 P11-A Wave 1 SCHEMA — wire ApproveV2+LevelOpinions"
|
||||
|
||||
**[cao] App-layer ApproveV2 CQRS (S42 Wave 2a LeaveOt stub + 2b Travel/Vehicle).** VIỆC: per-module DetailDto+LevelOpinionDto+GetById(JOIN Step/Level)+UpdateDraft+Submit+Approve(UPSERT+advance)+Reject+Return; shared internal CodeGen Serializable-tx. KẾT-LUẬN: TravelVehicleApprovalFeatures ~830 LOC; codes `{prefix}/{seq:D3}` (D3 NO year segment per spec) vs Leave/Ot `{prefix}/{year}/{seq:D3}`. BÀI-HỌC: **WorkflowAppCodeGen Serializable-tx Prefix-keyed**; **WorkflowAppStatus enum DIFFERS ProposalStatus int values (DaGuiDuyet=2 not 1, TraLai=3) → mirror by SEMANTIC enum member NOT literal**; Owner=RequesterUserId (not DrafterUserId); Submit verify wf.ApplicableType else Conflict. BẤT-NGỜ: 2a content absorbed (stub→git) into Pattern 4 + this 2b entry. → substring:"S42 P11-A Wave 2b APP — wire ApproveV2 CQRS Travel+Vehicle"
|
||||
|
||||
**[cao] Seed 4 sample workflow V2 (S42 P11-A) — gotcha #51.** VIỆC: DbInitializer mirror SeedSampleProposalWorkflowV2Async EXACT ×4 (Leave/Ot/Travel/Vehicle), each idempotent AnyAsync(ApplicableType) guard → 1 Workflow+1 Step+1 Level, codes QT-NP/OT/CT/XE-V2-001. KẾT-LUẬN: wired 4 calls; build 0 err 0 warn. BÀI-HỌC (marker #4): **gotcha #51 — infra seed NOT gated by DemoSeed:Disabled** (by-design reaches prod, like SeedDemoMasterData/Catalogs). BẤT-NGỜ: Bash tool runs bash NOT PowerShell despite env hint → use `cd && cmd | grep`. → substring:"S42 P11-A SEED — 4 sample ApprovalWorkflow V2"
|
||||
|
||||
## P11-B/C HRM business + catalogs (S43, S51)
|
||||
|
||||
**[cao] LeaveBalance deduct exactly-once (S43 P11-B W1) — Mig 42.** VIỆC: entity LeaveBalance:AuditableEntity (UserId/LeaveTypeId/Year + Entitled/Used/Adjustment decimal(5,2)); FK LeaveType Restrict + UNIQUE composite (UserId,LeaveTypeId,Year); deduction hook in ApproveLeaveRequestHandler terminal DaDuyet branch ONLY. KẾT-LUẬN: Mig 42 AddLeaveBalances; RemainingDays = Entitled+Adjustment−Used COMPUTED (not stored); GetMy/GetUser lazy-merge in-memory (no EF LEFT JOIN). BÀI-HỌC (marker #12): **deduct exactly-once GUARANTEED by early guard `Status != DaGuiDuyet throw`**; HRM admin convention `[Authorize(Roles="Admin")]` not menu policy. BẤT-NGỜ: OtRequest/Travel/Vehicle untouched — only Leave has balance. → substring:"S43 P11-B Wave 1 — LeaveBalance business logic"
|
||||
|
||||
**[cao] Vehicle+Driver catalogs (S51 P11-C W1) — Mig 44, gotcha #57.** VIỆC: 2 entity + 2 EF config (mirror filtered HolidayConfiguration NOT buggy bare LeaveType) + 2 DbSet + HrmConfigFeatures Region5/6 + Controller 8 endpoint (GET public, write Admin) + MenuKeys + DbInitializer seed. KẾT-LUẬN: Mig 44 AddVehicleAndDriverCatalogs; diff CLEAN 2 CreateTable+2 filtered IX. BÀI-HỌC (markers #57,#13,#14): **Code UNIQUE `.HasFilter("[IsDeleted]=0")`**; admin perm AUTO via MenuKeys.All (SeedAdminPermissions + Program.cs:78 both iterate); HRM no HasQueryFilter→`.Where(!IsDeleted)` manual. BẤT-NGỜ: RAG/Qdrant DOWN → all Read/Grep on-disk. → substring:"S51 P11-C HMW W1 — Vehicle+Driver catalogs HrmConfigs"
|
||||
|
||||
## P11-D/E/F WorkflowApps wave2 (S52)
|
||||
|
||||
**[cao] ItTicket round-robin + SLA (S52 P11-D W2) — Mig 46.** VIỆC: entity +SlaDueAt/SlaWarnedSent/SlaBreached; CreateItTicketHandler SlaWindow static map (Urgent4/High8/Medium24/Low72h) shared w/ ItTicketSlaJob; round-robin least-loaded assign (itDept=Departments.Code=="IT"); new ItTicketSlaJob:BackgroundService (breach+warn only, NO auto-transition). KẾT-LUẬN: Mig 46 AddSlaFieldsToItTicket 3 AddColumn no-table; AssignItTicketCommand PUT /{id}/assign [Authorize(Roles=Admin)]. BÀI-HỌC: **SeedItDepartmentStaffAsync MUST run AFTER SeedDemoUsersAsync** (that method reconciles 2 users to PRO/CCM each boot → override to IT after, end-state deterministic). BẤT-NGỜ: agent killed session-limit before MEMORY → proxy by em main. → substring:"S52 P11-D Wave2 BE — ItTicket round-robin + SLA"
|
||||
|
||||
**[cao] Attendance report + IT codegen (S52 P11-E+F) — NO mig.** VIỆC: P11-F MaTicket gen at Create (kanban no-workflow) `IT/2026/001`; P11-E AttendanceReportFeatures + IAttendanceReportExcelExporter (ClosedXML mirror ContractExcelExporter). KẾT-LUẬN: 2 endpoint [Authorize(Roles=Admin)] GET `/api/attendances/report` + `/report/excel`. BÀI-HỌC: **DayOfWeek+holidaySet NOT EF-translate → `.ToListAsync()` then group/classify IN-MEMORY C#**; **Holiday.Date = DateOnly NOT DateTime (spec wrote HashSet<DateTime> wrong) → HashSet<DateOnly>**; day-type prio holiday→weekend→weekday; OtWeighted=Σ(level×coef). BẤT-NGỜ: spec type-error caught (DateOnly). → substring:"S52 P11-E+F Wave1 BE migration-FREE"
|
||||
|
||||
## Master + cross-stack (S53, S54, S55)
|
||||
|
||||
**[cao] Filter 3 Master unique indexes (S53 gotcha #57 EXT) — Mig 47 + Mig 46 catch-up.** VIỆC: test-before RED→GREEN; edit 3 config Code unique `+.HasFilter("[IsDeleted] = 0")` (Department/Project/Supplier); Supplier ONLY Code filtered, Type index left untouched. KẾT-LUẬN: Mig 47 FilterMasterCatalogUniqueIndexesByIsDeleted; Mig 46 local catch-up closed LocalDB gap; 203 GREEN. BÀI-HỌC (markers #2,#57): **gotcha #57 — soft-delete UNIQUE must filter [IsDeleted]=0**; **copied filter string BYTE-FOR-BYTE from HolidayConfiguration:18 + LeaveTypeConfiguration:19** (spaces around `=`, not guessed) → snapshot+SQL consistent w/ 13 existing filtered idx. BẤT-NGỜ: Master HAS global HasQueryFilter (unlike HRM) → app-check passes but bare DB index counts soft-deleted → 500; filter aligns. → substring:"S53 gotcha #57 EXT BE — filter 3 Master Code unique indexes"
|
||||
|
||||
**[cao] ItTicket reassign capability + fail-closed authz (S54 cross-stack) — NO mig.** VIỆC: new GetAssignableItStaffQuery capability endpoint + AssignItTicketHandler authz Admin-OR-dept-IT→ForbiddenException, assignee-must-be-IT→ConflictException; controller /assign lowered Roles=Admin→[Authorize]. KẾT-LUẬN: 2 patterns saved (controller-lower-authorize-handler-finegrained + scoped-capability-endpoint-anti-silent-403); reviewer chain-verified role-string "Admin" real (AppRoles→SeedRoles→JWT→cu.Roles). BÀI-HỌC (marker #11): **fail-closed authz — Forbidden BEFORE NotFound** (avoid existence-oracle); ICurrentUser lacks DepartmentId → query db.Users. BẤT-NGỜ: em main reconciled 2 patterns from stray src/Backend/.claude (cwd-relative Write mishap). → substring:"S54 ItTicket reassign cross-stack — IT-staff self-service"
|
||||
|
||||
**[vừa] Menu leaf AttendanceReport (S54 Task D) — NO mig.** VIỆC: 3 insert — MenuKeys const Off_AttendanceReport + All[] + DbInitializer menu tuple (Order 8, parent Off). KẾT-LUẬN: admin perm auto via MenuKeys.All 2-point (SeedAdminPermissions :1916 + Program.cs:78). BÀI-HỌC: idempotent seed upsert (existing prod gets leaf on restart, existing rows only Order-reconciled). BẤT-NGỜ: none. → substring:"S54 Task D BE — promote AttendanceReport to sidebar menu leaf"
|
||||
|
||||
**[cao] Real master-data import (S55) — Mig 48 + ungated seed.** VIỆC: Project +4 prop (Year int?, Investor/Location/Package, maxlen 250/500/300); SeedRealMasterDataAsync 3 tuple-loop per-code idempotent, wired UNGATED after SeedCatalogsAsync. KẾT-LUẬN: Mig 48 AddProjectMasterFields 4 AddColumn no-table; seeds 62 Project+71 WorkItem+3 Supplier; runtime Dev proof landed. BÀI-HỌC: real-data import reaches prod by-design (DemoSeed:Disabled NOT gate); FLOCK01 collision demo↔real → per-code skip (demo wins). BẤT-NGỜ: WorkItem divider row "THIẾT BỊ" dropped; agent return truncated #53 → proxy by em main. → substring:"S55 master-data import (Mig 48 `AddProjectMasterFields`"
|
||||
|
||||
## [stub→git d2f52ba] older absorbed (S35/S40 trim)
|
||||
|
||||
**[thấp] S35 BE-CRUD-4-catalog + S29 Contract-V2-mirror.** KEY absorbed into L1 Pattern 12-bis foundation (catalog-mega + HRM `.Where(!IsDeleted)` manual + Validator MaxLength=EF). → substring:"Archived S35 G-H2 BE-CRUD-4-catalog + S29 Plan-B-Chunk-C Contract-V2-mirror"
|
||||
|
||||
**[thấp] S40 curate — FE/test + older BE → git d2f52ba.** S35 FE inline forms 5 satellite + S34 test +10 (130 PASS) + S33 EmployeesListPage + S32 wrap/startup. → substring:"Archived FE/test + older BE entries → `archive/2026-05-q4.md` + git d2f52ba (S40 curate)"
|
||||
|
||||
---
|
||||
|
||||
## L1-HOT markers (still in MEMORY.md — NOT moved; distilled here for coverage completeness)
|
||||
|
||||
> These live VERBATIM in MEMORY.md (kept hot). Pointers below target MEMORY.md, not 2026-06.md.
|
||||
|
||||
- **[cao] S56 GOLIVE-HARDEN — ExecuteUpdate atomic + fail-closed (markers #10,#11).** LeaveBalance lost-update → `ExecuteUpdateAsync` server-side row-lock inside tx (em-main post-review bumped to `IsolationLevel.Serializable`); AssignItTicket existence-oracle → Forbidden guard BEFORE NotFound (fail-closed). STALE-TRACKED caveat: ExecuteUpdate bypasses tracker → don't re-add `bal.UsedDays +=` (double-count). → substring(MEMORY.md):"S56 GOLIVE-HARDEN 3 BE fix"
|
||||
- **[cao] S57bis/S65 loose-Guid FK guard (marker #13).** loose-Guid FK (WorkItemId S57bis, Department.ParentId S65) = NO physical FK; guard `AnyAsync(x.Id==id && x.IsActive)`→Conflict (mirror S43); UpdateDraft null-safe `if (request.WorkItemId is not null)` to avoid null-ing bug-class S42. → substring(MEMORY.md):"FK-guard loose-Guid"
|
||||
- **[vừa] L1 Pattern foundation markers (#3,#5,#9b).** gotcha #17 = EF migration 3-file rule (Pattern 2, BẮT BUỘC commit `.cs`+`.Designer.cs`+snapshot); gotcha #65 = `dotnet build SolutionErp.slnx` includes 2 test projects; Mig 29/30/31 per-NV admin opt-in (Pattern 7, proven 4×). → substring(MEMORY.md):"EF migration 3-file rule (gotcha #17"
|
||||
29
.claude/agent-memory/implementer-backend/archive/2026-06.md
Normal file
29
.claude/agent-memory/implementer-backend/archive/2026-06.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Implementer-Backend — Archive 2026-06 (S41-S55 verbose, curated S? Harness-9)
|
||||
|
||||
> **Archived:** 2026-06-17 Harness-9 Stage B curate (em main proxy). 14 verbose Recent-activity bullets moved BYTE-EXACT from MEMORY.md (L87-L104) to keep L1 HOT slim < 25KB.
|
||||
> **Scope:** S55 master-import → S41 P11-A Wave 1 schema (+ 3 already-condensed Archived-stubs pointing git d2f52ba / earlier q4). FIFO chronological NEWEST-first as they appeared in L1.
|
||||
> **FROZEN:** entries below are verbatim copies — do NOT reflow/edit. Re-grounding of stale counts is a separate pass (additive-only).
|
||||
> **KEEP in MEMORY.md:** header+role+split boundary + BE Patterns 1-4/7-9/12-bis/12-ter + anti-patterns+BE conventions + Curate trigger + 6 newest Recent-activity (06-17 Off_Dashboard → S56 GOLIVE-HARDEN).
|
||||
|
||||
---
|
||||
|
||||
## Moved Recent-activity entries (verbatim, FIFO newest-first as in L1)
|
||||
|
||||
- **S55 master-data import (Mig 48 `AddProjectMasterFields` 4 AddColumn no-table + `SeedRealMasterDataAsync` 62 Project+71 WorkItem+3 Supplier) [proxy by em main — agent return truncated gotcha #53 before MEMORY step]:** Project entity +4 prop (`Year int?`, `Investor/Location/Package string?`, maxlen 250/500/300 ProjectConfiguration). `ProjectFeatures.cs` DTO+CreateCmd+UpdateCmd+validators+handlers+List/Get projections +4 (all nullable, appended end). **`SeedRealMasterDataAsync`** = 3 tuple-loop per-code idempotent (mirror `SeedDemoMasterDataAsync:2185` `existingCodes.Contains→skip`) wired UNGATED line 118 AFTER `SeedCatalogsAsync` → reaches prod (DemoSeed:Disabled=true KHÔNG gate, by-design như SeedDemoMasterData/Catalogs). Project Name=Code khi Excel blank. WorkItem 4 Category (Vật tư16/Thầu phụ30/MEP9/Thiết bị16, gen Code VT/TP/MEP/TB-NN; divider "THIẾT BỊ" dropped). Supplier NTP→NhaThauPhu/NCC→NhaCungCap, extras→Note. **FLOCK01 collision** demo↔real → per-code skip (demo thắng, real code+year only, OK). Compile-fix `MasterCatalogFilteredUniqueTests.cs` +4 null args CreateProjectCommand (necessary build-green). **Runtime Dev proof (em main):** app-start seeded 62proj/71wi/3sup landed, CAL01.Investor col populates, 0 overflow/dup. Build 0/0, test 216. Data spec `scripts/master-import-data.generated.md`. Tag `[s55, master-import, mig48, seed-real-ungated, project-4field]`.
|
||||
- **S54 ItTicket reassign cross-stack — IT-staff self-service (NO migration, 2 BE file edit):** NEW `GetAssignableItStaffQuery`+`AssignableStaffResult(CanReassign,Staff)`+`AssignableStaffDto(Id,FullName)` capability endpoint (REGION 5 WorkflowAppsFeatures.cs) + MODIFIED `AssignItTicketHandler`: authz Admin-OR-dept-IT → `ForbiddenException`; assignee-must-be-IT → `ConflictException`. Controller `/assign` hạ `[Authorize(Roles="Admin")]`→`[Authorize]` (handler enforce fine-grained data-driven) + NEW `GET /assignable-staff`. Predicate IT = reuse round-robin S52 `Departments.Where(Code=="IT" && !IsDeleted)`. `ICurrentUser` KHÔNG có DepartmentId → query `db.Users.Where(Id==cu.UserId).Select(DepartmentId)`. 2 pattern split (em main reconciled từ stray src/Backend/.claude — cwd-relative Write mishap): [[pattern-controller-lower-authorize-handler-finegrained]] + [[pattern-scoped-capability-endpoint-anti-silent-403]]. Build 0/0, test 203→216 (test-specialist +13 authz), reviewer PASS (role-string "Admin" chain-verified real: AppRoles→SeedRoles→JWT ClaimTypes.Role→cu.Roles). Tag `[s54, it-ticket-reassign, capability-endpoint, authz-handler, no-mig]`.
|
||||
- **S54 Task D BE — promote AttendanceReport to sidebar menu leaf (NO migration, 2 file edit):** Case 1 mechanical, menu = DbInitializer idempotent seed (not schema). 3 insert: (1) MenuKeys.cs const `OffAttendanceReport = "Off_AttendanceReport"` after OffChamCong:124 · (2) MenuKeys.cs All[] Off-group line +`OffAttendanceReport` after OffChamCong:158-159 · (3) DbInitializer.cs menu tuple `(OffAttendanceReport, "Báo cáo chấm công", Off, 8, "FileBarChart")` after OffChamCong:1787 (Order 8, parent Off, mirror Vehicle/Driver S51). **adminPermAutoViaAll=TRUE verified 2-point:** `SeedAdminPermissionsAsync` DbInitializer:1916 iterates `MenuKeys.All` → full-CRUD Permission row per missing key (idempotent `existingMenuKeys.Contains`); `Program.cs:78` iterates All × Actions → policy registration. +All[] = both auto, NO manual grant. **Idempotent-add verified:** menu upsert loop DbInitializer:1845-1862 `existingItems.TryGetValue(key)` miss → `MenuItems.Add` (existing prod gets leaf on restart, existing rows only Order-reconciled — same as S51). Build 0 err (2 pre-existing DocxRenderer warn). KHÔNG touch FE (menuKeys.ts/Layout = implementer-frontend) / tests / commit. Tag `[s54, task-d, menu-leaf, no-mig, admin-perm-via-all]`.
|
||||
|
||||
- **S53 gotcha #57 EXT BE — filter 3 Master Code unique indexes + Mig 46 local catch-up (Mig 47 `FilterMasterCatalogUniqueIndexesByIsDeleted`, index-only no-table):** Test-before RED→GREEN driven (test-specialist `MasterCatalogFilteredUniqueTests`, 3 FAIL on unfiltered → must turn GREEN). gotcha #57 4th+5th+6th cumulative (S45 Holiday Mig 43, S51 HRM×3 Mig 45 → now Department/Project/Supplier). Edit 3 config Code unique: `b.HasIndex(x=>x.Code).IsUnique()` → `+.HasFilter("[IsDeleted] = 0")`. **KEY: copied EXACT filter string byte-for-byte from HolidayConfiguration:18 + LeaveTypeConfiguration:19** (spaces around `=`, NOT guessed) → snapshot+SQL Server consistent with 13 existing filtered indexes. SupplierConfiguration: ONLY Code index filtered, `HasIndex(x=>x.Type)` :25 LEFT untouched (verified snapshot 3590 Type no-filter). Mig diff CLEAN: Up=3×DropIndex+3×CreateIndex filtered, Down=3×reverse unfiltered (no drift, no extra table/col). Master entities HAVE global `HasQueryFilter(!IsDeleted)` (unlike HRM) — app-check `db.X.AnyAsync(Code==req.Code)` filters soft-deleted → passed; then bare DB index counted it → UNIQUE violation 500. Filter aligns DB index with app-check. **Mig 46 local catch-up:** S52 left local LocalDB stuck at Mig 45 (prod had 46 via CI, local gap). `database update` to BOTH DBs applied Mig 46 (`AddSlaFieldsToItTicket`) THEN Mig 47 — residual closed. Dev override `--connection SolutionErp_Dev`; Design factory-default `SolutionErp_Design` (both `(localdb)\MSSQLLocalDB`). Build 0 err (2 pre-existing DocxRenderer warn). Full suite 203 GREEN (58 Domain + 145 Infra, +3 new). KHÔNG touch FE/test/commit (em main commits). Tag `[s53, gotcha-57-ext, mig47, mig46-catchup, filter-byte-for-byte]`.
|
||||
|
||||
- **S52 P11-D Wave2 BE — ItTicket round-robin + SLA (Mig 46 `AddSlaFieldsToItTicket`, 3 AddColumn no-table) [proxy by em main: agent killed session-limit trước MEMORY step]:** Entity +SlaDueAt/SlaWarnedSent/SlaBreached. `CreateItTicketHandler`: `SlaWindow` static map (Urgent4/High8/Medium24/Low72h) **shared với `ItTicketSlaJob`** (single-source) → `e.SlaDueAt=CreatedAt+window`. Round-robin least-loaded: `db.Users.Where(DepartmentId==itDeptId && IsActive).OrderBy(db.ItTickets.Count(assigned && !Closed && !Resolved)).ThenBy(Id).First()` (itDept = `Departments.Code=="IT"`); no IT-staff → unassigned. NEW `ItTicketSlaJob:BackgroundService` mirror SlaExpiryJob (30s warmup/15min loop/scope) NHƯNG **KHÔNG auto-transition** — chỉ breach (SlaDueAt<now & !breached & open → SlaBreached=true + notify assignee) + warning (≤20% window → notify + SlaWarnedSent), idempotent qua guard. DI `AddHostedService<ItTicketSlaJob>` sau SlaExpiryJob. `AssignItTicketCommand` + PUT `/{id}/assign` `[Authorize(Roles=Admin)]`. DTO +SlaDueAt/SlaBreached + projection. **DbInitializer `SeedItDepartmentStaffAsync` KEY ordering: PHẢI chạy SAU `SeedDemoUsersAsync`** (method đó reconcile 2 user dept về PRO/CCM mỗi boot → override về IT sau, end-state deterministic) + dept "IT" thứ 10 + gán nv.cao/nv.truong (sample user, idempotent). Build 0-err. Tag `[s52, p11-d, mig46, round-robin, sla-job, seed-ordering]`.
|
||||
|
||||
- **S52 P11-E+F Wave1 BE migration-FREE (4 file new + 3 edit, NO mig):** Case 1/2 deterministic ~98% em main. **P11-F** (2 LOC): `CreateItTicketHandler` set `e.MaTicket = WorkflowAppCodeGen.GenerateMaDonTuAsync(db,"IT",clock.Now.Year,clock,ct)` TRƯỚC `db.ItTickets.Add` — gen lúc Create (kanban no-workflow, khác Leave/OT gen lúc Submit). Helper = `internal static` cùng ns Office, gọi trực tiếp no-using. Format `IT/2026/001`. **P11-E** report chấm công: NEW `AttendanceReportFeatures.cs` (DTO 3 record EXACT + GetAttendanceReportHandler) + NEW `IAttendanceReportExcelExporter`(App/Reports/Services) + NEW Infra `AttendanceReportExcelExporter`(ClosedXML mirror ContractExcelExporter, sync `Export(dto)` no-DB) + DI scoped + Controller +2 endpoint `[Authorize(Roles=Admin)]`. **KEY gotcha day-type:** DayOfWeek+holidaySet KHÔNG EF-translate → `.ToListAsync()` rồi group/classify IN-MEMORY C#. **KEY gotcha type:** `Holiday.Date`=**DateOnly** KHÔNG DateTime (spec viết HashSet<DateTime> nhầm) → `HashSet<DateOnly>` + so `DateOnly.FromDateTime(a.AttendanceDate.Date)`. OtPolicy active fallback 1.5/2.0/3.0. Day-type prio: holiday→weekend(Sat/Sun)→weekday. OtWeighted=Σ(cấp×hệ số). FullName denorm ưu tiên `Attendance.UserFullName` rồi User.FullName. DeptName LEFT JOIN. RenderResult=(Content,FileName,ContentType) ns Forms.Services. Controller inject exporter ctor. Build 0 err (2 pre-existing DocxRenderer warn). KHÔNG touch FE/test/mig/ItTicket-khác. Routes: GET `/api/attendances/report?year&month&departmentId` + `/report/excel`. Tag `[s??, p11-e-f, wave1, no-mig, day-type-in-memory, dateonly-holiday]`.
|
||||
|
||||
- **S51 P11-C HMW W1 — Vehicle+Driver catalogs HrmConfigs (Mig 44 `AddVehicleAndDriverCatalogs`, 9 add-point ~12 file/edit):** Pattern 12-bis catalog-mega 4th cumulative. 2 entity (`Vehicle`/`Driver`:AuditableEntity Domain/Hrm) + 2 EF config (mirror `HolidayConfiguration` filtered, NOT buggy bare LeaveType) + 2 DbSet (IAppDbContext+ApplicationDbContext) + Mig 3-file + HrmConfigFeatures Region5/6 (DTO+List/Create/Update/Delete CQRS mirror Region1 EXACT) + Controller +2 route group (8 endpoint, GET public + POST/PUT/DELETE `[Authorize(Roles=Admin)]`) + MenuKeys +2 const +All array + DbInitializer (menu 2 leaf + SeedHrmConfigsAsync guard&seed 2 veh+2 drv). **gotcha #57 KEY:** Code UNIQUE `.HasFilter("[IsDeleted]=0")` — Mig diff verified CLEAN 2 CreateTable + 2 filtered IX no drift. Validator MaxLength = em main schema (Code50/Name200/Plate20/Phone20/LicNum50/LicClass20/Desc500), SeatCount GreaterThanOrEqualTo(0). Admin perm AUTO-grant: `SeedAdminPermissionsAsync` + `Program.cs:78` both iterate `MenuKeys.All` → +All array = 8 policy + Admin row auto (no manual grant code). HRM no HasQueryFilter → `.Where(!IsDeleted)` manual. Applied BOTH DB. Build 0 err (2 pre-existing DocxRenderer warn). RAG/Qdrant DOWN → all Read/Grep on-disk. Spec deterministic ~98% em main → ACCEPT Case 1. KHÔNG touch FE/test/commit. Tag `[s51, p11-c, mig44, vehicle-driver, catalog-mega]`.
|
||||
- **S43 P11-B Wave 1 — LeaveBalance business logic (Mig 42 `AddLeaveBalances`, 7 file: 1 entity + 1 config + 2 DbSet edit + Mig 3-file + 1 hook edit + 1 Features + 1 Controller):** Case 1/3 deterministic ~98% em main spec. Pattern 12-ter-adjacent single-entity: entity `LeaveBalance:AuditableEntity` (UserId/LeaveTypeId/Year + EntitledDays/UsedDays/AdjustmentDays decimal(5,2), nav LeaveType). Config FK LeaveType WithMany() **Restrict** (catalog no cascade) + UNIQUE composite (UserId,LeaveTypeId,Year) + IX UserId. Mig diff CLEAN: 1 CreateTable + 3 IX, no drift. Applied BOTH DB (Dev `SolutionErp_Dev` + Design default). **Deduction hook:** insert in `ApproveLeaveRequestHandler` terminal else (DaDuyet branch) ONLY — UPSERT LeaveBalance, `bal.UsedDays += p.NumDays`, exactly-once guaranteed by early guard `Status != DaGuiDuyet throw`. OtRequest/Travel/Vehicle UNTOUCHED (only Leave has balance). CQRS `Application.Hrm`: DTO RemainingDays=Entitled+Adjustment−Used COMPUTED (not stored) + GetMy/GetUser lazy-merge (load active LeaveTypes + balances → in-memory merge, synth default when no row — KHÔNG EF LEFT JOIN translate) + AdjustLeaveBalanceCommand admin upsert (HasValue-gated). **Policy resolved:** HRM admin convention = `[Authorize(Roles="Admin")]` NOT menu policy (verified HrmConfigsController write endpoints) — used on GET-by-user + PUT /adjust; /my = `[Authorize]`. Controller injects IDateTime for year default `clock.Now.Year` (thin, no DateTime.Now hardcode). HRM no HasQueryFilter → `.Where(!IsDeleted)` manual everywhere. KHÔNG touch FE/test/commit. Build 0 err (2 pre-existing DocxRenderer warn). Tag `[s43, p11-b-w1, mig42, leave-balance, single-entity]`.
|
||||
- **S42 P11-A SEED — 4 sample ApprovalWorkflow V2 for WorkflowApps (DbInitializer.cs only, +4 method ~210 LOC + 4 call):** Case 1 mechanical mirror `SeedSampleProposalWorkflowV2Async` EXACT × 4 (Leave5/Ot6/Travel9/Vehicle7). Each: idempotent `AnyAsync(ApplicableType==X)` guard → resolve approver `binh.le@solutions.com.vn` (SAME user as Proposal/Contract seed, null→LogWarning+return) → 1 ApprovalWorkflow (Version=1, IsActive+IsUserSelectable=true, ActivatedAt=UtcNow) + 1 Step (Order=1, Name="Cấp duyệt", DepartmentId=CCM?.Id) + 1 Level (Order=1, ApproverUserId). Codes QT-NP/OT/CT/XE-V2-001. Wired 4 calls after SeedSampleProposalWorkflowV2Async (NOT gated DemoSeed, gotcha #51 infra seed). Enum verified Grep. Build 0 err 0 warn. Bash tool = bash NOT PowerShell despite env hint (use `cd && cmd | grep`). Spec deterministic 100% → ACCEPT Case 1. NOT touched App/Controller/FE/test/Mig. Tag `[s42, p11-a, seed, mirror-proposal-exact]`.
|
||||
- **S42 P11-A Wave 2b APP — wire ApproveV2 CQRS Travel+Vehicle (`TravelVehicleApprovalFeatures.cs` ~830 LOC + 2 controller edit):** Cookie-cutter mirror Wave 2a / ProposalFeatures Region 2. 1 new file ns `Application.Office`: 2 module × (DetailDto + LevelOpinionDto + GetById JOIN Step/Level metadata + UpdateDraft + Submit + Approve UPSERT+advance + Reject TuChoi + Return TraLai+RejectedFromStatus) + 1 shared `internal static TravelVehicleCodeGen.GenerateMaDonTuAsync` (Serializable tx, `WorkflowAppCodeSequences` Prefix-keyed, prefix `DT/CT/{year}` Travel & `DX/XE/{year}` Vehicle, format `{prefix}/{seq:D3}` — D3 no year segment per spec). KEY gotcha: WorkflowAppStatus enum DIFFERS from ProposalStatus int values (DaGuiDuyet=2 not 1, TraLai=3) → mirror by SEMANTIC enum member not literal. Owner = `RequesterUserId` (not DrafterUserId). Submit verify wf.ApplicableType==Travel9/Vehicle7 else Conflict. 2 controller +6 route each (GET{id}/PUT/submit/approve/reject/return) nested body records, CreatedAtAction. KHÔNG sửa WorkflowAppsFeatures.cs/Leave/Ot/FE/test/seed. Build 0 err. Spec deterministic ~98% em main → ACCEPT Case 1/2. Tag `[s42, p11-a, wave-2b, mirror-proposal-region2]`.
|
||||
- **Archived S42 P11-A Wave 2a APP (LeaveOt ApproveV2 CQRS) → git history (S65b FIFO trim):** KEY absorbed Pattern 4 + WorkflowAppCodeGen Serializable-tx Prefix-keyed `{prefix}/{year}/{seq:D3}` + WorkflowAppStatus enum DIFFERS ProposalStatus (mirror SEMANTIC member not literal) + Owner=RequesterUserId. Detail in Wave 2b entry above + S56 em-main Serializable note.
|
||||
- **S41 P11-A Wave 1 SCHEMA — wire ApproveV2+LevelOpinions 4 WorkflowApps (Mig 41 `WireWorkflowAppsApprovalV2`):** Pattern 12-bis cookie-cutter mirror Proposal Mig 38, 13× cumulative. 11 file: 5 entity (4 `{Leave,Ot,Travel,Vehicle}RequestLevelOpinion` + shared `WorkflowAppCodeSequence` Prefix-PK) + 5 EF config (auto-discover ApplyConfigurationsFromAssembly, no manual register) + edit 4 parent (nav LevelOpinions + `WorkflowAppStatus? RejectedFromStatus`) + edit enum (`TravelRequest=9`) + 2 DbSet (IAppDbContext+ApplicationDbContext, 5 each). Mig diff CLEAN: 5 CreateTable + 4 AddColumn (no drift). FK Cascade parent + Restrict Level + UNIQUE composite ({Parent}Id, ApprovalWorkflowLevelId). Applied BOTH DB (Dev + Design). Build 0 err. Wave 2 (App/Controller) + Wave 4 (test) deferred per spec. Spec deterministic 100% (em main chose schema) → ACCEPT Case 1. Tag `[s41, p11-a, 12-bis-13x, mig41]`.
|
||||
- **Archived S35 G-H2 BE-CRUD-4-catalog + S29 Plan-B-Chunk-C Contract-V2-mirror → `archive/2026-05-q4.md` (S65 FIFO trim):** KEY absorbed Pattern 12-bis foundation above (catalog-mega + HRM `.Where(!IsDeleted)` manual + Validator MaxLength=EF-source).
|
||||
- **Archived FE/test + older BE entries → `archive/2026-05-q4.md` + git d2f52ba (S40 curate):** S35 FE inline forms 5 satellite (→ frontend domain) · S34 test bundle +10 [Fact] 130 PASS (→ test-specialist domain) · S33 Task 5 EmployeesListPage · S32 wrap/startup. KEY absorbed in Patterns above + split pointers.
|
||||
66
.claude/agent-memory/implementer-backend/archive/_INDEX.md
Normal file
66
.claude/agent-memory/implementer-backend/archive/_INDEX.md
Normal file
@ -0,0 +1,66 @@
|
||||
# Archive Index — implementer-backend (L2 catalog, NO content)
|
||||
|
||||
> **Purpose:** rescue L2 archive "dark-matter" (not in RAG). One line per archived record so a reader can locate + Ctrl-F the verbatim text. This index holds NO record bodies — see `<file>.gist.md` for 4-field distillations, and the named archive file for full verbatim.
|
||||
> **Pointer style = substring (Ctrl-F) PRIMARY.** Each row ends `substring:"…"` — a string that greps UNIQUE (count=1) inside its target file. Open the file, Ctrl-F the string, land on the record. Fallback hint = the record's `## /### ` heading (date + session). NO line-number hints (archives are frozen but line numbers drift if ever re-touched).
|
||||
> **Why not date pointers:** dates COLLIDE — q1 has 5× `2026-05-14` + 4× `2026-05-15` + 2× `2026-05-13`; q2 2× `05-19` + 2× `05-22`; q3 4× `05-22`; q4 3× `05-26`. So every pointer is keyed on a git-SHA (`cdfd542`/`0bf6c7e`/`06a441c`/`62b50d1`) or a distinctive Mig-name / phrase, never the bare date.
|
||||
> **Archives are FROZEN / additive-only.** `2026-05-q1..q4.md` are verbatim history (do NOT edit). `2026-06.md` (new, Harness-9 curate) holds 14 bullets moved byte-exact out of MEMORY.md L87-L104.
|
||||
> **Labels:** `[meta]` = curate/governance/setup note (low code value). `[stub→git]` = FIFO-trim stub whose real content lives in git `d2f52ba` (do not expect rich body).
|
||||
> **Sorted by DATE** (ISO 05-xx first; then session-keyed S41→S55 records in `2026-06.md`, which are chronologically post-`05-26`).
|
||||
|
||||
---
|
||||
|
||||
## archive/2026-05-q1.md (S21-S24 · 12 records · verbose)
|
||||
|
||||
- 2026-05-13 · REFUSE-class log (S21 t3-t5) · 3× REFUSE cross-stack; gotcha #45 bug+reasoning; per-NV split + EF backfill-reorder pattern saved · substring:"gotcha #45 = bug + reasoning"
|
||||
- 2026-05-13 · REFUSE-class log (S22) · 100% REFUSE; state 30 mig/104 test; 4 S22 mismatches (accessToken, size=xl, changelogs field) · substring:"30 migrations** (+1 Mig 30 AllowApproverEditSection1"
|
||||
- 2026-05-14 · FE Designer label polish (S23 pre-A) · ApprovalWorkflowsV2Page.tsx:873 fullName lookup from usersList · substring:"UI polish slot label Designer Mig 31 prep"
|
||||
- 2026-05-14 · BE Mig 31 schema swap (S23 K1) · F2 storage Users→ApprovalWorkflowLevels, ADD-DROP no-BACKFILL, both DB · substring:"Mig 31 schema swap F2 storage Users → ApprovalWorkflowLevels"
|
||||
- 2026-05-14 · FE Designer 7th checkbox (S23 K3) · AllowApproverSkipToFinal + banner rewrite, per-slot mirror 3× · substring:"FE Admin Designer 7th checkbox AllowApproverSkipToFinal"
|
||||
- 2026-05-14 · BE+FE zombie cleanup (S23 K5) · drop F2 endpoint/column/DTO/stale comments, 9 file +42/-94 · substring:"Cleanup zombie F2 endpoint + UsersPage column"
|
||||
- 2026-05-14 · Test Approver F2 regression (S23 K7) · del 3 Drafter F2 + add 3 Approver F2; 104 PASS; SeedApproverF2WorkflowAsync · substring:"Mig 31 Approver F2 service regression tests"
|
||||
- 2026-05-15 · Test ReturnMode edge (S24 M2) · 2 [Fact] reset Bước1Cấp1 keep ChoDuyet; 106 PASS · substring:"Plan M Chunk M2"
|
||||
- 2026-05-15 · FE rename label (S24 M3) · Phase=TraLai(98) "Trả lại"→"Cần chỉnh sửa lại", 4 file ×2 app · substring:"rename Phase=TraLai (98) display label"
|
||||
- 2026-05-15 · REFUSE-class log (S23 t4-t11) · 8 plans em main solo; Plan R/S/T destructive sqlcmd ~720 rows; DemoSeed flag · substring:"8 plan consecutive em main solo"
|
||||
- 2026-05-15 · FE read-only matrix (S24 Plan AA B) · WorkflowMatrixViewPage fe-user; shadcn lacks Card/Badge→inline fallback · substring:"FE user read-only matrix view workflow V2 ghim"
|
||||
- 2026-05-15 · [meta] wrap (S24 Plan AA) · Patterns 13/14/15 NEW; REFUSE 4/4 correct; shadcn fe-user subset note · substring:"shadcn fe-user KHÔNG có `Card`/`Badge`"
|
||||
|
||||
## archive/2026-05-q2.md (S25-S29 · 5 records · verbose)
|
||||
|
||||
- 2026-05-19 · BE Changelog refactor (S25 AB-A) · commit cdfd542; ApplyReturnModeAsync 215-378 4-mode uniform log; FE substring filter · substring:"Commit `cdfd542` 3 file +146/-95 LOC"
|
||||
- 2026-05-19 · [meta] wrap (S25) · Patterns 16/17/18 NEW (batch fix / FE synthetic rows / userMap fallback); 6 follow-ups em main solo · substring:"Plan AB Chunk A spawn 1× ~12K Case 1"
|
||||
- 2026-05-21 · FE PE list tree (S26 Plan AG) · commit 0bf6c7e; Pattern 19 details/summary + localStorage Set; ×2 app SHA256 match · substring:"`0bf6c7e` 2 file +346/-116 LOC"
|
||||
- 2026-05-22 · FE 4 master pages mirror (S27 CA-B) · commit 06a441c; Pattern 16 byte-identical admin→user SHA256; PS $_ gotcha · substring:"Commit `06a441c` 6 file changed"
|
||||
- 2026-05-22 · FE Contract V2 dropdown (S29 Plan B-D) · commit 62b50d1; Pattern 12-bis + 16-bis ×2 app; ApplicableType=3 · substring:"Commit `62b50d1` clean 2 file"
|
||||
|
||||
## archive/2026-05-q3.md (S27-S29 wraps + setup · 5 records · mostly meta)
|
||||
|
||||
- 2026-05-22 · [meta] S29 wrap · 5 spawn busiest; Pattern 12-bis NEW; stash em-main WIP race trick; forward SEQUENTIAL not parallel BE · substring:"busiest agent S29 với 5 spawn total"
|
||||
- 2026-05-22 · [meta] S28 governance · Layer A distributed; Pattern 7 proven 4× Layer-B candidate; no self-authorize cross-project · substring:"S28 wrap Layer A governance distributed active"
|
||||
- 2026-05-22 · [meta] S27 retrospective · 2/8 tasks delegable but registry empty→em-main solo; Pattern 20 (5 PS scripts mirror) · substring:"wrap-up retrospective REFUSE analysis"
|
||||
- 2026-05-22 · [meta] curate (S29-era) · archived 12 q1 entries; 38.8KB→~22-24KB · substring:"Archived 12 verbose Recent activity entries S21 t3"
|
||||
- 2026-05-11 · [meta] setup baseline · agent initialized; 5 patterns S1-S20; awaiting first SendMessage · substring:"Implementer agent initialized"
|
||||
|
||||
## archive/2026-05-q4.md (S32-S33 + S35/S29 trim-stubs · 5 records)
|
||||
|
||||
- 2026-05-26 · FE HRM scaffold (S33 G-H1 T5) · 3 NEW page ×2 app SHA256 IDENTICAL; Pattern 16-bis 4th + 12-bis 4th · substring:"S33 Plan B G-H1 Task 5"
|
||||
- 2026-05 · [stub→git] BE CRUD 4 catalog (S35 G-H2) · HrmConfigFeatures 372 LOC 16 endpoint; HRM no HasQueryFilter→.Where(!IsDeleted); Validator MaxLength=EF (Code=50 not 20) · substring:"S35 G-H2 BE CRUD 4 catalog — archived S65 FIFO trim"
|
||||
- 2026-05 · [stub→git] Contract V2 mirror (S29 Plan B-C) · Mig 33 ContractLevelOpinions; Pattern 12-bis 1st; 8 file +4265 LOC autogen · substring:"S29 Plan B Chunk C Contract V2 mirror — archived S65 FIFO trim"
|
||||
- 2026-05-26 · [meta] S32 wrap · curate q2 36.2→27.5KB; Plan G 11-module backlog; S33 pending tasks list · substring:"S32 wrap — em main proxy curate + Plan G 11 module"
|
||||
- 2026-05-26 · [meta] S32 startup · size FLAG 36.2KB>25KB; 17 patterns confirmed; RAG live rerank 0.824/0.801/0.793 · substring:"S32 startup — context verify + RAG live confirm"
|
||||
|
||||
## archive/2026-06.md (S41-S55 · 14 records moved byte-exact from MEMORY.md L87-L104, S? Harness-9 curate · chronologically post-2026-05-26)
|
||||
|
||||
- S41 · BE schema 4 WorkflowApps (P11-A W1) · Mig 41 WireWorkflowAppsApprovalV2; Pattern 12-bis 13×; 5 CreateTable+4 AddColumn both DB · substring:"S41 P11-A Wave 1 SCHEMA — wire ApproveV2+LevelOpinions"
|
||||
- S42 · [stub→git] BE App LeaveOt ApproveV2 (Wave 2a) · absorbed Pattern 4 + WorkflowAppCodeGen Serializable-tx {prefix}/{year}/{seq:D3} + status enum DIFFERS ProposalStatus + Owner=RequesterUserId · substring:"Archived S42 P11-A Wave 2a APP (LeaveOt ApproveV2 CQRS) → git history"
|
||||
- S42 · BE App Travel+Vehicle ApproveV2 (Wave 2b) · TravelVehicleApprovalFeatures ~830 LOC; mirror SEMANTIC enum member not literal; D3 no-year · substring:"S42 P11-A Wave 2b APP — wire ApproveV2 CQRS Travel+Vehicle"
|
||||
- S42 · BE seed 4 workflow V2 (P11-A) · DbInitializer mirror SeedSampleProposalWorkflowV2 ×4; UNGATED gotcha #51; Bash tool=bash not PowerShell · substring:"S42 P11-A SEED — 4 sample ApprovalWorkflow V2"
|
||||
- S43 · BE LeaveBalance logic (P11-B W1) · Mig 42 AddLeaveBalances; deduct exactly-once via early Status!=DaGuiDuyet throw; RemainingDays computed · substring:"S43 P11-B Wave 1 — LeaveBalance business logic"
|
||||
- S51 · BE Vehicle+Driver catalogs (P11-C W1) · Mig 44; Pattern 12-bis catalog-mega 4×; gotcha #57 Code UNIQUE HasFilter [IsDeleted]=0; admin perm via MenuKeys.All · substring:"S51 P11-C HMW W1 — Vehicle+Driver catalogs HrmConfigs"
|
||||
- S52 · BE attendance report + IT codegen (P11-E+F) · NO mig; day-type classify IN-MEMORY (DayOfWeek+holidaySet no EF-translate); Holiday.Date=DateOnly not DateTime · substring:"S52 P11-E+F Wave1 BE migration-FREE"
|
||||
- S52 · BE ItTicket round-robin+SLA (P11-D W2) · Mig 46 AddSlaFieldsToItTicket; SlaWindow shared w/ job; SeedItDeptStaff AFTER SeedDemoUsers ordering · substring:"S52 P11-D Wave2 BE — ItTicket round-robin + SLA"
|
||||
- S53 · BE filter 3 Master unique idx (gotcha #57 EXT) · Mig 47; copy filter string byte-for-byte from HolidayConfiguration; Mig 46 local catch-up; 203 GREEN · substring:"S53 gotcha #57 EXT BE — filter 3 Master Code unique indexes"
|
||||
- S54 · BE menu leaf AttendanceReport (Task D) · NO mig; admin perm auto via MenuKeys.All 2-point; idempotent seed upsert · substring:"S54 Task D BE — promote AttendanceReport to sidebar menu leaf"
|
||||
- S54 · BE ItTicket reassign capability (cross-stack) · NO mig; fail-closed authz Forbidden BEFORE NotFound (existence-oracle); 2 patterns saved · substring:"S54 ItTicket reassign cross-stack — IT-staff self-service"
|
||||
- S55 · BE master-data import · Mig 48 AddProjectMasterFields 4 AddColumn; SeedRealMasterDataAsync UNGATED 62 Project+71 WorkItem+3 Supplier; runtime Dev proof · substring:"S55 master-data import (Mig 48 `AddProjectMasterFields`"
|
||||
- S35 · [stub→git] BE CRUD 4 catalog + Contract V2 (S40 trim) · KEY absorbed into Pattern 12-bis foundation (catalog-mega + HRM .Where(!IsDeleted) + Validator MaxLength=EF) · substring:"Archived S35 G-H2 BE-CRUD-4-catalog + S29 Plan-B-Chunk-C Contract-V2-mirror"
|
||||
- S35 · [stub→git] FE/test + older BE (S40 curate) · git d2f52ba: S35 FE inline forms 5 satellite + S34 test +10 + S33 EmployeesListPage + S32 wrap/startup · substring:"Archived FE/test + older BE entries → `archive/2026-05-q4.md` + git d2f52ba (S40 curate)"
|
||||
@ -6,6 +6,29 @@
|
||||
|
||||
---
|
||||
|
||||
## 🆕 S79 (2026-06-19) — PE detail: live thousand-sep + ghi chú giá PRO/CCM + typo (×2 app)
|
||||
3 task presentation+wiring `PeDetailTabs.tsx` (+`PeWorkspaceCreateView.tsx` typo). BE thêm `note` body song song; FE chốt UX rồi.
|
||||
- **A live phân cách:** chỉ **2 comp** thiếu sep — `VndInlineEdit` (~990) + `BudgetCell` (~1100): đổi `onChange` `value.replace(/[^\d.]/g,'')` → **`formatVndInput(parseVnd(e.target.value))`** (format-on-keystroke, reuse helper module-level :51/52). Dialog money inputs (2111/2555/2645) ĐÃ dùng `formatVndInput`/`parseVnd` sẵn → no-op. KHÔNG đụng phone(tel)/%/qty.
|
||||
- **B ghi chú giá:** `SuggestedPriceRows` (~1509) — `proPriceMut` body `{minPrice,maxPrice,note}` + `ccmPriceMut` `{ccmPrice,note}` (absolute-set 3-field S74). 3 call-site giá echo `note:ev.*SuggestedPriceNote`. State `proNoteText/ccmNoteText` (useEffect sync ev.*). Textarea+nút "Lưu ghi chú" dưới PRO Min/Max & dưới CCM (gate canEdit; read-only `<p>` khi có note). `hasAnyValue` +`!!note` (early-return không nuốt note-only readonly).
|
||||
- **Type DIVERGE 2 app** (xác nhận S76): `PeDetailBundle` +`proSuggestedPriceNote`+`ccmSuggestedPriceNote` sửa **CẢ 2 file** riêng (sau `ccmSuggestedPrice`). PeDetailTabs/PeWorkspaceCreateView byte-identical → cp.
|
||||
- **C typo:** `d. Bản so sánh` → `d. Bảng so sánh giá` PeDetailTabs:1661(`<span>`) + PeWorkspaceCreateView:278(`label`). Comment :1712 + `PeAttachmentPurposeLabel[4]` GIỮ (ngoài spec, không UI).
|
||||
- SHA256 IDENTICAL ×2 pair: PeDetailTabs `285ebcf9…`, PeWorkspaceCreateView `99caed4b…`. Build PASS ×2 (user 1935mod `index-_axiw6cM.js` / admin 1946mod `index-BqPYp7Pt.js`, 0 TS err). Pre-existing warn: @import CSS + >500KB + realtime.ts. NO ambiguity, full precedent. Tag `[s79, pe-live-vnd-sep, suggested-price-note, sha256-2pair, type-diverge-2app]`.
|
||||
|
||||
## 🆕 S78 (2026-06-19) — PE "focus mode" redesign mirror fe-user→fe-admin (CONVERGE, em-main chốt UX)
|
||||
Mechanical mirror-step: redesign built+reviewed-PASS ở fe-user, em-main đã chốt UX (focus overlay). fe-admin ONLY, NO BE/schema. 2 file:
|
||||
- **PeWorkflowPanel.tsx** PURE-ADDITIVE: +`onApproved?:()=>void` prop + `wasReject` const (byte-mirror `isReject` formula mutationFn) + `if(!wasReject) onApproved?.()` cuối `onSuccess`. Trả-lại/Từ-chối giữ overlay; chỉ forward-approve auto-đóng.
|
||||
- **PurchaseEvaluationsListPage.tsx** STRUCTURAL port: bỏ 3-panel grid → list-state `mx-auto max-w-5xl` panel (4-level tree giữ verbatim) + focus-state `?id` = `fixed inset-0 z-50` overlay slide-from-right (translate-x-full→0, 2× rAF ~200ms) che menu+TopBar, top-strip "← Thu gọn"+title+"Chế độ duyệt" pill, phiếu trái scroll-riêng + panel duyệt phải `lg:w-[24rem] xl:w-[26rem]` stack <lg. a11y: role=dialog aria-modal, Esc, focus-trap, scroll-lock, backdrop-click, prefers-reduced-motion. `selectRow` matchMedia-split → `setParam('id',id)` UNCONDITIONAL; MAIN comp drop `navigate` (→ `useCallback setParam`); bottom `PurchaseEvaluationDetailPage` GIỮ `useNavigate` (deep-link compat).
|
||||
- **PRESERVE fe-admin props** (rule): `<PeDetailTabs readOnly={true}>` + `<PeWorkflowPanel readOnly={!pendingMe} onApproved={closeFocus}>`. Tình cờ trùng fe-user → redesign CONVERGE 2 app.
|
||||
- **SHA256 IDENTICAL ×2 pair** (khớp tới trailing-newline `}\n\n`): page `011968bb…`, panel `1dc24641…`. LESSON: focus-overlay redesign khiến 2 app converge (props trùng) → mirror-step ra file byte-identical, đo bằng `diff`+`sha256sum` KHÔNG mắt. Build PASS (1946 mod, `index-D6IyFKgP.js`, 0 TS err — tsc-b clean trước vite). Pre-existing warning: @import-order CSS + >500KB chunk + realtime.ts dynamic-import. NO ambiguity, full precedent. Tag `[s78, pe-focus-mode, mirror-step, sha256-2pair, converge-2app]`.
|
||||
|
||||
## 🆕 S76 (2026-06-19) — PE budget MA TRẬN 3 cột + bảng lưới `<table>` + badge quyền NS (anh Kiệt FDC)
|
||||
- **Part 1 mirror** (fe-user→fe-admin byte-identical, Python difflib slice-region): `PeBudgetSummaryTable` Block A → ma trận 3 cột (DỰ ÁN hiển-thị-only `—` / PRO `canEditPro` / CCM `canEditCcm`). Type `PeBudgetSummary` +`proInitialAmount`+`proAdjustmentAmount` cạnh `proEstimateAmount`(LEGACY). `proMut` body `{proInitialAmount,proAdjustmentAmount,proNote}`. proFull=proInit+proAdj.
|
||||
- **Part 3 mirror:** `PeWorkflowPanel` approver `.join(' / ')` → map từng approver + badge `✎ NS PRO`(amber)/`✎ NS CCM`(sky) gate `a.canEditProBudget/canEditCcmBudget`. Type `PeCurrentApprovalLevelApprover` +2 cờ **CẢ 2 app** (purchaseEvaluation.ts types DIFFER giữa 2 app → sửa cả 2; PeDetailTabs/PeWorkflowPanel byte-identical).
|
||||
- **Bảng lưới (em-main, anh phản hồi "chưa chia cột giống Excel"):** Block A flex→`<table border-collapse>` viền ô. `BudgetCell` xếp dọc (input full ô + Lưu dưới). `BudgetNoteRow`→`BudgetNoteCell` (td colSpan=3). **LESSON: flex+gap KHÔNG ra "bảng" — phải `<table>` viền ô mới giống spreadsheet.**
|
||||
- **cwd-misland:** Part-1 `cd fe-admin` npm build → MEMORY rơi `fe-user/.claude` stray → em-main harvest về + `.gitignore` guard `fe-*/.claude/` (S76).
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Role baseline
|
||||
|
||||
WRITE specialist FE 2 app (fe-admin + fe-user). Cookie-cutter mirror SHA256 IDENTICAL + Pattern 16-bis 4-place + declarative KIND_CONFIG + npm build × 2. Case 1+2 only. Tools: Read, Edit, Write, Bash, Skill, Grep, Glob + 5 RAG. Skill: `permission-matrix`.
|
||||
@ -42,12 +65,13 @@ Dynamic class purged. PALETTE array full literal `as const` cycle `index % lengt
|
||||
|
||||
## 📅 Recent activity (last 10 FIFO)
|
||||
|
||||
- **2026-06-17 (Văn phòng số "Bảng điều khiển" — mirror fe-user→fe-admin + 4-place wiring ×2 app):** Mechanical mirror+wire step (frontend-designer built in fe-user, em main chốt UX). (A) **index.css SYNC** prereq: fe-admin STALE pre-S66 → `cp fe-user/src/index.css fe-admin/` (no admin-only class to merge — utility sets identical, chỉ S66 gotcha-66 diffs: h1-h4 `#0b1220`/wt700 + .label-eyebrow brand-600 + accent palette comments) → SHA256 `e8631471…` identical. (B) **Mirror 4 file** `cp` user→admin SHA256 IDENTICAL: ui/PageHeader `6ff5303f…`(Văn-phòng-số richer header — eyebrow/icon/accent/breadcrumb, NOT @/components/PageHeader constrained one), ui/KpiCard `f8042ade…`(clickable filter chip a11y role=button), ui/WidgetCard `9221cbed…`(gotcha66 gradient header `text-white!`), pages/office/OfficeDashboardPage `c6d9dc08…`(composes 3 ui over EXISTING hooks, NO new API). All `@/` imports + Button/api/cn + types/{proposal,workflowApps,meeting} đã có ở fe-admin → 0 import fix. (C) **4-place ×2 app:** page(mirror) + App.tsx import+`<Route path="/office/dashboard">` (NEW `/office/` prefix convention — page file ở pages/office/, không xung đột; flat office routes giữ nguyên) + menuKeys `OffDashboard:'Off_Dashboard'` (mirror BE MenuKeys.OffDashboard, đặt sau `Off` trước `OffDanhBa` đúng thứ tự BE) + Layout staticMap `Off_Dashboard:'/office/dashboard'` (4th place gotcha#50, trước Off_DanhBa). Leaf admin auto via BE All[]. Build PASS ×2 (user 1934mod `index-BYj_ew5Q.js`, admin 1945mod `index-Cn1flmn6.js`, 0 TS err). CSS @import-order warning + >500KB chunk = pre-existing. NO ambiguity, full precedent (Pattern 16-bis 4-place + SHA256 mirror). Tag `[office-dashboard, mirror-step, 4-place, sha256-5file, office-route-prefix]`.
|
||||
- **2026-06-16 (S65 PE mục E "Link hồ sơ" FE ×2 app — em-main PROXY, PE-Workflow FE-stage died-empty #53):** Thêm mục "e. Link hồ sơ" hyperlink NAS dưới mục "d. Bản so sánh" + rename "Dự trù PRO"→"Ngân sách PRO". 4-file ×{user,admin} SHA256-mirror: PeDetailTabs.tsx (`HoSoLinkRow` :1353/1386 — useState(ev.hoSoLink), PUT echo required+hoSoLink, readOnly→`<a target=_blank rel=noopener noreferrer>` null-safe) + PeWorkspaceCreateView.tsx + types/purchaseEvaluation.ts (+hoSoLink). Ship Run #293 PASS, bundle both-rotate, GET phiếu thật `"hoSoLink":null` backward-compat ✓. SURPRISE: render landed on disk despite empty-return — work COMPLETE, chỉ MEMORY-update bị cắt (#53, lần này trong Workflow fan-out); em main self-gate bắt **badge "DỰ TRÙ PRO" sót rename** (agent chỉ đổi row label 1120/1126, sót badge 1078) → vá nốt ×2 app. Tag `[s65, pe-section-e-link-FE, hosolink-row, em-main-proxy-truncated-53, workflow-fanout]`.
|
||||
- **2026-06-16 (Department parentId — cây tổ chức, fe-admin ONLY):** Case 1 master-data enrich (NO 4-place, NO menu/route/Layout — chỉ Place-1 page+type). BE đã sẵn (local `0f44d97`): `DepartmentDto.parentId:Guid?` + POST/PUT nhận `parentId?` + cycle-guard 409 ConflictException. fe-admin ONLY (intentional — KHÔNG fe-user, KHÔNG SHA256). 2 file: (1) types/master.ts `Department` +`parentId:string|null` (sau name, trước managerUserId) — DepartmentInput auto-inherit via Omit · (2) DepartmentsPage.tsx: import Select + FormState/emptyForm +`parentId:string` ('' khi rỗng) + load-all query `['departments-all']` pageSize:200 (reuse pattern proven UsersPage/Workflows/AttendanceReport) + `deptNameById` Map từ allDepts cho cột "Thuộc" + mutate payload `parentId:d.parentId||null` + openEdit `parentId:d.parentId??''` + Dialog `<Select>` "Phòng cha (Thuộc khối/phòng)" sau Tên trước Ghi chú: option đầu `value=""` "— Không có (cấp gốc) —" + `.filter(d=>d.id!==form.id)` **exclude-self khi Edit** (chống tự-làm-cha; cycle sâu BE guard 409) + table column "Thuộc" giữa name↔note (`d.parentId?deptNameById.get()??'—':'—'`). Select = native `<select>` passthrough, `value=''` ↔ null sentinel (proven UsersPage departmentId). Build PASS (1941 mod, 0 TS err — tsc -b clean trước vite). NO ambiguity, full precedent (S55 enrich + UsersPage Select).
|
||||
- **2026-06-11 (S57bis PE WorkItem FE ×2 app — PARTIAL, on-behalf em main ghi hộ, H2-proposed):** Task = PeWorkspaceCreateView select "c. Hạng mục công việc *" sau Dự án + PeHeaderForm (select + load existing + PUT/POST workItemId) + PeDetailTabs (subtitle "Dự án – Hạng mục" + FormRow + inline-edit khóa) + types +3 field. Return-truncated #53 GIỮA mirror fe-admin → em main solo mirror 7 edits PeHeaderForm + 3 edits PeDetailTabs ×2 app; PeHeaderForm SHA256 IDENTICAL. LEARNED: mirror đo bằng SHA256 (không diff mắt); option label `[Category] Code — Name` + canSubmit require; route reuse `/catalogs/work-items` (KHÔNG endpoint mới). SURPRISE: điểm gãy lặp tại mirror-2-app-trong-1-spawn → cân nhắc per-app stage khi slice lớn. Tag `[s57bis, truncated-53, sha256-mirror, on-behalf]`.
|
||||
- **2026-06-09 (S55 HMW P2 — Project +4 optional master fields):** Case 1 master-data enrich (NO 4-place, NO menu/route/Layout — chỉ Place-1 page+type). BE adds 4 nullable Project fields parallel (implementer-backend). 2 file × 2 app: (1) types/master.ts `Project` +`year:number|null`+`investor/location/package:string|null` (sau `note`) +ProjectInput auto-inherit via Omit · (2) ProjectsPage.tsx (single-Dialog CRUD, NO separate pages): FormState +4 `string` (form dùng string, convert on submit) + emptyForm +4 '' + mutate payload `year:d.year?Number():null, investor/location/package:d.x||null` + openEdit `year:p.year?.toString()??'', x:p.x??''` + Dialog 4 Input sau "Ngày kết thúc" trước "Ghi chú" (Năm type=number, Chủ đầu tư, Địa điểm col-span-2, Gói thầu col-span-2) + table column "Chủ đầu tư" (`p.investor??'—'`) giữa name↔startDate. **`package` = valid TS object KEY** (reserved chỉ khi binding-identifier) → `form.package`/`{...form,package:x}` build sạch, KHÔNG cần rename. cp admin→user SHA256 IDENTICAL: master.ts `93ac1b0f…`, ProjectsPage `b002061…`. Build PASS ×2 (admin 1945mod, user 1934mod, 0 TS err — tsc -b clean trước vite). Reuse S42 enrich-pattern (string-form + convert-on-submit). NO ambiguity, full precedent.
|
||||
- **2026-06-08 (S54 ItTicket reassign → CONVERGE 2 app, REVERSE S53 divergence) [harvested by em main — agent MEMORY write mis-landed, B2/B3]:** S53 đã tách fe-admin-only (admin reassign). S54 cho tổ IT tự reassign → cả 2 app cần nút. **Pattern mới: BE capability-flag gate thay vì FE đoán role.** Dropdown đổi `GET /users` → `GET /it-tickets/assignable-staff` trả `{canReassign:bool, staff:[{id,fullName}]}` (`[Authorize]` any-auth, KHÔNG 403 → chống gotcha #44). `canReassign = staffQ.data?.canReassign ?? false` (fetch on mount, KHÔNG `enabled`) → nút Pencil bọc `{canReassign && …}`. types/workflowApps.ts +`AssignableStaff`+`AssignableStaffResult` (mirror cả 2). fe-admin rewrite (bỏ UserOption/Paged<T>) + fe-user full-add → **SHA256 IDENTICAL `4bcaf2f…`** (viết admin canonical → `cp` → verify; cùng `@/...` import + shadcn Dialog/Select/Button identical 2 app). Build PASS ×2 (0 TS err). **Gotcha (pre-existing, NOT từ change này):** types/workflowApps.ts 2 app NOT SHA-identical — fe-admin có `AttendanceReportDto` (P11-E) mà fe-user thiếu; 2 type S54 mirror đúng cả 2. Lesson: BE-computed capability flag = single-source → 2 app converge lại sau intentional divergence.
|
||||
- **2026-06-08 (S52 Task C+D-FE — ItTicket admin reassign + AttendanceReport menu, fe-admin ONLY):** Both intentional mirror-break (admin-only, NO fe-user touch, NO SHA256). **Task D-FE menu wiring:** Page+App.tsx route `/attendance/report` ALREADY exist (S52 prior). Only 2 of 4-place needed: (1) menuKeys.ts +OffAttendanceReport='Off_AttendanceReport' (mirror BE string exact, after OffChamCong) · (2) Layout.tsx staticMap +Off_AttendanceReport:'/attendance/report' (4th place gotcha #50). `types/menu.ts` = MenuNode tree type, key:string NOT typed-union → NO mirror there (resolvePath(key:string)). Leaf perm-gated via BE All[]→admin auto. **Task C reassign:** ItTicketsPage.tsx top-comment updated DIVERGES fe-user. Per-card Pencil button (cạnh 👤 assignee) → Dialog (size sm) + Select user. Users source = **GET /users {params:{page:1,pageSize:200}}→{items:UserOption{id,fullName,email}}** (reuse, proven PeWorkflows/Workflows/MeetingCalendar — `enabled:target!==null` lazy fetch). useMutation api.put(`/it-tickets/${id}/assign`,{assignedToUserId})→204 (NO json read)→invalidate['it-tickets']+toast.success+close. preselect t.assignedToUserId. UI deps: Dialog(open/onClose/title/children/footer?/size) + Select(native passthrough) + Button(variant=outline) + toast(sonner) + getErrorMessage(@/lib/apiError). Build PASS (0 err, 1945 mod). git: only 3 fe-admin file, fe-user untouched.
|
||||
- **2026-06-08 (P11-E Wave 1 — AttendanceReportPage fe-admin ONLY):** Report endpoint `[Authorize(Roles=Admin)]` → KHÔNG fe-user page → NO SHA256 mirror (intentional). 4 FE file: (1) types/workflowApps.ts +AttendanceReportRowDto{userId,fullName,departmentName?,daysPresent,totalWorkHours,otRaw,otWeekday,otWeekend,otHoliday,otWeighted}+AttendanceReportDto{year,month,rows,grandTotalWorkHours,grandTotalOtWeighted} (decimal→number) · (2) pages/office/AttendanceReportPage.tsx NEW: PageHeader+filter(Year Input number / Month Select 1-12 / Phòng ban Select fetch /departments) + TanStack key ['attendance-report',year,month,deptId] GET /attendances/report + Table 9 col STT/Họ tên/Phòng ban/Ngày công/Tổng giờ/OT thường/OT cuối tuần/OT lễ/OT quy đổi + tfoot Tổng(colSpan trick) + fmtNum vi-VN · (3) App.tsx import+route /attendance/report · (4) MyAttendancePage.tsx +button "Báo cáo" admin-only (user?.roles.includes('Admin')) navigate → DIVERGED fe-user (header comment cảnh báo). **Download Excel: `api.get(url,{params,responseType:'blob'})` (api instance inject JWT interceptor + refresh-retry — CHUẨN HƠN raw fetch spec gợi ý; proven ReportsPage/FormsPage/PeDetailTabs) → blob → createObjectURL → anchor.download.click → revoke. Filename content-disposition regex, fallback BaoCao-ChamCong-{Y}-{MM}.xlsx.** Build PASS (0 err, 1945 mod). KHÔNG menu key (button-reachable MVP).
|
||||
- **2026-06-08 (S51 P11-C — vehicles+drivers kind → HrmConfigsPage):** Declarative KIND_CONFIG +2 entry (10th proof). 4-place adapt (`:kind`-driven page → NO App.tsx route): types/hrm-config.ts (union +'vehicles'+'drivers' + VehicleDto/DriverDto + Create/Update inputs) · HrmConfigsPage.tsx (KIND_CONFIG +2, KINDS array +2, renderCells +2 branch before ot-policies fallback, import Car+IdCard) · menuKeys.ts (+Hrm_Config_Vehicles/Drivers — BE string exact) · Layout.tsx staticMap +2 BOTH app. Field keys: vehicles{code,name,licensePlate,seatCount,description} drivers{code,name,phoneNumber,licenseNumber,licenseClass,description}. cp admin→user 3 file SHA256 identical (page a3afd724, type 2c0775b3, menuKeys d650c086). Layout mirror tay (structural diff OK, 2 entry verified both). Build PASS ×2 (admin 1944mod, user 1934mod, 0 TS err). lucide IdCard EXISTS (no UserRound fallback). AMBIGUITY: BE catalog vehicles/drivers chưa tồn tại on-disk (Wave 1 parallel — implementer-backend đang/sẽ làm) → FE scaffold theo contract spec cấp; runtime cần BE `/hrm-configs/vehicles`+`/drivers` endpoint + Hrm_Config_Vehicles/Drivers const trong BE MenuKeys.cs + seed.
|
||||
---
|
||||
|
||||
## ⚠️ Anti-patterns (DO NOT)
|
||||
|
||||
@ -30,6 +30,9 @@ Read-only EXTERNAL research specialist. WebFetch/WebSearch official docs + NuGet
|
||||
|
||||
- **2026-05-29 (S39 agent split setup):** NEW agent created từ split investigator. Seeded external-research half. Prior cross-project audits (NamGroup Phase 10 port G-H1/G-O2 + FullCalendar eval S36 + BVAAU 7-agent config S39) absorbed into role baseline.
|
||||
- **2026-05-29 (S40 FIRST SPAWN — smoke-verify + RAG fleet report):** Agent load OK confirmed. `list_projects` → 7 project, total **39,798 chunks**. Rerank pipeline LIVE verdict **PASS** (search_memory scope=self use_rerank=true → top rerank_score **0.8789**, 3 results all carry rerank_score). Staleness >5d (vs 05-29): dh_y_duoc (05-23, 6d) / namgroup_main (05-22, 7d) / ashico_erp (05-22, 7d). solution_erp 05-28 fresh-ish but missing S37-S39 content. `shared_global` = 0 chunks (chưa promote pattern nào). MINOR drift: namgroup_main actual 11306 (brief said 11305). vipix_ai_infra (1652) = AI_INFRA hub root `D:\...\AI_INFRA`. No re-ingest performed (report-only).
|
||||
- **2026-06-16 (Góc 1 — FOUNDATIONAL: HTTPS `<a href=file://>` click có mở Explorer/local/UNC?):** Dứt khoát = **KHÔNG** trên browser mặc định. Chrome/Edge **76+** chặn navigate file:// từ trang non-file:// → click = "nothing visibly happens", console `"Not allowed to load local resource: file://host/page"` (textslashplain 2019 + MS Learn xác nhận verbatim). Firefox cũng chặn cross-origin file://. **NGOẠI LỆ CHÍNH THỨC duy nhất** = Edge GPO **`IntranetFileLinksEnabled`** — confirmed **Windows ≥95** (MS Learn dedicated page DEFINITIVE; "77+" trên trang chỉ là article-applies boilerplate, ĐỪNG nhầm). Behavior: intranet-zone HTTPS → file:// link → Explorer parent-dir + select file (dir-link mở folder no-select). Reg `HKLM\SOFTWARE\Policies\Microsoft\Edge\IntranetFileLinksEnabled=1` REG_DWORD, ADMX `Content settings`. `https://localhost/` block exception; loopback 127.x/[::1]=internet-zone. **URLAllowlist KHÔNG giải**: MS Learn quote verbatim "doesn't work as expected with file://* wildcards" + chỉ exception URLBlocklist, KHÔNG lift nav-block. **Chrome KHÔNG có equivalent** ("No option to disable this navigation blocking is available in Chrome"). Ext "Enable Local File Links"/Local Explorer = workaround nhưng cần install per-machine + "will not help if page uses JS to navigate" (chỉ bắt click listener). one-click+zero-install thuần-web = **FALSE**; chỉ path = Edge GPO 100% fleet (Góc 3). Sources: textslashplain.com/2019/10/09/navigating-to-file-urls; learn.microsoft.com/deployedge intranetfilelinksenabled + urlallowlist.
|
||||
- **2026-06-16 (Góc 2 — ZERO-install mở Explorer tới O:\ từ web, complements Góc 3 below):** Ruled OUT the no-server-policy tricks: (a) **File System Access `showDirectoryPicker` KHÔNG mở path cho-sẵn** — `startIn` chỉ nhận well-known dir (downloads/desktop/...) HOẶC FileSystemHandle đã-pick, KHÔNG raw `O:\`; BẮT BUỘC user gesture + tự navigate (MDN). (b) **.url download = 2-BƯỚC** (download→double-click), KHÔNG one-click; nhưng `.url [InternetShortcut] URL=file:///O:/...` dispatch qua shell nên folder/UNC CÓ mở Explorer — `.lnk` thì dính MoTW/SmartScreen (chính vector LNK-stomping CVE-2024-38217, IT flag). (c) **ms-explorer/shell: KHÔNG web-callable** = custom-protocol anh đã reject. Kết: KHÔNG có true one-click+zero-install thuần web; chỉ-server-side path duy nhất = Edge GPO `IntranetFileLinksEnabled` (xem Góc 3 entry kế). Sources: MDN showDirectoryPicker; learn.microsoft win32 internet-shortcuts; textslashplain navigating-to-file-urls.
|
||||
- **2026-06-16 (eoffice "open mapped O: drive folder from web app" — Góc 3 setup-light research):** WINNER = native Edge policy **`IntranetFileLinksEnabled`** (Edge ≥95, REG_DWORD `HKLM\SOFTWARE\Policies\Microsoft\Edge\IntranetFileLinksEnabled=1`, ADMX `Admin Templates/Microsoft Edge/Content settings`). Behavior: intranet-zone HTTPS page → file:// link → opens Explorer to parent dir + selects file (dir-link opens folder). **ZERO per-machine install** (1 GPO setting). HARD reqs: (1) app served HTTPS, (2) eoffice host in **IE Intranet Zone** (push via SiteToZoneAssignmentList GPO), (3) Edge only — **Chrome has NO equivalent** (confirmed MS+textslashplain: Chrome 76+ blocks file:// nav, only fix=Local Explorer ext). Caveat: first-click shows external-protocol prompt unless ExternalProtocolDialogShowAlwaysOpenCheckbox lets user tick "always". URLAllowlist does NOT solve nav-block + "doesn't work with file://* wildcards" (MS Learn). Ext route = ExtensionInstallForcelist (force-install, but 3rd-party Local Explorer = paid + needs native helper.exe install = NOT zero-install). .reg custom-protocol = anh đã reject. Verdict: oneClick+zeroInstall TRUE **only if 100% Edge fleet**; mixed-Chrome → must add extension (loses zero-install). Sources: learn.microsoft.com/deployedge intranetfilelinksenabled + urlallowlist + extensioninstallforcelist; chromeenterprise.google/policies/url-patterns; textslashplain.com navigating-to-file-urls.
|
||||
- **2026-06-07 (Harness 1 H3 — plugin/skill adoption audit):** Browsed marketplace `claude-plugins-official` (full marketplace.json ~200+ plugin; local folder 35). Enabled user-global (`~/.claude/settings.json`) = **15 plugin**. **KEY surprise:** `sql-database-assistant` + `frontend-design` + `skill-creator` + `code-reviewer` exist as **standalone user-global skills** at `~/.claude/skills/` (auto-available every project — NO plugin enable needed). `~/.claude/skills/` has 23 standalone skills. Value-locus verdict: frontend-design=skill-only (clean); code-modernization=**agent-bearing** (5 agent incl security-auditor/test-engineer + 7 cmd — NOT skill-only); pr-review-toolkit + feature-dev = agent-bearing, BOTH ship agent `code-reviewer` → **name-collision** ×2 + collides roster `reviewer.md`. csharp-lsp = **Windows no-op** (`csharp-ls` NOT on PATH, needs `dotnet tool install`). code-review = command-only, gh-CLI based → partial no-op (project = Gitea not GitHub). session-report = node `.mjs` (Win-OK, Node 20). Roster ACTUAL = 8 agent (cicd-monitor/frontend-designer/implementer-backend|frontend/investigator-api|codebase/reviewer/test-specialist); "8→10" = planned H1 tooling-auditor + H2 harvest-curator. Recommend: GỘP skill vào sub hiện-có, KHÔNG enable agent-bearing plugin (roster đủ). Report-only.
|
||||
|
||||
---
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
> **Persistent diary cross-session.** Auto-injected first ~200 lines at spawn (L1 HOT).
|
||||
> Update BEFORE every stop. Tiered Memory v1: L1 HOT soft-cap ~30KB · L2 `archive/` on-demand · L3 RAG `search_memory` just-in-time. Keep entry ≤ 1.5K chars (gotcha #53).
|
||||
> Full verbatim history pre-S40 → git `d2f52ba` + `archive/2026-05-q1..q4.md`.
|
||||
> Full verbatim history pre-S40 → git `d2f52ba` + `archive/2026-05-q1..q4.md` + `archive/2026-06.md`. 🗺️ **Lookup map (Harness-9 S70): `archive/_INDEX.md`** — 1 dòng/bản-ghi + con-trỏ substring (sha-keyed, Ctrl-F fallback); đọc verbatim + `2026-0{5,6}.gist.md` (nén 4-field) theo nhu cầu.
|
||||
> **Renamed S39:** investigator → investigator-codebase (internal half; external → investigator-api).
|
||||
|
||||
---
|
||||
@ -70,41 +70,38 @@ Bearer từ `POST api.solutions.com.vn/api/auth/login` → status matrix expecte
|
||||
|
||||
## 📅 Recent activity (FIFO — older → archive/git)
|
||||
|
||||
- **2026-06-12 (S60 recon #2 — V2 engine map cho drafter-in-chain bypass):** ⭐ 3 entity V2 CÙNG 1 file `Domain/ApprovalWorkflowsV2/ApprovalWorkflow.cs` — Step.Order :65 1-based, Level.Order :78 1-based PER-STEP (reset mỗi step), Level.ApproverUserId :80 Guid đơn; **OR-of-N = N Level rows cùng Order** (service GroupBy :475 — entity comment :73 "KHÔNG OR-of-many" STALE Mig22-era). `PurchaseEvaluationWorkflowService.cs`: submit :131-158 init pointer StepIdx=0 :151 + LevelOrder=1-if-V2 :153 (TraLai resubmit CÙNG path = restart từ đầu); ApproveV2Async :446-634 — guard actor∈HashSet ApproverUserId :488, Approval row :501, LevelOpinion UPSERT :522-546 (SignedByUserId KHÔNG nullable — Guid.Empty system :536; placeholder "(duyệt — không ý kiến)" :526), advance level++ :605 / step++ :628 / terminal DaDuyet :617-624 (pointers null + LogTransition). **Notify DRAFTER-only :748 — KHÔNG notify approver Ở ĐÂU CẢ** (submit silent vì drafter==actor). V1/V2 fork approve :167; submit chung (chỉ :153 conditional). skipToFinal F2 :561-602 = PRECEDENT advance-pointer-KHÔNG-ghi-opinion cho slot bị skip. FE Section 5 `fe-admin/src/components/pe/PeDetailTabs.tsx:510` render approvalFlow×levelOpinions match stepOrder :528; counter :531 đếm TỪ opinions (level bị skip hiện "chưa ký"); badge :592 signedByUserId!==approverUserId → "⚠ Admin … duyệt thay" :602 (text misleading nếu bypass ký hộ slot người khác). Chỗ chèn bypass: submit branch sau :153. Tag `[s60-recon2, v2-engine-map, drafter-bypass]`.
|
||||
- **2026-06-19 (S76 P2+P3 — budget-edit-role BADGE insert-point map, designer+fe-user-flow, on-disk):** ⭐ **Display-only "✎ NS PRO/CCM" badge per approver — BE change = SMALL both DTOs.** **(A) Designer fe-admin `ApprovalWorkflowsV2Page.tsx`:** read-only render `DefinitionCard:446-454` (level group → approver `{approverUserName}` + `({approverEmail})`); DTO `LevelDto:37-54` (approverUserId/userName/email + 7 Allow* flag, **NO role/dept field**). Feed = `GetAwAdminOverview` (`/approval-workflows-v2`). **Insert badge → `:447-452`** cạnh `approverUserName`. **(B) fe-user `PeDetailTabs.tsx`:** approvalFlow render `LevelOpinionsSectionV2:588` (signed-only) — но live flow tree = `currentApproval.approvers` :131 + Panel3 separate. `PeApprovalFlow` DTO `purchaseEvaluation.ts` + BE `PurchaseEvaluationApprovalLevelApproverDto` (`PurchaseEvaluationDtos.cs:129-132` = UserId/FullName/Email, **NO role**). **(C) Role-resolve for LIST userId:** codebase uses `userManager.GetRolesAsync(u)` (per-user, N+1 risk) OR `GetUsersInRoleAsync(role)` (reverse, `PeUrgentFeatures.cs:74`). `IApplicationDbContext` exposes `DbSet<Role> Roles` :29 but **NO UserRoles join-table DbSet** → efficient batch = either (a) `userManager.GetUsersInRoleAsync(Procurement/CostControl)`→2 HashSet<Guid>, mark approver if id∈set (NO N+1, 2 queries total); or (b) add `DbSet<IdentityUserRole<Guid>>` to interface for join. **BE build site `PurchaseEvaluationFeatures.cs:964-972`** already batches `approverInfos` via `userManager.Users.Where(Contains(allApproverIds))` — extend SELECT or post-join 2 role-sets here; handler has both `db`+`userManager` :750-751. **(D) Change size = SMALL:** +2 bool field (canEditProBudget/canEditCcmBudget) per approver DTO + 2 GetUsersInRoleAsync calls. Designer side: `GetAwAdminOverview` query needs same 2-set lookup (admin-only, cheap). Gate semantics ALREADY proven `:800-801` (canEditPro=Admin||Procurement, canEditCcm=Admin||CostControl). **(E) REC:** minimal = compute 2 HashSet once (proFans/ccmFans via GetUsersInRoleAsync), pass into approver-DTO map both sites; badge = pure display `id∈proFans→"✎ NS PRO"` `id∈ccmFans→"✎ NS CCM"`. RISK low (display-only, no authz touch) — only watch: a user can hold BOTH roles → show both badges; Admin holds neither role explicitly unless seeded → may need OR Admin note. Tag `[s76, budget-role-badge, designer+pe-flow, getusersinrole-batch-no-n1, approver-dto-add-2bool, display-only]`.
|
||||
|
||||
- **2026-06-12 (S60 PE Section-3 submit-guard recon, on-disk):** ⭐ **Submit path:** `POST /pe/{id}/transitions` (Controller:68) → `TransitionPurchaseEvaluationCommand` (Features:434, validator :446 shape-only) → handler :462 (auth+NotFound, NO data check) → `PurchaseEvaluationWorkflowService.TransitionAsync:38`; submit branch :131-158 (Nháp/TraLai→ChoDuyet) guard ROLE-only (Drafter/DeptMgr/Admin :138-144), KHÔNG validate supplier/budget/quote/attachment. WorkItem guard S57bis create-only (:43/:66). **Section-3 map (ChonNccSection PeDetailTabs:1135):** (a) winner = header `SelectedSupplierId` (entity :27 loose-Guid; set `POST /{id}/select-winner` → DetailFeatures:390, NO phase guard); (b) budget dual `BudgetId` || `BudgetManualAmount` (ManualName always-null :847, manual-detect :825); (c) giá chào thầu = DERIVED sum quotes winner row :1139-48 (0=chưa nhập → guard cần >0); (d) bản so sánh = attachments `supplierRowId===null` ONLY :1150-53, KHÔNG check Purpose=4 → BE guard mirror predicate null-row tránh FE mismatch. **Label:** heading DUY NHẤT :228 (CAPS do h3 uppercase :317); SHA256 identical 2 app. **Nút gửi:** PeDetailTabs :291-304 + `canSubmitForApproval`:146 + `submitDisabledReason`:153 = chỗ chèn FE pre-check; S59 hide-self PeWorkflowPanel:271. **Test mirror:** `Services/PurchaseEvaluationWorkflowServiceGuardTests.cs` (BuildPeInChoDuyet:46) + `Application/PeWorkItemGuardTests.cs` (:43 BuildCreateHandler). Tag `[s60-recon, pe-submit-guard, section3-map]`.
|
||||
- **2026-06-19 (PE Block-A budget editable-gate audit — submission-count lock NEXISTS, on-disk):** ⭐ **Gate = PURE ROLE, KHÔNG phase, KHÔNG số-lần-trình.** BE `PurchaseEvaluationFeatures.cs:800-801` `canEditPro=isAdmin||Procurement` · `canEditCcm=isAdmin||CostControl` (DTO arg :856). Handler `PeWorkItemBudgetFeatures.cs`: PRO `:86-91` CCM `:152-157` fail-closed ForbiddenException role-only TRƯỚC side-effect; comment `:18-20` ghi RÕ "KHÔNG ràng Phase (bảng NS = tài-liệu-sống chỉnh bất-kỳ-lúc-nào như Excel)". Validator chỉ `>=0` (Initial :136, Adjustment cho-ÂM :138), absolute-set null=clear. **FE `PeDetailTabs.tsx:1060 PeBudgetSummaryTable`:** ô "Ban hành lần đầu" :1173 + ô "hiệu chỉnh V0" :1188 dùng **CÙNG biến `bs.canEditCcm`** — ZERO phân-biệt 2 ô, ZERO lock-after-first. `drafterEditable:1066`=`!readOnly&&isEditablePhase` chỉ áp row3/row8 (drafter NS-kỳ-này), KHÔNG áp Block-A. **(b) submission-count lock = KHÔNG TỒN TẠI:** grep `submitCount|lanTrinh|firstSubmit|lockInitial|hasSubmitted|soLanTrinh` toàn `src/Backend`=0 + FE=0. Entity `PeWorkItemBudget.cs` 6 field plain, KHÔNG cờ `IsInitialLocked`/`SubmitCount`; record per-cặp(Project×WorkItem) share mọi phiếu KHÔNG track lần-trình. **Kết luận: yêu-cầu chị Trà/anh Kiệt (khóa Initial sau lần-trình-đầu, mở Adjustment) = FEATURE MỚI — cần field track first-submit-done + TÁCH gate 2 ô (Initial vs Adjustment), HIỆN cùng `canEditCcm` không tách được.** Tag `[pe-block-a-gate, role-only-no-phase, submission-count-lock-NEXISTS, initial-vs-adjustment-same-gate, fdc-feature-new]`.
|
||||
|
||||
- **2026-06-11 (S59 recon — prod test-data wipe + PE tree Hạng mục, prod+on-disk):** ⭐ **Prod:** PE=10 active (1 Nháp + 1 DaDuyet(7) + 8 ChoDuyet(10), MaPhieu A/031-040, ALL WorkItemId NULL) + child 20/10/20/28/138/18/18 (Sup/Det/Quote/Appr/Chg/Att/LvlOp); Contracts=7 ALL `[DEMO]` 05-08 pin V1 (AwId NULL) + Appr15 + details15; Budgets/WorkflowApps/Proposals/Attendances/Meetings ALL 0; Notifications 64. Seq: PE/2026/A=40 B=1; CT=7 demo prefix LastSeq=1. **FK:** PE child CASCADE trừ `Quotes→PE NO_ACTION` (multi-path; Plan R S23 proved single `DELETE FROM PurchaseEvaluations` OK — NO_ACTION check end-of-statement sau cascade Details→Quotes). Contract child ALL CASCADE. PE.ApprovalWorkflowId Restrict → wipe PE trước khi xóa AW QT-DN-V2-001 v1 (inactive, còn 1 PE pin). AW V2=8: 7 ghim KEEP. **Uploads orphan:** purchase-evaluations/ 19 folder vs 10 PE → ~10 orphan từ S23 (file không xóa); contracts/ 1. **Demo gate OK:** SeedDemoContracts/PE TRONG `DemoSeed:Disabled` (DbInitializer:80,131-132) → wipe không resurrect. **Surprise:** Users 55 total / 21 active — 20 user THẬT batch 2026-06-11 06:01 (S58 seed fix ăn; thanh.lethanh NOW EXISTS — stale S57bis mem; chuong.phan typo-domain VẪN active song song twin). **FE tree:** `pe/PurchaseEvaluationsListPage.tsx:138-179` Project>Year(createdAt :150)>Supplier; SHA256 identical 2 app; PeListItem ĐÃ có workItemId/Name (types :116-118, BE Features :514/570/644) → đổi tree FE-only. Tag `[s59-recon, prod-wipe, pe-tree-workitem]`.
|
||||
- **2026-06-18 (PE price-model recon FDC "Giá chào thầu" PRO-Min/Max + CCM-proposed, on-disk):** ⭐ **"Giá chào thầu" mục c = DERIVED, KHÔNG stored column** = `WinnerQuoteTotal` = SUM(Quote.ThanhTien WHERE supplierRows==SelectedSupplierId). Computed 3 nơi đồng-predicate: submit-guard `PurchaseEvaluationWorkflowService.cs:188-192` · detail-GET `PurchaseEvaluationFeatures.cs:818-826`(→`CurrentProposalTotal`) · CEO-threshold `:833`. DTO `WinnerQuoteTotal` `PurchaseEvaluationDtos.cs:244`. **ALL money fields:** Quote(NCC) `BgVat/ChuaVat/ThanhTien` decimal non-null `PurchaseEvaluationQuote.cs:12-14` · PE-header `BudgetPeriodAmount`(row3 drafter)/`ExpectedRemainingAmount`(row8) decimal? `PurchaseEvaluation.cs:40-41` · PeWorkItemBudget(per cặp Project×WorkItem) PRO `ProEstimateAmount:27` + CCM `InitialAmount`/`AdjustmentAmount`(ÂM-OK) `:29-30` decimal? · Detail dự-toán `KhoiLuong/DonGia/ThanhTienNganSach` `PurchaseEvaluationDetail.cs:15-18`. **PRO-min/max + CCM-proposed = KHÔNG tồn-tại** (grep Min|Max|Proposed|Suggest|BidPrice|GiaChaoThau PE-entities=0) → field MỚI. **Role-gate mirror-được (`PeWorkItemBudgetFeatures.cs`):** 2 cmd tách `UpdatePeBudgetProCommand:61`+`UpdatePeBudgetCcmCommand:126`; handler fail-closed `ForbiddenException` TRƯỚC side-effect — PRO `:86-91`(`Admin||Procurement`) CCM `:150-155`(`Admin||CostControl`); capability-flag BE-computed `canEditPro/canEditCcm` `PurchaseEvaluationFeatures.cs:783-784`→DTO `PeBudgetSummaryDto:290-291`; auto-create race-safe `PeWorkItemBudgetEnsurer.EnsureTrackedAsync:34`; KHÔNG ràng Phase. NO AutoMapper (DTO project tay). **FE (fe-user `src/`; fe-admin PeDetailTabs.tsx = SHA-identical `diff -q`):** mục-c `components/pe/PeDetailTabs.tsx:1406-1417`(helper `computeGiaChaoThau` def:71 call:1393) · budget-table `PeBudgetSummaryTable:1062-`(rows:1110-1128, host `ChonNccSection:1383`) · giá-gói+CEO-threshold `:311-313` · create `PeWorkspaceCreateView.tsx` · header `PeHeaderForm.tsx`. FE type `types/purchaseEvaluation.ts` `PeBudgetSummary:292-307`+`winnerQuoteTotal:445`. ⚠️ fe-admin types DIFFER (sync cả 2). **Surprise:** PRO-Min/Max-chốt + CCM-proposed = semantic MỚI (giá-người-duyệt ≠ giá-NCC-báo); gắn PeWorkItemBudget(per-cặp role-gate-sẵn) vs column-PE(per-phiếu) = em-main quyết. Mig 53 CeoApprovalThreshold+cờ-gấp đã có khung CEO-duyệt-theo-ngưỡng. Tag `[pe-price-model, gia-chao-thau-derived, pro-minmax-ccm-proposed-NEW, role-gate-mirror, fdc]`.
|
||||
|
||||
- **2026-06-11 (S57bis lock no-op — prod user census, on-disk+prod):** ⭐ `LockDemoSampleUsersAsync` (DbInitializer.cs:1552, chạy CUỐI :98) hardcode 14 named-person email (bod.huynh/pm.nguyen/fin.do/qs.hoang…) = population CHỈ CÓ TRÊN DEV. **Prod 34 user ALL-active:** 20 UAT-matrix placeholder hand-created batch 2026-05-13 15:04-05, scheme `{act,equ,fin,hra,pm,qs}.{nv,pp,tp}@` + `bod.{1,2}@` (FullName tự khai "ACT NV - Drafter+Accounting", "[Bypass]"/"[SkipFinal]" = test Mig 29-31 flags) + 9 real staff hand-created 05-04→05-12 + `binh.lethanh@` (người thật Lê Thanh Bình — seed dùng `thanh.lethanh@` KHÔNG tồn tại prod) + `chuong.phan@solution.com.vn` TYPO-domain dup (twin đúng tạo 05-12) + admin/catalog.manager/nv.test. **ROOT CAUSE seed-user never-on-prod:** prod `Identity:Password:RequiredLength=12` (appsettings.Production.json) vs `DemoUserPassword="User@123456"`=11 chars → CreateAsync silent-fail MỌI startup từ prod-init 04-21 (code comment :1675-79 đã biết); Dev fallback 8 (DependencyInjection.cs:67 `?? 8`, Development.json no Identity section) → Dev đủ 33 user named-person. `bod.1@` NEVER in git pickaxe = tạo tay qua admin UI, không phải seed. Surprise: _Dev hiện CŨNG chưa khóa (Locked=0; LockoutEnd=MaxValue sẽ persist qua reconcile re-activate :1714 nếu từng chạy) → lock chưa từng execute against _Dev runtime. Fix cần 20 email prod-thật; GIỮ binh.lethanh + 9 real + admin/catalog.manager; `nv.test@` = creds smoke-verify (khóa = vỡ cicd smoke). Tag `[s58, s57bis-lock-noop-recon, prod-user-census, pwd-policy-env-divergence]`.
|
||||
- **2026-06-18 (S71 PART-C audit — run-trace vs checklist-v2 FLAT + detector-refine, on-disk):** ⭐ **2 GAP THẬT (trung-thực, không inflate):** (1) **C1/C2/C8 = SUBFOLDER, canonical-v2 = FLAT → migration NEEDED, chưa làm.** `find runs/` cho thấy MỖI run-folder có `sub-md/`+`harvest/` SUBDIR (5 run: h10-{invest,implement,review}+h910-{finalize,curate}) — đúng cấu-trúc CŨ broadcast-delta phát-bỏ. ZERO flat-awareness: grep `phẳng|flat|cùng cấp` trong `.claude/workflows/`+`.claude/commands/`=0 hit. SE-adoption-commit `8c47bd0`(06-18) TRƯỚC broadcast-flat cùng-ngày → SE chưa biết. README/hmw.js/session-end đều mô-tả subfolder. C8 dual-form-acceptance close-gate cũng chưa. (2) **REFINE(b) detector = MISSING HOÀN-TOÀN.** `find .claude -name *.js/*.ps1`=CHỈ `hmw.js`(=engine ≠ detector). `.claude/hooks`+`.claude/scripts` KHÔNG tồn-tại. Repo-wide grep `bypass|scan.*runs` script=0. SE KHÔNG có bộ-dò chống-lách-engine → 3-function (whitelist/path-variants/launch-key-anchor)+relation-acceptance = n-a. **MET (đừng nhạ oan):** C3 committed THẬT — `git check-ignore runs`=exit1(NOT-ignored)+`git ls-files runs`=22 file (cả hai nấc). C4 per-turn real (`invest-synthesis.md` 43-dòng). C5 3-layer wired: L1 README:51(convention em-main) · L2 `session-start.md:71` orphan-scan `runs/*/` closed=⏳+harvest-rỗng · L3 `session-end.md:51` close-gate idempotent 5-trục. C6 ledger 2-beat (`_ledger.md:7`, 5 run đều CLOSE-beat+wf_). C7 caveat present (README §69-73 no-overclaim/fragile/G-015 TRACKED≠enforced). ⚠️ sub-md/ chỉ `.gitkeep` (read-only sub→em-main scribe, design KHÔNG phải miss). Tag `[s71, part-c-audit, subfolder-not-flat, detector-MISSING, c3-committed-real]`.
|
||||
|
||||
- **2026-06-11 (S57bis PE recon — 4 đầu việc sếp, on-disk):** ⭐ PE entity NO Year, NO WorkItem link (`PurchaseEvaluation.cs:15` ProjectId req; Detail free-text `PurchaseEvaluationDetail.cs:10-13`). Create cmd `PurchaseEvaluationFeatures.cs:19-30`; MaPhieu gen-AT-CREATE `:114-116` format `PE/{YYYY}/{A|B}/{Seq:D3}` (`PurchaseEvaluationCodeGenerator.cs:23`). Main create UI = `PeWorkspaceCreateView.tsx` (:151 workflow-select isUserSelectable ĐẦU TIÊN → tenGoiThau → projectId → DiaDiem → MoTa → PaymentTerms → budget; canSubmit :129 = wf+project+ten). PE controller class-`[Authorize]` ONLY no policy → mở menu là đủ, no silent-403. Pe_* leaves NOT in `MenuKeys.All` (chỉ root :156); PE defaults 7 role × 11 key (root + 2type×{group,WfView,List,Create,Pending}) `DbInitializer.cs:2098-2160`. S57 `SeedAllRolesReviewReadPermissionsAsync:1993-2001` InReviewScope EXCLUDES Pe; extend đúng = `key == MenuKeys.PurchaseEvaluations` EXACT (prefix "Pe" sẽ dính PeWorkflows admin!) — root inherit cascade (`GetMyMenuTreeQuery.cs:49-82`). Demo gate: prod `appsettings.json:35 DemoSeed:Disabled=true` → 7 `[DEMO]` HĐ + 4 `[DEMO]` PE (MaPhieu `[DEMO]-A-001`) KHÔNG lên prod; UNGATED trên prod = 31 users + 18 demo NCC + 8 demo project (:2244-2315) + real 62/71/3 (:2329-2522). ⚠️ Clear-demo gotcha: seed re-add per-code idempotent MỖI startup → xóa DB-only sẽ resurrect, phải gỡ khỏi DbInitializer code. WorkItem write Admin-only (`CatalogsController:113-130`) — CatalogManager có menu-perm nhưng API write bị chặn. Tag `[s57bis, pe-recon, demo-inventory]`.
|
||||
- **2026-06-18 (S71 Harness-10 ref-sweep — wave-*/agent-teams/harvest migration map, on-disk):** ⭐ **2 RULE-MECHANISM (cơ-chế, sửa CẨN THẬN ≠ text-swap):** (1) **`.gitignore:93-94`** `.claude/workflows/wave-*/` + `.claude/agent-teams/` AFTER `!.claude/**:83` (last-match-wins) — Harness-10 LẬT containment: runs/ TRACKED nên KHÔNG gitignore (đã có `runs/_ledger.md:4` "tracked-change NGOÀI run-folder = vi-phạm" thay B6 "mọi tracked = vi-phạm"). agent-teams = n-a Windows in-process → giữ-hay-bỏ tùy. (2) **`hmw.js`** wave-mechanism: meta.description:9 (2-MODE) · args:19 `wave:{name,dir}` · SCHEMA subMdPath:52 · WAVE-MODE block:87-91 (`const wave = A.wave&&A.wave.dir`) · log:94 · subMd path:102 · writeGuard TOOL-AWARE 2-nhánh:106-120 (wave→sub-MD-isolated / default→return-delta) · prompt subMdPath:131. → run-trace = đổi `wave`→`run`, dir `wave-<tên>`→`runs/<run-id>`, +harvest/ path. **TEXT-ONLY (đổi chữ, KHÔNG cơ-chế):** `.claude/workflows/README.md` TOÀN BỘ (48 dòng, đầu-đề + table 2-MODE + structure + B1-B6 + agent-team §) · `session-end.md:32,49,51` (§L.b(d)(f) GATE + B5 wave-gom) · `session-start.md:71` (H2 báo wave-folder tồn-đọng) · `agents/README.md:111` (decision-tree wave-gom B5) + :22-28,52 harvest-curator.md (B5 scan path `wave-<tên>/sub-*.md` + agent-team) · `harvest-curator/MEMORY.md:20` (wave-folder gitignored). **DOC/HISTORY (immutable evidence — KHÔNG sửa):** `broadcasts/**` (handshake:17 + inbox/README:15 "khác wave-folder gitignored") · `docs/governance/adap-reports/2026-06-07-Agent-harness-2.md` (toàn bộ B1-B6 spec) · `docs/changelog/sessions/*` · `error-ledger.md:86` (wave-folder-leak=0 evidence). **ĐÃ SCAFFOLD Harness-10 (S71 đang chạy):** `runs/_ledger.md` (2-beat OPEN/CLOSE) + `runs/2026-06-18-h10-invest/run.md` + `harvest/`. ⚠️ **Note .gitignore:92 comment** `git check-ignore -v .claude/workflows/wave-x/wave.md` = verify-cmd cũ, đổi theo. Tag `[s71, harness-10-refsweep, wave-to-runtrace-migration, gitignore-mechanism, hmw-wave-mechanism]`.
|
||||
|
||||
- **2026-06-10 (S57 perm-broaden blocks A/B/E/F — on-disk):** ⭐ **BE AUTHZ SPLIT (decision-critical for E):** Config controllers gate WRITE behind `[Authorize(Roles="Admin")]`, READ open to any-authed: `HrmConfigsController.cs:15` class `[Authorize]`+GET open `:19-21`, all POST/PUT/DEL `Roles="Admin"`; `CatalogsController.cs:14` same (write `:23+` Admin); `MeetingRoomsController.cs:15` same (comment `:9-10` explicit). → granting FE read on Hrm_Config/Catalogs/MeetingRooms = pure UI-visibility, BE already correct. **BUT Master 3 controllers = class `[Authorize]` ONLY, no per-action role:** `SuppliersController.cs:17`, `ProjectsController.cs:11`, `DepartmentsController.cs:11` — ANY authed user can POST/PUT/DELETE via API (FE menu perm is only gate, not BE-enforced). Flag em-main: making Suppliers/Projects/Departments visible-to-all ⇒ staff can write master incl. S55 prod data unless add `[Authorize(Roles="Admin,CatalogManager")]` or per-action policy. **S55 PROD DATA location:** `SeedRealMasterDataAsync` `:2267-2460` writes to **Projects**(62, `:2270`), **WorkItems**(71=CatalogWorkItems key, `:2430-2438`), **Suppliers**(3, `:2440-2456`) — all ungated idempotent per-code. **10 departments now** (`SeedDepartmentsAsync:2104`): 9 orig + IT (`:2115`). **31 demo users** (`SeedDemoUsersAsync:1553-1609`): dept spread BOD4/PM2/CCM9/PRO6/QS2/FIN2/ACT1/EQU1/HRA2; roles = mostly Drafter/CostControl/Procurement + 1 CatalogManager (`catalog.manager@`, dept PRO, `:1608`). NO user has zero-role; every demo user authenticatable. **Inherit-root display:** `GetMyMenuTreeQuery.cs:96 HasAccess = CanRead OR Children.Any(HasAccess)` → a non-inherit root (Hrm/Off/Master) auto-shows if ANY child has CanRead, so granting leaves is enough to reveal the root node (root row itself optional for display, but grant it too for cleanliness). Inherit-roots (Contracts/Pe/Wf/PeWf) cascade root→child so root-row-only suffices there. Menu already S57-edited: `Personal` group + `Off_ChamCong` re-parent Off→Personal via `parentBackfill:1908`. Tag `[s57-perm, be-authz-split, master-write-open, s55-data, 31user]`.
|
||||
- **2026-06-18 (S71 Harness-10 STAGE-C harvest-flow recon — per-turn + 3-layer wire points, on-disk):** ⭐ **CURRENT harvest = SINGLE-POINT @session-end (B5), KHÔNG per-turn.** Driver = `harvest-curator` H2 (`agents/harvest-curator.md:22` "sau workflow-dài/cuối-session quét `wave-<tên>/sub-*.md`→gom→APPEND agent-memory/<role>"). Wired ONLY `session-end.md §L.b(f):51` (5-trục GATE + wave-folder gom B5). `session-start.md:71` = REPORT-only (báo wave tồn-đọng, KHÔNG gom). ZERO per-turn hook — `hmw.js` JS-sandbox no-fs (`hmw.js:5`), harvest deferred-to-close. **C4 per-turn-primary wire (3 chỗ):** (a) `hmw.js:122-134` prompt-builder — sub return findings; (b) NEW em-main step: ghi `runs/<id>/harvest/` SAU MỖI fan-out turn (KHÔNG đợi close); (c) `session-end.md §L.b(f):51` đổi "gom @end" → "VERIFY per-turn harvest đủ". **C5 3-layer anti-miss wire:** L1 in-run-reminder = `hmw.js` prompt + `run.md` checklist (run trước chưa-harvest → flag); L2 post-exec-rescan = `session-start.md:71` (mở rộng orphan-scan `runs/*/` tìm ledger-OPEN-no-harvest, hiện chỉ báo wave); L3 close-gate = `session-end.md §L.b(f):51` (GATE đã có, repoint wave→runs). **EVIDENCE tracked:** `git check-ignore runs/.../run.md`→matched `!.claude/**` (.gitignore:83 negation)=NOT-ignored ✓ vs `wave-*/` still gitignored (:93). Run-folder ĐÃ scaffold S71: `runs/2026-06-18-h10-invest/`{run.md·sub-md/.gitkeep·harvest/.gitkeep}+`runs/_ledger.md` (2-beat OPEN/CLOSE `:3`, orphan=OPEN-no-CLOSE). **G-015 shift:** Harness-2 "mọi tracked=vi-phạm" (wave gitignored→diff mù) → Harness-10 "tracked NGOÀI run-folder+code-disjoint=vi-phạm" (`_ledger.md:4`) → containment MẠNH hơn (run-folder in git-diff thấy sub-MD writes). Tag `[s71, h10-harvest-flow, per-turn-C4, 3-layer-C5, single-point-end-current]`.
|
||||
|
||||
- **2026-06-10 (S57 perm-broaden RECON blocks C/D — RAG down, on-disk):** ⭐ **SEED MODEL:** `SeedAdminPermissionsAsync` `DbInitializer.cs:1939-1977` Admin loops `MenuKeys.All` CRUD=true skip-existing dedup (`:1950/:1952`). Calls 2 sub: (a) `SeedPurchaseEvaluationPermissionDefaultsAsync` `:2036-2098` → **7 roles** {Drafter,DeptManager,Procurement,CostControl,ProjectManager,Director,AuthorizedSigner} Read+Update on PE keys only; (b) `SeedCatalogManagerPermissionsAsync` `:1984-2029` → role **CatalogManager** full-CRUD 9 master keys. **NO generic per-employee Read seed** — plain Drafter user sees ONLY PE keys; DOESN'T see Off_*/Hrm_*/Master/Contracts. **Most non-admin staff today see ~nothing but PE.** GetMyMenuTree `:96` filters CanRead=true. **Permission entity** `Permission.cs:3-15`: RoleId/MenuKey/CanRead/Create/Update/Delete. Dedup=app-level skip-existing per(RoleId,MenuKey), NO DB upsert. **13 AppRoles** `AppRoles.cs:23`: Admin(system)+12 employee. **4 inherit-roots** `GetMyMenuTreeQuery.cs:56-84`: Contracts/Workflows/PurchaseEvaluations/PeWorkflows — root grant auto-cascades to child IF child no own row (`:66`). Hrm/Off/Master NOT inherit → each leaf needs own row or add to switch. **GRANT-ALL pattern (block D):** mirror CatalogManager seeder but loop `roleManager.Roles` (all 13) × chosen key-set, CanRead=true only, insertion AFTER SeedCatalogManagerPermissionsAsync `:1976`. Tag `[s57-perm-recon, seed-model, no-employee-default, inherit-4root]`.
|
||||
- **2026-06-18 (S71 Harness-10 task-A — hmw.js EXACT edit-list wave→run-trace, on-disk):** ⭐ Refines sibling ref-sweep with precise diffs. **3 LOGIC edits:** (1) `:90` `const wave=(A.wave&&A.wave.dir)?A.wave:null` → `run=(A.run&&A.run.dir)?A.run:null`; (2) `:102` subMd `\`${wave.dir}/sub-${role||'task'}-${i}.md\`` → `\`${run.dir}/sub-md/sub-${role||'task'}-${i}.md\`` (⚠️ +`/sub-md/` SUBDIR — matches scaffolded `runs/<id>/sub-md/`, today FLAT); (3) `:106-120` writeGuard 2-branch keep TOOL-AWARE, reword. **CONTAINMENT-FLIP 2 strings:** `:112` "wave-folder gitignored nên KHÔNG hiện trong diff = sạch" → "run-folder TRACKED; tracked-change NGOÀI run-folder(+code-disjoint)=vi-phạm" (model = `runs/_ledger.md:4`); `:114` "file NGOÀI repo/wave-folder"→run-folder. **TEXT reword:** `:5,9(+drop stale two-tier H4.5→H8),19(args wave→run),52,55,88-91,94,108,113,131`. **VERDICT: pure mechanical** — fan-out/SCHEMA/resolveModel/parallel/checkpoint-gate ALL unchanged; only rename + path-subdir + 2 string-flips. **Read-only sub flow same** (`:111` subMdPath→em-main-scribe @P3, no Write tool). **C2/C4 stay em-main** (hmw.js no-fs `:1-5`). Tag `[s71, h10-task-a, hmw-exact-difflist, subdir-sub-md, mechanical-rename]`.
|
||||
|
||||
- **2026-06-10 (menu-order cross-repo recon SE↔NAMGROUP, RAG down, on-disk):** ⭐ **SE menu seed = `SeedMenusAsync` `DbInitializer.cs` tuple-list** (NOT partial). Văn phòng số root `Off` (Order=29) `:1769-1792`; HR root `Hrm` "Nhân sự" (Order=28) `:1754-1767` (+`Hrm_Dashboard` appended out-of-order `:1791`). ⚠️ **HR SCATTERED 2 roots:** `Hrm` holds only Hồ sơ+Cấu hình HRM(6 leaf)+Dashboard; transactional HR (Nghỉ phép/OT/Công tác/Đặt xe/Chấm công/Báo cáo CC) live under `Off` as `Off_DonTu_*`/`Off_DatXe`/`Off_ChamCong`/`Off_AttendanceReport`. **SEED = UPSERT that RE-SETS Order** (`:1845-1871` `if(existing.Order!=o){existing.Order=o}`) → reorder in code propagates to Dev/prod next deploy, NO migration. BUT Label/ParentKey/Icon NOT touched on existing rows (`:1855` comment) — rename needs separate `labelBackfill` dict `:1874`. **Order = BE-only:** `GetMyMenuTreeQuery.cs:35 OrderBy(m.Order)`; both FE `Layout.tsx` render `useAuth().menu` as-is, `staticMap`+`menuKeys.ts` = key→route ONLY (no sort). FE needs NO edit for pure reorder. **NAMGROUP "Puro" = hardcoded FE array (NOT DB seed),** index=order: client `InternalLayout.tsx:83-118` (Nhân sự 3-item / Văn phòng số 6-item FLAT / Chấm công under separate "Cá nhân" group); admin `AdminLayout.tsx:87-119` splits by function not HR/office. NAMGROUP `Đơn từ`/`Phòng họp`/`Đề xuất` = flat single links (no sub-children) vs SE deep-nested. Tag `[menu-order, se-namgroup, seed-upsert-order, fe-be-driven, s57]`.
|
||||
- **2026-06-17 (PE-workflow recon for FDC feature-plan — urgent flag + value-threshold routing, on-disk):** ⭐ **PE VALUE: NO stored "giá trị gói thầu" column.** Best-fit = winner-quote-total `SUM(Quote.ThanhTien WHERE supplier==SelectedSupplierId)` — COMPUTED (submit-guard `PurchaseEvaluationWorkflowService.cs:188-190` + `CurrentProposalTotal` in `PeBudgetSummaryDto`). Other amounts: `PE.BudgetPeriodAmount`(:40 drafter NS kỳ này)/`ExpectedRemainingAmount`(:41)/`PeWorkItemBudget.FullAmount`=(Initial??0)+(Adjustment??0) (`PeWorkItemBudget.cs:29-30`) — all budgets, not deal-value. **ROLES PRO/CCM/CEO = domain shorthand NOT constants** (`AppRoles.cs` has Procurement/CostControl/Director; PRO=Procurement CCM=CostControl CEO=Director). **V2 routing IGNORES roles** — approvers = specific `ApproverUserId` (`ApprovalWorkflow.cs:80`), OR-of-N = N Level rows same `Order` (GroupBy :687). "Phòng CCM" = seed Step NAME + non-strict DeptId hint only (`:67`). **CEO = positional (last level/last step), NOT conditional.** **ROUTING 100% LINEAR** (level→step, `DaDuyet` when `nextIdx>=steps.Count`). ZERO value/threshold/conditional config anywhere (grep 0 on AW/Step/Level/PEType). ⭐ **HOOK B (value-threshold) = `ApproveV2Async` advance block lines 816-845** (`:817` levelOrder++ / `:828-837` terminal DaDuyet / `:838-845` next step). Precedent: `skipToFinal :773-814` already "jump pointer to last step+level" — reuse mechanic conditioned on value. **HOOK A (urgent):** add `IsUrgent bit`/`PePriority` enum (mirror `ItTicketPriority{Low,Medium,High,Urgent}` `Office/Enums.cs:48-54`) AddColumn no-new-table; notify `INotificationService.NotifyAsync(userId,type,title,desc?,href?,refId?)` (`INotificationService.cs:10`)+SignalR interceptor; LogTransition notifies DRAFTER-only on terminal (`:960-980`), NO approver-notify yet. Badge DTOs: `PurchaseEvaluationListItemDto`(`PurchaseEvaluationDtos.cs:6`)+`DetailBundleDto`(:201). Type A/B (`PurchaseEvaluationType.cs:6-10`) constrains pinnable ApplicableType only — ZERO type-conditional routing. ⚠️ "Từ chối" REMOVED S60 hard-guard `:80-85` (throws even Admin; only Duyệt/Trả lại). ⚠️ drafter-in-chain bypass `:543` auto-approves drafter's own step-1 levels on submit (interacts w/ value-finalize). Tag `[pe-workflow-recon, value-threshold-hook, urgent-flag, fdc-feature-plan]`.
|
||||
|
||||
- **2026-06-09 (S56 pre-golive verify — 4 logic streams, all PASS):** Audited P11-B/D/E/F + ApproveV2 + catalogs + S55 master-wiring. **LeaveBalance** deduction exactly-once (terminal DaDuyet, guard `Status!=DaGuiDuyet` :296 blocks re-approve), FK guard Create+UpdateDraft→Conflict; **AttendanceReport** classify day-type IN-MEMORY (Holiday DateOnly HashSet), OtPolicy multiplier; **MaTicket** gen-on-Create Serializable IT/2026/NNN. Tests cover (LeaveBalance 9 + AttReport 2 + codegen 3 = 29 green). ApproveV2 4-module flatten Steps→Levels correct; **Travel/Vehicle ApproveV2 = 0 test** (cookie-cutter of tested Leave/OT — add 2 smoke post-golive). master-data **idempotency PROVEN** (DbInitializer re-run → counts identical, per-code guard :2310/:2404/:2422). **⚠️ PROD FACT (corrects stale S52 mem):** dept "IT"/Phòng CNTT DOES exist (Id 65CC6307…) but has **0 active users** on prod → ItTicket auto-assign no-ops, reassign dropdown empty, SLA job no notify-target. Pre-golive ops fix: assign ≥1 real user to dept IT (1 UPDATE, no code). Tag [s56, pre-golive-verify, logic-pass, dept-IT-empty-prod, travel-vehicle-untested].
|
||||
- **2026-06-17 (S69 recon — Office-module inventory + Hồ sơ-NS CSS-contract, on-disk):** ⭐ **PART A Office:** 21 `Off_*` keys (`MenuKeys.cs:99-121`): root `Off` + DanhBa(card-grid), `Off_PhongHop`{View=cal/Manage=room-CRUD-admin/Book}, `Off_DeXuat`{List/Create/Inbox=Proposal-V2}, `Off_DonTu`{Leave/Ot/Travel}, `Off_DatXe`, `Off_ItTicket`, `Off_ChamCong`(re-parent→Personal S57), `Off_AttendanceReport`(admin). 10 office pages `{fe-admin,fe-user}/src/pages/office/` ALL SHA256-MIRROR except **MyAttendancePage DIFFERS** + AttendanceReportPage ADMIN-ONLY. Routes `App.tsx` user:70-80/admin:88-100; staticMap `Layout.tsx:87-103` (workflow-apps :kind `/workflow-apps/{leave,ot,travel,vehicle}`); menuKeys.ts:45-63. **HIDE-FLAG** `RevokeTemporarilyHiddenModulesAsync` (`DbInitializer.cs:2157-2190` called :2040 LAST) wipes CRUD on `MenuKey.StartsWith("Off")||"Hrm"||==Personal` non-Admin, idempotent. **Golive flip:** remove :2040 call (+ re-add prefix InReviewScope grant). Office already S55-shell polished NOT bare. **PART B Hồ sơ-NS CSS:** layout=3-col flex (`EmployeesListPage.tsx` SHA256-identical x2, 1597 LOC): cây-tổ-chức TRÁI(:178) + NV-list MID(:244) + detail PHẢI = avatar-header `app-gradient-brand`(:643)+`text-white!`(:653)+initials chip bg-white/15 → 5-TAB(:507 Tổng quan/Thân nhân/Trình độ/Kinh nghiệm/Hợp đồng) → `Card`(:1526 left-rail+icon-chip) w/ `Field`(:1572 label uppercase accent-tint + value `font-medium text-brand-800`, empty=`text-slate-300 —`). `ACCENT` map :497-503 Record<5,{chipBg/chipFg/head/rail/labelText}> accent∈{brand,teal,violet,amberx,greenx}, palettes stops 50/100/500/600/700 only no-800→headings -700 (brand -800 OK). Tokens `index.css`: brand-600=#1f7dc1 brand-800=#175685 @theme:5-55, font Be-Vietnam-Pro:53; classes `.app-gradient-brand`(:105 120deg b600→700→800),`.card-accent`(:112),`.icon-chip`(:128 --chip-bg/--chip-fg),`.stat-value`(:140),`.label-eyebrow`(:89). ⚠️ **GOTCHA #66 = `index.css:79-83` `h1,h2,h3,h4{color:#0b1220;font-weight:700}` OUTSIDE @layer** → TW-v4 unlayered wins → heading-tag inside gradient MUST `text-white!`. ⚠️ **CROSS-APP DRIFT:** fe-user=S68 (h1-4 #0b1220/700, label-eyebrow brand-600, 175L); **fe-admin STILL OLD** (h1-4 #0f172a/600, label-eyebrow #64748b slate, 167L) — fe-admin NOT synced S66-68 heading bump → mirror Office to fe-admin needs index.css sync. Tag `[s69, office-inventory, hoso-css-contract, gotcha66, fe-admin-css-drift]`.
|
||||
|
||||
- **2026-06-09 (S56 Phase 2 FE-redesign RECON — 25 page audit, on-disk):** ⭐ **NOT a rewrite** — S55 already redesigned ui-primitives + DataTable + shell → any page importing `ui/{Button,Input}`+`DataTable` AUTO-inherits density. **Hover-hidden quick-win nearly absent:** repo-wide grep `opacity-0`×`group-hover` = **only 1 real site** `ContractCreatePage.tsx:196` (EXCLUDED scope) + DataTable.tsx:15 is a comment forbidding it (good). In-scope = **0 hover-hidden fixes**. **DataTable adoption split:** only 5/25 use `<DataTable>` (Suppliers/Projects/Departments/Users/Forms); 12 pages roll RAW `<table>` (MeetingRooms/Catalogs/HrmConfigs/EmployeesList/Proposals/WorkflowApps/Attendance×2/MenuVisibility/Roles) → custom density pass needed. **Drawer ≥8-field candidates = 3:** Suppliers (9 fld, Dialog), Projects (10 fld, Dialog), Users-CREATE (~8 fld w/ roles multiselect, 4 Dialogs total but only create is big). All currently big-Dialog → convert. **NO `Drawer.tsx` exists** (`ui/` = Button/Dialog/Input/Label/Select/Textarea only) → build first. **Bậc-thang reference ALREADY EXISTS:** `EmployeesListPage.tsx` (1200L) = canonical inline add/edit-row for 5 satellites (`setEditing{X}Id` + `addingX` mutex, :256-356) → extract `InlineEditRow` pattern from here, reuse for Catalogs/HrmConfigs/MeetingRooms (these 3 currently edit via Dialog :251/:316/:232, ≤7 cols → bậc-thang candidates). **Modal-detail = NONE:** ProposalDetail:275 + WorkflowAppDetail:424 Dialogs are tiny action-confirm (just `<Textarea>` ý kiến), detail body already inline 2-col grid (:181) → NOT convert. **fe-user mirrors** office/master/hrm (SHA256-identical per comments) but NO system/forms/reports mirror. **Custom-layout heavy:** InternalDirectory (card-grid :124 not table), MeetingCalendar (693L FullCalendar), EmployeesList (2-panel), HrmConfigs (declarative KIND_CONFIG :45). Effort: Master 3×Drawer=M, Catalogs/HrmConfigs/MeetingRooms bậc-thang=M, Users Drawer=M, rest S (auto-inherit polish). Surprise: hover-hidden NAMGROUP-win essentially pre-solved (team already avoids opacity-0 pattern, DataTable comment enforces) → quick-win section nearly empty. Tag `[fe-redesign-p2, recon, drawer-3, basc-thang-ref-exists, s56]`.
|
||||
- **2026-06-18 (S71 Harness-8/hmw/pending/sleep audit — fidelity ground-truth, on-disk):** ⭐ **H8 all-inherit = FULLY ADOPTED both layers.** (1) Frontmatter: ALL 12 `.claude/agents/*.md` = `model: inherit` (11 sub + README; grep-count 12/12, zero demoted). (2) `hmw.js resolveModel:36-44` = all-inherit (`:43` role-with-frontmatter→`undefined`=inherit; `:41` invalid-role→fail-UP inherit; `:42` role-less→inherit). **Per-task escape-hatch PRESENT** `:37` `if(tier==='fable'||'opus')return tier` (H8.1 "ngoại lệ per-task" satisfied). **hmw run-trace = args.run + legacy args.wave alias** `:91` `(A.run&&A.run.dir)?A.run:((A.wave&&A.wave.dir)?A.wave:null)` ✓. ⚠️ **GAP-1 (the one real gap): hmw FLAT-vs-SUBFOLDER.** hmw STILL emits SUBFOLDER: `:103` `subMd=${dir}/sub-md/${role}-${i}.md` + desc/`:113` reference `run.md+sub-md/+harvest/`. The 2026-06-18 `h10-flat-detector-refine` (supersedes-part-of checklist-9-10 §C1/C2/C8) mandates FLAT files-one-level distinguished BY FILENAME, no subdirs. Disk confirms subfolder: `runs/2026-06-18-h10-invest/` has `harvest/`+`sub-md/` dirs (not flat). **GAP-2: SE adopted checklist v1 NOT v2** (`broadcasts/outbox/ai_infra/2026-06-18-...checklist-adopted.md:16` cites `2026-06-16-Governance-checklist-harness-9-10` = v1, no `-v2`). **UNADOPTED broadcasts (2 confirmed PENDING):** `2026-06-18-h10-flat-detector-refine` + `2026-06-18-checklist-harness-9-10-v2`. NO other newer (last outbox/all = these two; SE outbox last report = 2026-06-18 v1). **C3 two-level VERIFIED:** `git check-ignore runs/` exit=1 (NOT-ignored, neg `!.claude/**` :83) + `git ls-files runs/` = 22 files committed. `wave-x/wave.md` check-ignore exit=0 (still gitignored :93). **sleep-recovery-memory-l2 = AI_INFRA-internal** (`AI_INFRA/.claude/commands/sleep-recovery-memory-l2.md` + `docs/architecture/MEMORY-SLEEP-RECOVERY-L2-DESIGN-v3.md:scope` "CHỈ L2, KHÔNG sister"; NOT in outbox/all — only its Harness-9 broadcast form is, already adopted S70). NOT a sister obligation. **SE already does equivalent FULLY** (not ad-hoc): `memory-budget.json` (caps seeded-by-measure `scripts/measure-agent-memory.ps1`) + 4× `archive/_INDEX.md` + 4× `*.gist.md` (`distill-gen:2` counter) + `.ragignore` (exclude index/gist). Tag `[s71, harness8-audit, hmw-flat-gap, checklist-v1-not-v2, sleep-recovery-ainfra-internal, gist-additive-done]`.
|
||||
- **[→ git pre-S60]** S60 recon#2 V2-engine-map (ApprovalWorkflow.cs Step/Level Order 1-based per-step; OR-of-N=N rows cùng Order service GroupBy:475; ApproveV2Async:446-634 guard+UPSERT+advance; notify DRAFTER-only:748; skipToFinal F2:561-602 = precedent advance-không-ghi-opinion) · S60 PE Section-3 submit-guard (submit path POST/pe/{id}/transitions→TransitionAsync:38 ROLE-only guard NO data-check; Section-3 mục a/b/c/d map — SUPERSEDED bởi S65ter post-Mig50 Budget-drop; test mirror PurchaseEvaluationWorkflowServiceGuardTests). Full text git.
|
||||
|
||||
- **2026-06-09 (S55 master-data Excel-import recon — 3 master + seed mechanism, on-disk):** ⭐ **"Hạng mục"/WorkItem master TỒN TẠI** — `Domain/Master/Catalogs/WorkItem.cs:6-14` (Code(50)UNIQUE-filtered/Name(200)/Category(100,idx)/DefaultUnit(50)/Description/IsActive), config `CatalogsConfiguration.cs:60-74`, full CRUD `CatalogsFeatures.cs:260-324` → group(VẬT TƯ/THẦU PHỤ/MEP)→Category, "1 Mat"→Code, item→Name. KHÔNG cần table/migration mới. **PE detail = pure free-text** (`PurchaseEvaluationDetail.cs` GroupCode/GroupName/ItemCode/NoiDung strings, NO FK→WorkItem) → load WorkItems non-breaking. **Project** (`Project.cs:5-14`, cfg `:14-21`): Code(50,UNIQUE `[IsDeleted]=0` Mig47)+Name(200) REQUIRED, StartDate/EndDate/BudgetTotal(18,2)/Note(1000)/ManagerUserId optional. ❌ **THIẾU Year/Investor/Location/Package** — chỉ Note free-text catch-all. Create cmd `ProjectFeatures.cs:67` dup-check `:87 AnyAsync(Code==)`. **Supplier** (`Supplier.cs:5-16`, cfg `:14-27`): Code/Name req + Type enum + TaxCode(20)/Phone/Email/Address/ContactPerson/Note. `SupplierType.cs`: NhaCungCap=1/NhaThauPhu=2/ToDoi=3/DonViDichVu=4/ChuDauTu=5. ❌ **THIẾU Status/TinhTrang (KHÔNG có field/enum nào)** + bank-acct + legal-rep (≠ContactPerson) + quality-score; "Cả hai" PHÂN LOẠI unmappable (Type single-valued). Create `CreateSupplierCommand.cs:10` dup `:45`. **Seed = idempotent `existingCodes.Contains→skip`** (`DbInitializer.SeedDemoMasterDataAsync:2149`, today 18 supplier `:2155` + 8 project `:2222`; WorkItems 15 rows tuple-loop `SeedCatalogsAsync:576-599`). **NO bulk import** — Master chỉ single CRUD; Import/Upload hits = Forms/PE/Employees attachment only; POST one-at-a-time. **Seed→prod:** `DbInitializer.InitializeAsync` chạy MỌI startup (`Program.cs:197` unless `--no-db-init`) → `MigrateAsync` THEN seed; demo gated `config.GetValue<bool>("DemoSeed:Disabled")` (`:80`) NHƯNG SeedDemoMasterData+SeedCatalogs chạy BẤT KỂ flag (ngoài if-block :108/:115) → seed method mới auto-reach prod next deploy. Rec: idempotent DbInitializer mirror (NOT API loop). Surprise: real+demo data sẽ trộn chung Suppliers/Projects/WorkItems (18/8/15 demo rows) → cân nhắc gate demo off prod. Tag `[master-import, workitem-exists, seed-idempotent, s55]`.
|
||||
- **2026-06-16 (S65bis recon — Employee profile master-detail vs NamGroup, on-disk):** ⭐ **STALE-PREMISE CORRECTION:** fe-user `/employees` KHÔNG list-only — `hrm/EmployeesListPage.tsx` (1201 LOC) ĐÃ master-detail 2-panel (filter sidebar :117 + list table :197 + inline detail :234) với **6 collapsible section** (`<details>` :1157, KHÔNG tab) + 5 satellite inline CRUD (WorkHistory/Education/FamilyRelation/Skill/Document, `setEditing{X}Id`+`adding{X}` mutex pattern 12-ter S35). **fe-admin == fe-user `diff -q` IDENTICAL** (SHA256 same). **Entity gần đủ screenshot:** `Domain/Hrm/EmployeeProfile.cs` (137 LOC) CÓ: DOB/Gender/Ethnicity/Religion/Nationality/Height/Weight(:98-99)/IdCard(số+ngàycấp+nơicấp :52-54)/permanent+temporary addr/phone/personalEmail/code/hireDate/qualification/salary(Base+Total)/bank/4×leave-days. **THIẾU vs screenshot:** (a) BloodType CÓ nhưng "sức khỏe loại" (health-grade A/B/C) KHÔNG; (b) thâm niên = DERIVED từ HireDate (no column); (c) chức danh = `User.Position`/`PositionLevel` (Identity, KHÔNG ở EmployeeProfile) — list/detail JOIN Users (`EmployeeFeatures.cs:467`); (d) "lương BHXH/phụ cấp" tách riêng KHÔNG có (chỉ Base+Total); đơn vị=DepartmentName JOIN. **5 satellite entity + 15 endpoint FULL** (`EmployeesController.cs:75-233` 5 region×Create/Update/Delete; GET detail Include cả 5 :455-459). Skill polymorphic gộp 3 NamGroup table (Computer/Language/Other Kind :69). **GAP THẬT:** (1) **NO org-tree** — `Department.cs` FLAT (Code/Name/ManagerUserId/Note, KHÔNG ParentId), `DepartmentsController` chỉ GET list+byId (NO /tree), KHÔNG endpoint count-per-dept → cây trái + badge phải build CLIENT-side group-by departmentId từ list; (2) **5-tab layout** screenshot = 6-section `<details>` hiện tại (re-skin UI, data đủ); (3) "Hợp đồng lao động" tab = chỉ có `EmployeeDocument` type=LaborContract(5), KHÔNG entity HĐLĐ riêng (3 HĐLĐ table DEFER Plan H2 per `EmployeeProfile.cs:10`). **NamGroup source:** `D:\...\NAMGROUP\SOURCECODE_CÔNG_TY\` find .tsx/.razor = 0 hit (KHÔNG phải React/archived) — RAG `proj_namgroup_main` 0 component; tham khảo layout = screenshot anh gửi, KHÔNG có code mirror trực tiếp. ⇒ **Wire-lại-là-xong:** data + API + satellite CRUD 100% sẵn. **Build mới:** Department.ParentId migration + /tree + count endpoint (nếu muốn org-tree thật thay client-group). **Re-skin:** 6-section→5-tab + avatar header. **No new field bắt buộc** trừ health-grade nếu anh cần. Tag `[s65bis, employee-profile, master-detail-EXISTS, dept-flat-no-tree, stale-list-only-corrected]`.
|
||||
|
||||
- **2026-06-17 (S69 recon — NamGroup "PURO" digital-office layout, CROSS-REPO `D:\...\NAMGROUP\`):** ⭐ **PURO = UI design-language/skin (ref ERP demo.purocorp.vn), KHÔNG phải app riêng** — NamGroup mirror sidebar/typography của nó (comments `InternalLayout.tsx:33,74,109,200,332` "PURO exact spec"). Digital-office sống trong `namgroup.client/` (app NV; admin = config-only). **Shell = `components/layout/InternalLayout.tsx`** (724L): sidebar trái fixed h-screen + `<main flex-1 overflow-auto p-2.5..lg:p-4>` :609 chứa `<Outlet/>`. Sidebar = **`navTree` hardcoded array :76-122** (KHÔNG DB), flat 2-tier group→leaf, 4 group: **"Văn phòng số" :90-100 = 6 leaf** {Danh bạ `/danhba` · Phòng họp `/phonghop` · Đề xuất `/dexuat` · Đơn từ `/dontu` · Đặt xe công `/xecong` · Ticket CNTT `/ticket`}; + Nhân sự(3) + Cá nhân{Chấm công}(1) + Hệ thống(5). Routing = `App.tsx:81-140` flat `<Route element={InternalLayout}>` > `<RouteGuard>` (perm) > index=HomePage. **Landing `/` = `internal/HomePage.tsx`** (296L): grid 2-col (LEFT 2/3 stack 4 WidgetCard: Đề xuất/Nghỉ phép/Bình luận/Truyền-thông · RIGHT 1/3 Công-việc-của-tôi); WidgetCard = gradient-blue header + inline stat-chips + body/EmptyState (shared comp tại :219). **Layout pattern mỗi feature (KHÔNG dùng tab — dùng KpiCard-row + view-toggle):** (a) `<PageHeader>` shared (icon-badge accent + breadcrumb + actions slot, `components/shared/PageHeader.tsx`); (b) **KPI stat-cards clickable filter** (DeXuat :1643 6-card / Ticket :197 5-card — "PURO KPI cards" comment); (c) body = **list-table** (DeXuat/Ticket: `<table>` master + right detail panel grid-cols-3) HOẶC **list↔calendar ViewToggle** (DonTu :683 + XeCong + PhongHop: custom month-grid Sun-Sat, NO FullCalendar — comment :PhongHop "saves install friction"); DanhBa = dept-tree trái + card-grid phải. **Shared comp tái dùng:** PageHeader · DataTable · KpiCard · CrudToolbar · ActionLogList · DatePickerVN · MasterDataPage · DonutChart/MiniBarChart (`components/shared/` 16 file). **Top files mirror:** InternalLayout.tsx(shell+nav) · HomePage.tsx(dashboard) · PageHeader.tsx · DeXuatPage.tsx(1676 KPI+table+detail) · DonTuPage.tsx(1269 +DonTuCalendar.tsx toggle) · XeCongPage/PhongHopPage(month-grid) · TicketPage.tsx(595) · DanhBaPage.tsx(756 tree+grid) · ChamCongPage.tsx(809). ⚠️ Style/màu lấy chỗ khác per task — chỉ structure. SE đã có analog (fe-user `InternalLayout`/office pages) nhưng layout khác (deep-nested vs PURO flat). Tag `[s69, namgroup-puro-recon, digital-office-layout, hardcoded-navtree, kpicard-not-tab, cross-repo]`.
|
||||
- **[→ archive/2026-06.md + git]** S52 P11-D/E/F 6-gap recon (IT-pool absent → S56 corrected: dept IT exists 0 user · SlaExpiryJob HostedService DI:46 pattern · OtPolicy 3-multiplier · ClosedXML exporter reuse · Attendance API cá nhân-only · FE skeleton state — full text git pre-S60) · S50 P11-C HrmConfigs add-kind 11-chỗ pattern · S50 wave h2-verify B6 gitignore ordering + POSIX-not-pwsh (curated S57bis) · S51 gotcha #57 EXT reachability 3-Master-fix/3-skip global-filter-makes-bug (curated S59).
|
||||
|
||||
- **2026-06-07 (Harness 1/2/3 adap-apply recon — 3 slice, HMW wave):** Governance recon AI_INFRA broadcast harness-1/2/3. **H1/H2 (Harness 1):** roster 8→10 — CREATE 2 sub TÁCH BIỆT `tooling-auditor` (H1 freshness 4-mặt skill/sub-role/plugin/docs) + `harvest-curator` (H2 integrity 5-trục). H2 PARTIAL sẵn: `session-end.md` Phase 1.5 §L.b(d) spawn-record 4-field + (f) double-check moved-not-cut + (c) 0-byte AS-8 = Coverage+Completeness+Corruption (3/5); THIẾU Fidelity-escalate + Placement. RE-REPORT @session-start = 0 (chỉ generic Phase 2.7). 2 sub mirror inv-codebase read-set + store_memory strip + NO Write/Edit; color brown+teal (8 màu cũ hết). **H2 wave (Harness 2):** SE `hmw.js` = OLD pre-wave (no subMdPath/writeGuard/wave-block); AI_INFRA `hmw.js` = canonical template. ⭐ `git check-ignore -v` = ground-truth B6: `.claude/workflows/wave-test/wave.md` HIỆN match `.gitignore:83 !.claude/**` = TRACKED → wave pattern PHẢI đặt AFTER `!.claude/**` (last-match-wins, mẫu `hmw-mode.on` :87). Read-only sub (4)=inv-cb/inv-api/reviewer/cicd; Write sub (4)=impl×2/test/fe-designer. B5 depends H2 harvest-curator. **H3 email (Harness 3):** broadcasts/ absent; id authoritative = `se` (NOT solution_erp), 6 others short `{ai_infra,vipix,dyd,namgroup,ashico,bvaau}` từ `AI_INFRA/broadcasts/sister-commands/send-email.md:13-22` (folder name = 2nd source-truth); `adap-apply.md:14` base-path STALE flat → `outbox/all/*.md` (latent bug). broadcasts/ ở root → commit OK (no gitignore rule). **Containment post-P2:** git-diff bắt 1 file-write (inv-api self-MEMORY), chunk-count 2414=2414 (0 RAG-write) = defense-in-depth proven. Tag [harness-recon, governance, hmw-wave, 2026-06-07].
|
||||
|
||||
- **2026-06-01 (P11-C Vehicle+Driver catalog pre-flight):** Mig 44 next (latest=Mig 43 `FilterHolidayUniqueIndexByIsDeleted` S45). **NO Vehicle/Driver master exists** — chỉ `Office/VehicleBooking.cs` (request, Mig 39) dùng FREE-TEXT (`VehicleLicense`/`VehicleName`/`DriverName?` strings, :13-19 comment "defer catalog Phase 11"). **RECOMMEND home = extend HrmConfigs** (NOT new module): `Application/Hrm/HrmConfigFeatures.cs` mega 4-region + `HrmConfigsController` (`[Authorize]` read / `[Authorize(Roles="Admin")]` write) — add Region 5 Vehicle + 6 Driver (kind `vehicles`/`drivers`), pattern proven 12-bis. ⚠️ HRM entities KHÔNG global HasQueryFilter → manual `.Where(!IsDeleted)` + UNIQUE soft-delete cần `.HasFilter("[IsDeleted]=0")` (Holiday Mig 43 lesson, LeaveType/Shift UNIQUE Code chưa có filter → nếu Vehicle BienSo UNIQUE phải add filter). **FE cheap:** `HrmConfigsPage.tsx` declarative KIND_CONFIG Record — add 2 entry vào KIND_CONFIG + KINDS[] + `renderCells` branch + smart-defaults; NO new page. **Menu+perm:** add 6 const `MenuKeys.cs` (+`Hrm_Config_Vehicles/Drivers`), thêm vào `All[]` (:140) → Admin auto-grant qua `SeedAdminPermissionsAsync` loop (:1909 idempotent), +2 MenuItem `DbInitializer` :1757, +2 `menuKeys.ts` mirror. Hrm_Config KHÔNG inherit-root (4 root=Contracts/Workflows/Pe/PeWf only) → leaf cần row riêng (loop lo). **Fields (NamGroup XeCong DROPPED Mig 2026-05-15, ref response shape only):** Vehicle{Code/BienSo UNIQUE, Hang, MauXe, SoCho int, TrangThai, GhiChu}; Driver{Code/Hoten, SDT, GPLX, Hang bằng, TrangThai}. FK link defer: P11-C = catalog only, optional FK `VehicleBooking.VehicleId?/DriverId?` giữ free-text back-compat (Mig sau). Tag `[pre-flight, p11-c, vehicle-driver-catalog]`.
|
||||
- **2026-06-01 (MONTHLY DRIFT AUDIT):** Ground truth code: **migrations=42** (last `AddLeaveBalances`, path `.../Persistence/Migrations/*.cs`) · **gotchas highest=#56** (file header NO self-count → drift chỉ ở file *reference* gotchas.md) · tests=154 (58 Domain+96 Infra, em main verified) · tables≈91 (45 config class nhưng Catalogs=4+ContractDetails=7+7 Identity untracked → khớp STATUS 91, KHÔNG cheap-exact). **Biggest drift: ef-core-migration SKILL** (frontmatter:3 "31 migration"→42, :19 history "31"→42 + thiếu rows Mig 27-42, :50 "59 bảng"→91, :80 "111 test"→154, :258/:267 "59 bảng"). dependency-audit SKILL:153 "49 bẫy"→56. CLAUDE.md:53 "40 mig→84 bảng"→42/91, :66 "130 test"→154, :133 "52 bẫy"→56. docs/CLAUDE.md:65 "52"→56. **schema-diagram GAP:** migration TABLE dừng Mig 16 (line 487); detail § cuối =§15=Mig 26 (§16=Related KHÔNG phải mig) → thiếu § cho **Mig 27-42** (16 mig). database-guide:4 "47 bảng/13 mig"→91/42. STATUS:97 backlog "Curate 4 agent MEMORY 35.7/35.3/30.9/28.4" STALE (đã curate S40 `78c9de3`, all ≤16KB) → REMOVE. NO-CHANGE: contract-workflow (historical counts OK), form-engine, iis-deploy (no count), HANDOFF (S43 current), PROJECT-MAP (no count). Tag `[drift-audit, monthly, 2026-06]`.
|
||||
- **2026-05-30 (P11-A WorkflowApps wire pre-flight):** 4 module Leave/OT/Travel/Vehicle. Schema pin ĐÃ CÓ SẴN (Mig 39): `Office/{Module}.cs` đều có `ApprovalWorkflowId?`+`CurrentApprovalLevelOrder?`+`WorkflowAppStatus` (5-state khớp ProposalStatus). SKELETON tại `Application/Office/WorkflowAppsFeatures.cs:11-15` (chỉ Create+List, KHÔNG Approve/Reject/Return/GetById). **Proposal = mirror HOÀN HẢO** (cùng Office ns, Mig 38): `ProposalFeatures.cs:403-486` ApproveHandler = flatten `Steps.OrderBy(Order).SelectMany(Levels.OrderBy(Order))` global level index → `allLevels[CurrentApprovalLevelOrder-1]` → actor match `Level.ApproverUserId==uid||Admin` → UPSERT LevelOpinion → advance++/DaDuyet. ⚠️ `ApprovalWorkflow.cs:72` nói Level **KHÔNG OR-of-N** (1 ApproverUserId/Level) — KHÁC memory cũ "OR-of-N", verify lại. Gap: 4 bảng `{Module}LevelOpinion` mới (Mig 41+), 3 route/controller, 4 seed WF, FE Detail+Opinion (chỉ có WorkflowAppsListPage chung). ⚠️ enum `ApplicableType` THIẾU Travel (có Leave=5/OT=6/Vehicle=7/ItTicket=8); `ExtendApplicableTypeForWorkflowApps` mig empty Up/Down (enum-only). Tag `[pre-flight, p11-a, workflowapps]`.
|
||||
- **2026-05-29 (S40 STATE GROUNDING):** 7 metric verify. ✅ Migrations=**40** (path `.../Persistence/Migrations/*.cs`, last `AddAttendances`). ✅ Gotchas=**55** (`### N.`). ✅ git clean. DbSet=77 nhưng **SQL tables=84** (em main verify `.ToTable()` ModelSnapshot — 77+7 Identity, "84 docs ĐÚNG", DbSet count sai −7). Endpoints=**211** (docs ~223). FE pages fe-admin **36**+fe-user 29=**65** (docs 53 under-count). Menu keys=**53** const (docs 85 over-count). 3 số tin cậy nhất = mig/gotcha/git. Lesson: tables phải count ToTable KHÔNG DbSet. Tag `[state-grounding, docs-drift, s40]`.
|
||||
- **2026-05-29 (S39 BVAAU 7-agent extract):** Đọc 8 file BVAAU `.claude/agents/` ~22K. Split 4→7 trục research(2)/implement(2)/quality(3). Boundary: repo interface=domain, EF config=infra, test=test-specialist. Tool: cả 7 agent 5 RAG MCP (+search_code BM25 +store_memory +list_projects). BVAAU Phase 0 codebase RỖNG → aspirational template chưa battle-test; SOLUTION_ERP giữ 6 skill + backend/frontend split (thay domain/infra cho 2-FE fit). VIPIX guide claim KHÔNG verify được (file miss). Tag `[cross-project, bvaau, port]`.
|
||||
- **Archived S29-S37 → `archive/2026-05-q4.md` + git d2f52ba (S40 curate):** S36 G-O2 Phòng họp clean-room + FullCalendar v6 MIT eval · S36 startup MEMORY-size audit · S35 G-H2 HRM clean-room verdict · S33 G-H1 NamGroup TblNhanVien 10-bảng (105 cols main) · S33 startup RAG verify · S32 Plan G 11-module backlog · S29 Plan CA+B pre-flight (3 patterns: 9-menu terrain, V1+V2 coexist, reference-template-paths cite line-range ROI). KEY absorbed: **clean-room > NamGroup port verified 4×** · Pattern 12-bis cross-module mirror · FK+freetext dual-write.
|
||||
|
||||
- **2026-06-18 (S71 audit — Harness-9 PART B adap-2workflow trung-thuc, on-disk):** ⭐ **VERDICT B substantially-LANDED, ZERO nac-inflation (reports UNDER-state via honest hedge).** B1/B2 ok-runtime: 2 adap cycle ran SEPARATE workflows distinct run-id — S70 impl `wf_a58e0d15-beb`≠audit `wf_9520d8cd-4fe`; S71 impl `wf_e4e46725-231`≠review `wf_636bc95b-939` (review caught real C5-L1 over-claim impl-self-check missed). B2.5 ok: reverse-findings non-empty both reports (3+4 items)+both emails. B3 ok: 2 emails `broadcasts/outbox/ai_infra/{2026-06-17,2026-06-18}-se-to-ai_infra-*.md` carry true-nac+findings+BOTH run-ids. B4 **partial/convention-met**: rule codified `adap-apply.md:38` (short-but-confirm→review) BUT no runtime instance (no short-decision task arose S70/S71) → pure-convention, lead-discipline. ⚠️ **3 PRECISION-flaws (not protocol-gap):** (1) reviewer-agent StructuredOutput unreliable both sessions → em-main self-gate git/sha (disclosed, valid-branch per feedback memory). (2) **PATH-TRAP:** S71 report/email self-verify cited bare `git ls-files runs/`=14 — WRONG; actual = `.claude/workflows/runs/` (22 tracked files, 5 runs: invest/implement/review + h910-finalize `wf_73de399d-753` + h910-curate `wf_f32987b8-03f`). Auditor running bare `runs/` from repo-root gets 0 → false "not-committed". Folder genuinely committed `8c47bd0` + NOT-ignored at correct path. (3) hmw.js RUN-TRACE runtime honest-flagged pending-restart. adap-apply.md:33-36 codify 2-workflow mandate (auto-loaded). Tag `[s71-audit, harness-9-partB, adap-2workflow, path-trap-runs-folder, b4-convention-gap, no-nac-inflation]`.
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Curate trigger
|
||||
- >~30KB → archive recent → L2 `archive/<period>.md`. Stale >3mo → remove.
|
||||
- **Last curate: 2026-05-29 S40 em main proxy** (35.7→~20KB): archived 7 FIFO S29-S36 → q4 + git d2f52ba, refreshed stale essentials S25→S40 numbers, trimmed memory-list. Prev: S34 q3 · S32 q2 · S22 q1.
|
||||
- **Last curate: 2026-06-18 S71 Harness-9 L1→L2 (auto-inject 25600B cap)** (29.8→23.2KB): FIFO-trimmed 3 oldest 2026-06-16 entries (S66/S65ter/S65) → `archive/2026-06.md` (additive +6 -0, md5 `cedc21eb…` byte-exact) + 3 rows `_INDEX.md` (unique substring) + 3 4-field gist `2026-06.gist.md` (distill-gen:1→2, 15→18 records). 0-byte-loss verified (numstat additive + md5 match). Kept S65bis/S69/S71. Prev: S70 (47.0→24.1KB dark-matter recovery) · S40 (35.7→~20KB) · S34 q3 · S22 q1.
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
# Investigator-Codebase — Archive Q4 2026-05 (S39-S40 era)
|
||||
|
||||
> Moved from MEMORY.md L1 (Harness-9 curate 2026-06-17). Verbatim — byte-exact, no reflow.
|
||||
> Created S69: MEMORY.md referenced `archive/2026-05-q4.md` (S40 curate stub + footer) but the file was absent on disk (content was git-only `d2f52ba`). This file now holds the 3 May FIFO-overflow entries; the S29-S37 batch remains in git `d2f52ba`.
|
||||
|
||||
---
|
||||
|
||||
- **2026-05-29 (S39 BVAAU 7-agent extract):** Đọc 8 file BVAAU `.claude/agents/` ~22K. Split 4→7 trục research(2)/implement(2)/quality(3). Boundary: repo interface=domain, EF config=infra, test=test-specialist. Tool: cả 7 agent 5 RAG MCP (+search_code BM25 +store_memory +list_projects). BVAAU Phase 0 codebase RỖNG → aspirational template chưa battle-test; SOLUTION_ERP giữ 6 skill + backend/frontend split (thay domain/infra cho 2-FE fit). VIPIX guide claim KHÔNG verify được (file miss). Tag `[cross-project, bvaau, port]`.
|
||||
|
||||
- **2026-05-29 (S40 STATE GROUNDING):** 7 metric verify. ✅ Migrations=**40** (path `.../Persistence/Migrations/*.cs`, last `AddAttendances`). ✅ Gotchas=**55** (`### N.`). ✅ git clean. DbSet=77 nhưng **SQL tables=84** (em main verify `.ToTable()` ModelSnapshot — 77+7 Identity, "84 docs ĐÚNG", DbSet count sai −7). Endpoints=**211** (docs ~223). FE pages fe-admin **36**+fe-user 29=**65** (docs 53 under-count). Menu keys=**53** const (docs 85 over-count). 3 số tin cậy nhất = mig/gotcha/git. Lesson: tables phải count ToTable KHÔNG DbSet. Tag `[state-grounding, docs-drift, s40]`.
|
||||
|
||||
- **2026-05-30 (P11-A WorkflowApps wire pre-flight):** 4 module Leave/OT/Travel/Vehicle. Schema pin ĐÃ CÓ SẴN (Mig 39): `Office/{Module}.cs` đều có `ApprovalWorkflowId?`+`CurrentApprovalLevelOrder?`+`WorkflowAppStatus` (5-state khớp ProposalStatus). SKELETON tại `Application/Office/WorkflowAppsFeatures.cs:11-15` (chỉ Create+List, KHÔNG Approve/Reject/Return/GetById). **Proposal = mirror HOÀN HẢO** (cùng Office ns, Mig 38): `ProposalFeatures.cs:403-486` ApproveHandler = flatten `Steps.OrderBy(Order).SelectMany(Levels.OrderBy(Order))` global level index → `allLevels[CurrentApprovalLevelOrder-1]` → actor match `Level.ApproverUserId==uid||Admin` → UPSERT LevelOpinion → advance++/DaDuyet. ⚠️ `ApprovalWorkflow.cs:72` nói Level **KHÔNG OR-of-N** (1 ApproverUserId/Level) — KHÁC memory cũ "OR-of-N", verify lại. Gap: 4 bảng `{Module}LevelOpinion` mới (Mig 41+), 3 route/controller, 4 seed WF, FE Detail+Opinion (chỉ có WorkflowAppsListPage chung). ⚠️ enum `ApplicableType` THIẾU Travel (có Leave=5/OT=6/Vehicle=7/ItTicket=8); `ExtendApplicableTypeForWorkflowApps` mig empty Up/Down (enum-only). Tag `[pre-flight, p11-a, workflowapps]`.
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
# Gist 2026-05 — investigator-codebase (q1+q2+q3+q4 distilled)
|
||||
|
||||
> `distill-gen: 1` (already-distilled — do NOT re-compress).
|
||||
> 4-field per record: **VIỆC** · **KẾT-LUẬN** (+file:line/commit) · **BÀI-HỌC** · **BẤT-NGỜ**. Same-topic records merged. Confidence tag cao/vừa/thấp.
|
||||
> Each line ends with a back-resolve `substring:"…"` (unique Ctrl-F) into the named verbatim file. Files: q1=2026-05-q1.md · q2=2026-05-q2.md · q3=2026-05-q3.md · q4=2026-05-q4.md.
|
||||
> This is the lossy lens; the verbatim files hold the full text. The 22-marker coverage gate is satisfied here.
|
||||
|
||||
---
|
||||
|
||||
## Bug RCAs (PE workflow, F-features)
|
||||
|
||||
- **[cao] F4 budget-edit broken at readOnly short-circuit** · VIỆC: UAT admin tick F3+F4 for Approver slot, menu Duyệt → Section2 + budget-adjust stay read-only. KẾT-LUẬN: F3 wire OK (`PeDetailTabs.tsx:118 itemsReadOnly = readOnly && !approverEditMode`); **F4 BUG `BudgetAdjustSection :973 canAdjust = !readOnly && (…isApproverChoDuyet)` — `!readOnly` short-circuits to false BEFORE isApproverChoDuyet evaluates** (asymmetric vs F3 :113-118); BE handler `:281-329` correct. BÀI-HỌC: mirror the approverEditMode bypass into the budget gate; ~3-5 LOC 1 file. BẤT-NGỜ: only desktop `?pendingMe=1` 3-panel hard-codes readOnly=true; mobile `/inbox`→DetailPage default false. · q1 · substring:"S23 t2 spawn L2 — F3+F4 edit menu Duyệt audit"
|
||||
- **[cao] Allow* flags invisible to non-row-1 actor — wrong-slot lookup** · VIỆC: admin tick 7 Allow*=TRUE on one specific NV slot, that NV logs in, Duyệt dialog shows no SkipToFinal/Return/Edit. KẾT-LUẬN: HYPOTHESIS B — **BE `PurchaseEvaluationFeatures.cs:765 curLevel = Levels.FirstOrDefault(l => l.Order == curLevelOrder)` picks FIRST row of the Cấp, not the actor's**; post-Mig29 every NV in a Cấp shares the same `Order` (4 rows Order=1). Fix = add `&& l.ApproverUserId == currentUser.UserId` (admin fallback `?? FirstOrDefault(Order==)`); ~2-3 LOC. BÀI-HỌC: Mig29 OR-of-N split (1 row per ApproverUser) silently broke every `FirstOrDefault(Order==)` lookup. BẤT-NGỜ: **bug PRESENT since Mig29 deploy 2026-05-13, NOT an S23 regression** — hidden because default users sat in row-1 with all-FALSE flags so behavior looked identical. · q1 · substring:"S23 t3 spawn — UAT bug Allow* flags không hiện cho actor non-row1"
|
||||
- **[cao] Return-mode + budget changelog gaps** · VIỆC: 2 UAT bugs — Budget-adjust 2×click shows no history; Return-Assignee shows no history. KẾT-LUẬN: Budget handler DOES log (Header/Update) → FE HistoryTab filter (TraLai-only) hides it; **`PurchaseEvaluationWorkflowService.cs:215-378 ApplyReturnModeAsync` has ZERO changelog writes** (4 mode branches mutate pointers only; caller LogTransition logs phase only). Schema `EntityType.Workflow=5` exists but **design-only, unused 4+ migrations**. BÀI-HỌC: fix B2 first (add `Changelogs.Add()` per mode, 1 BE file) then relax FE filter. BẤT-NGỜ: Header.Update collision — budget-adjust vs section-2 edit share the same EntityType/Action (needs a Kind subtype). · q2 · substring:"S25 t1 spawn audit 2 bug critical UAT"
|
||||
|
||||
## Semantic / phase audits (no code bug)
|
||||
|
||||
- **[cao] F1-F4 all preserve Phase=ChoDuyet except F1.Drafter** · VIỆC: bro thought "return/skip/edit forces draft". KẾT-LUẬN: code already correct — `PurchaseEvaluationWorkflowService.cs:268-275` F1.Drafter is the ONLY path setting Phase=TraLai=98; `:285-360` OneLevel/OneStep/Assignee keep ChoDuyet (move pointer only); `:483-524` F2 skipToFinal keeps ChoDuyet (jumps to last step+level, final NV approves → DaDuyet). BÀI-HỌC: this was a mental-model disconnect not a defect; LOW effort = confirm + optional FE label. BẤT-NGỜ: "Trả lại" gets called "draft" colloquially → conflates Phase=TraLai=98 vs Phase=DangSoanThao=1. · q1 · substring:"S23 t2 spawn M0 — Plan M F1+F2+F3+F4 ChoDuyet semantic audit"
|
||||
|
||||
## Pre-flight audits (schema/migration/cleanup)
|
||||
|
||||
- **[cao] Plan K F2 refactor state** · VIỆC: move `Users.AllowDrafterSkipToFinal` → Level slot. KẾT-LUẬN: Mig30 latest; F2 Drafter branch in SUBMIT path `:119-161`, advance branch `~:393-525`; prod 33 users / 4 flagged (Dev only 2). BÀI-HỌC: flagged users lose value on DROP — acceptable under new semantic. BẤT-NGỜ: Dev DB drifted to 2 users, not matching 33-user prod seed. · q1 · substring:"S23 t1 spawn K0 — Plan K F2 refactor pre-flight"
|
||||
- **[cao] Plan R destructive-cleanup pre-flight + DECISION** · VIỆC: wipe prod test PE. KẾT-LUẬN: **PE.ApprovalWorkflowId = Restrict + ApprovalWorkflow extends BaseEntity (NO soft-delete) → hard-DELETE required**; cascade ~620 rows; filtered indexes need `SET QUOTED_IDENTIFIER ON`. **DECISION: bro chose Option A = hard-DELETE PE + V2-unghim + V1-inactive, KEEP V2-ghim + V1-active.** BÀI-HỌC: Plan F precedent — never drop a V1 that still has PE pinned (BE crash). BẤT-NGỜ: SQL Express has no COMPRESSION and RESTORE VERIFYONLY needs sysadmin. · q1 · substring:"S23 t8 spawn Plan R pre-flight cleanup audit"
|
||||
- **[cao] Plan B Contract V2 wire pre-flight** · VIỆC: mirror PE V2 onto Contract. KẾT-LUẬN: **`ApproveV2Async` PurchaseEvaluationWorkflowService.cs:446-634 = 189-LOC clone template** (load AW.Steps.Levels + actor match :484-495 + UPSERT opinion :522-546 + advance :605-633 + skipToFinal F2 :561-602); **Mig22 = 3 CREATE TABLE shared PE+Contract; Mig23/24 = 15-LOC / 3-LOC cookie-cutter rename Pe→Contract**; prod 7 Contracts 100% V1, ApprovalWorkflows ZERO ApplicableType=3. BÀI-HỌC: coexist V1+V2, 6-chunk split. BẤT-NGỜ: ContractType has 7 variants vs one generic ApplicableType=3 → may need per-type filter later. · q2 · substring:"Plan B Contract V2 wire pre-flight audit"
|
||||
- **[vừa] Plan AG PE-list tree-view** · VIỆC: flat list "đám rừng". KẾT-LUẬN: shadcn fe-user MISSING Accordion/Collapsible/Tree → Phase1 native `<details>` group view ~160 LOC ×2 app; Phase2 ProjectPackage schema defer. BÀI-HỌC: hybrid FE-first cookie-cutter. · q2 · substring:"S26 spawn Plan AG 5Q audit"
|
||||
- **[vừa] Plan AA matrix-view + sidebar** · VIỆC: workflow matrix page + widen sidebar. KẾT-LUẬN: `ApprovalWorkflowsV2Controller.cs:16-19` **class-level [Authorize] bare since S18 2026-05-08 (gotcha #44 fixed permanent)**; ApplicableType enum {DuyetNcc=1,DuyetNccPhuongAn=2,Contract=3}. BÀI-HỌC: order-strategy must UPDATE existing Order (not just INSERT-if-absent); wrap commits `da218f1..ee0902a` see Tailwind-JIT record below. · q1 · substring:"S24 t1 spawn Pre-A — Plan AA User Workflow Matrix view"
|
||||
|
||||
## Patterns / gotchas / state
|
||||
|
||||
- **[cao] Identity rename atomic + pwd policy + PS discipline** · VIỆC: seed 20 role-based users on prod. KẾT-LUẬN: **gotcha #38 = rename atomic 4 fields (Email+NormalizedEmail+UserName+NormalizedUserName+FullName) + sqlcmd needs `SET QUOTED_IDENTIFIER ON` for filtered unique index**; **Identity pwd policy ≥12 chars (`User@123456`=11 FAILs 400, `TestUser@2026`=13 passes)**; login response = `accessToken`+`refreshToken`+`user` (no `token`). BÀI-HỌC: **gotcha #30 = PS 5.1 ASCII-only script discipline (Vietnamese names without diacritics or parser error)**. · q1 · substring:"S22, no spawn — em main solo throughout"
|
||||
- **[cao] bug #45 + per-NV scope + EF reorder** · VIỆC: S21 dev log. KẾT-LUẬN: **gotcha #45 bug-fix** + F1/F2/F3 workflow-level Mig28/29; reusable = per-NV permission scope split + EF migration ADD→BACKFILL→DROP reorder. · q1 · substring:"S21 t3-t5, no spawn"
|
||||
- **[cao] Tailwind JIT full-class-strings** · VIỆC: Plan AA color-coded menu hierarchy. KẾT-LUẬN: **Tailwind JIT requires full class strings array — dynamic `bg-${color}-100` gets purged**; STEP/LEVEL 5-palette each; v2 table rowSpan layout. BÀI-HỌC: reusable for hierarchy color coding cross-project; plus hanging-indent reverse-wrap = `inline-block icon + inline text + absolute ChevronDown`. · q1 · substring:"S24 t1-t4 post-spawn, em main solo 4 polish chunks"
|
||||
- **[cao] S40 state grounding** · VIỆC: re-ground 7 metrics. KẾT-LUẬN: mig=40 / gotcha=55 / git clean / **SQL tables=84 = count `.ToTable()` in ModelSnapshot NOT DbSet (DbSet=77 undercounts by 7 Identity)** / endpoints=211 / FE 65 pages / menu 53 keys. BÀI-HỌC: tables must be counted via ToTable, never DbSet. · q4 · substring:"S40 STATE GROUNDING"
|
||||
- **[vừa] BVAAU 7-agent port** · VIỆC: split roster 4→7. KẾT-LUẬN: research(2)/implement(2)/quality(3); boundary repo-interface=domain, EF-config=infra, test=specialist; all 7 get 5 RAG MCP. BÀI-HỌC: BVAAU template aspirational (Phase-0 empty codebase), not battle-tested; SE keeps 6 skill + backend/frontend split. · q4 · substring:"S39 BVAAU 7-agent extract"
|
||||
- **[vừa] P11-A WorkflowApps pre-flight** · VIỆC: wire Leave/OT/Travel/Vehicle. KẾT-LUẬN: schema pinned Mig39 (ApprovalWorkflowId?+CurrentApprovalLevelOrder?+WorkflowAppStatus); Proposal = perfect mirror (`ProposalFeatures.cs:403-486` flatten Steps→Levels). BÀI-HỌC: gap = 4 LevelOpinion tables + 3 controllers + 4 seed WF. BẤT-NGỜ: **`ApprovalWorkflow.cs:72` says Level is NOT OR-of-N (1 ApproverUserId/Level) — contradicts older "OR-of-N" memory, verify**; `ExtendApplicableTypeForWorkflowApps` mig has empty Up/Down (enum-only). · q4 · substring:"P11-A WorkflowApps wire pre-flight"
|
||||
|
||||
## RAG research
|
||||
|
||||
- **[vừa] RAG distribution research** · VIỆC: setup RAG for 5 colocated projects. KẾT-LUẬN: 4 study cases (Cursor Merkle+Turbopuffer / Cline markdown JIT / Continue.dev hub / Sourcegraph Cody dropped-embeddings); winner for single-dev = Pattern C user-global MCP (1 localhost server serves 5). BÀI-HỌC: **Voyage AI 200M tokens/month free covers 5 devs** → only $15 VPS. · q2 · substring:"S26 spawn Plan AI RAG distribution research"
|
||||
|
||||
## [meta] bookkeeping (low signal)
|
||||
|
||||
- **[thấp] spawn-attribution FLAG** · The S20-S26 memory entries claiming "spawn Investigator" may be misattributed `general-purpose` (the `investigator` agent type was NOT found / registry not loaded in S27); trust-but-flag for future audit. · q3 · substring:"S27 wrap-up retrospective em main proxy"
|
||||
- **[thấp] gotcha #48 SQLite tie-break** · Plan AB wrap: CICD runs #215 FAIL on Plan-M tests SQLite tie-break re-emerge → #216-#221 PASS; **gotcha #48 SQLite tie-break pending docs**. · q3 · substring:"S25 wrap Plan AB Bug 1+2 audit"
|
||||
- **[thấp] curate note (S29-era)** · archived 10 verbose S21→S24 entries to q1; KEEP-list recorded. · q3 · substring:"Curate session em main S29 era"
|
||||
- **[thấp] agent setup baseline** · init load 44 gotcha / 27 mig / 81 test, no investigation yet. · q1 · substring:"2026-05-11 (setup)"
|
||||
- **[thấp] agent setup baseline (re-archived dup)** · same baseline note, kept in q3 by S34 curate. · q3 · substring:"2026-05-11 — Setup baseline"
|
||||
@ -0,0 +1,47 @@
|
||||
# Gist 2026-06 — investigator-codebase (2026-06.md distilled)
|
||||
|
||||
> `distill-gen: 2` (already-distilled — do NOT re-compress).
|
||||
> 4-field per record: **VIỆC** · **KẾT-LUẬN** (+file:line) · **BÀI-HỌC** · **BẤT-NGỜ**. Same-topic merged. Confidence cao/vừa/thấp.
|
||||
> Each line ends with a back-resolve `substring:"…"` (unique Ctrl-F) into `2026-06.md`. Covers all 18 records (3 S57bis-era + 12 moved by Harness-9 curate 2026-06-17 + 3 S65-series moved by S71 curate 2026-06-18).
|
||||
> 22-marker coverage gate satisfied across this + 2026-05.gist.md.
|
||||
|
||||
---
|
||||
|
||||
## gotcha #57 family — soft-delete + bare unique index (reachable 500)
|
||||
|
||||
- **[cao] gotcha #57 EXTENSION reachability audit** · VIỆC: 6 candidates for soft-delete + bare `.IsUnique()` on Code → recreate-after-delete throws DbUpdateException **500**. KẾT-LUẬN: **FIX 3 Master** = Department/Supplier/Project (`Department/Supplier/ProjectConfiguration.cs:18/24/19` bare unique), all CONFIRMED-reachable (`DepartmentFeatures.cs:76+125`, `ProjectFeatures.cs:87+147`, `CreateSupplierCommand.cs:45`+`DeleteSupplierCommand.cs:20`). **SKIP 3** = ContractClause (no Create/Update/Delete handler anywhere — not CRUD-reachable), MeetingRoom (Delete sets `IsActive=false` NOT IsDeleted, `MeetingFeatures.cs:178`; Create also `&& !IsDeleted`), EmployeeProfile (Create BLOCKS reuse by design — UserId check sees soft-deleted → throws "Cần khôi phục" :160-163; EmployeeCode auto-gen atomic). Mig 46 = exactly 3 indexes. BÀI-HỌC: every OTHER bare-unique is safe (composite junction, nullable-code already filtered, or no-soft-delete). BẤT-NGỜ: **Master's GLOBAL `HasQueryFilter(!IsDeleted)` MAKES the bug — auto-hides soft-deleted from the Create dup-check so it passes, then the unfiltered index throws 500 — opposite of HRM where the bug needs a manual `!IsDeleted`**; either way the unfiltered index is the fault. · 2026-06.md · substring:"S51 gotcha #57 EXTENSION reachability audit"
|
||||
- **[cao] add-kind 11-spot stack + bare-unique confirm** · VIỆC: add a HrmConfigs kind (Vehicle/Driver). KẾT-LUẬN: **HrmConfigs has NO kind-enum/registry backend — 4 separate entities (LeaveType/Holiday/ShiftPattern/OtPolicy), "kind" is FE-only union+route param**; adding 1 kind = mirror full entity stack across **11 spots** (Domain/Configuration/DbContext×2/Features-region/Controller-4-routes/DbInitializer/MenuKeys+All/Page-KIND_CONFIG/App-route/menuKeys+staticMap). **gotcha #57 CONFIRMED still bare: `LeaveTypeConfiguration.cs:19` + `ShiftPatternConfiguration.cs:19` + `OtPolicyConfiguration.cs:22` `.IsUnique()` lack `.HasFilter("[IsDeleted]=0")` — only `HolidayConfiguration.cs:18` fixed (Mig43)** → Vehicle/Driver Code UNIQUE must add the filter from day one. BÀI-HỌC: each kind = its own table (NOT discriminated) → Mig 44 must CREATE TABLE. · 2026-06.md · substring:"S50 P11-C Vehicle+Driver — HrmConfigs add-kind pattern VERIFIED"
|
||||
- **[vừa] P11-C Vehicle/Driver catalog pre-flight** · VIỆC: where to home the catalog. KẾT-LUẬN: extend HrmConfigs (NOT new module) — add Region 5/6 (kind vehicles/drivers); FE = +2 KIND_CONFIG entries; VehicleBooking stays free-text (`Office/VehicleBooking.cs:13-19`, no VehicleId/DriverId FK). BÀI-HỌC: **HRM entities have NO global HasQueryFilter → manual `.Where(!IsDeleted)` + UNIQUE soft-delete needs `.HasFilter("[IsDeleted]=0")` (Holiday Mig43 lesson)**. · 2026-06.md · substring:"P11-C Vehicle+Driver catalog pre-flight"
|
||||
|
||||
## Permission / authz / seed model
|
||||
|
||||
- **[cao] BE authz split — Master write-open** · VIỆC: assess making modules visible to all roles (blocks A/B/E/F). KẾT-LUẬN: config controllers gate WRITE behind `[Authorize(Roles="Admin")]`, READ open (HrmConfigs/Catalogs/MeetingRooms) → FE-grant is pure UI-visibility there. **BUT Master 3 controllers = class `[Authorize]` ONLY, no per-action role: `SuppliersController.cs:17`, `ProjectsController.cs:11`, `DepartmentsController.cs:11` — ANY authed user can POST/PUT/DELETE via API** (FE menu is the only gate). **S55 prod data lives in `SeedRealMasterDataAsync :2267-2460` → Projects(62 :2270), WorkItems(71 :2430-2438), Suppliers(3 :2440-2456), all ungated idempotent.** BÀI-HỌC: making Suppliers/Projects/Departments visible-to-all needs `[Authorize(Roles="Admin,CatalogManager")]` or the staff can overwrite S55 master data. 10 departments now (9 + IT); 31 demo users, none zero-role. · 2026-06.md · substring:"S57 perm-broaden blocks A/B/E/F"
|
||||
- **[cao] seed model — no per-employee default Read** · VIỆC: blocks C/D recon. KẾT-LUẬN: `SeedAdminPermissionsAsync DbInitializer.cs:1939-1977` loops MenuKeys.All CRUD=true (skip-existing); 2 sub-seeders give 7 roles Read+Update on PE keys and CatalogManager full-CRUD 9 master keys. **NO generic per-employee Read seed → a plain Drafter sees ONLY PE keys; most non-admin staff see ~nothing but PE.** 4 inherit-roots (Contracts/Workflows/PurchaseEvaluations/PeWorkflows) cascade root→child; Hrm/Off/Master NOT inherit (each leaf needs own row). BÀI-HỌC: grant-all pattern = mirror CatalogManager seeder but loop ALL 13 roles × key-set, CanRead-only, inserted AFTER the catalog seeder. · 2026-06.md · substring:"S57 perm-broaden RECON blocks C/D"
|
||||
- **[cao] menu seed = UPSERT that re-sets Order** · VIỆC: menu reorder cross-repo SE↔NAMGROUP. KẾT-LUẬN: SE menu = `SeedMenusAsync DbInitializer.cs` tuple-list; **seed UPSERT re-sets Order (`:1845-1871`) → reordering in code propagates to Dev/prod next deploy, NO migration; but Label/ParentKey/Icon are NOT touched on existing rows → rename needs a separate labelBackfill dict**. Order = BE-only (`GetMyMenuTreeQuery.cs:35 OrderBy`); both FE render menu as-is → no FE edit for pure reorder. BÀI-HỌC: **HR is SCATTERED across 2 roots** — `Hrm` (Hồ sơ+Config+Dashboard) and transactional HR under `Off` (DonTu/DatXe/ChamCong/AttendanceReport). BẤT-NGỜ: NAMGROUP "Puro" = hardcoded FE array (NOT DB seed), index=order, flat single links vs SE deep-nested. · 2026-06.md · substring:"menu-order cross-repo recon SE↔NAMGROUP"
|
||||
- **[cao] public-HRM for all-role — seed-only** · VIỆC: open HRM module (Hồ sơ/Dashboard) to every role. KẾT-LUẬN: **`EmployeesController.cs:23-25` is `[Authorize(Policy="Hrm_HoSo.Read")]` (NOT `Roles="Admin"`) — policy resolves THROUGH the permission matrix (`MenuPermissionHandler.cs:40-52`), so seeding a CanRead row also unlocks the API, NO 403** → seed BE permission is enough, no controller edit; FE auto-renders (menu-tree API-driven, `Hrm` NOT in `USER_HIDDEN_KEYS`, root auto-shows if a child has access). **`Hrm_HoSo`+`Hrm_Dashboard` ARE in `MenuKeys.All` (unlike Pe_* leaves); 13 roles in `AppRoles.All`.** BÀI-HỌC: Pe-style grant-all = `SeedAllRolesReviewReadPermissionsAsync :2055` loops all roles × keys, upsert CanRead, idempotent. BẤT-NGỜ: **`RevokeTemporarilyHiddenModulesAsync` is called LAST `:2040` in SeedAsync (after the grant `:2033`) and wipes CRUD on every `StartsWith("Hrm")||"Off"||==Personal` non-Admin row → it BEATS any earlier grant; opening Hrm needs either excluding Hrm_HoSo/Dashboard from the revoke OR granting AFTER :2040.** · 2026-06.md · substring:"S65 recon — public HRM module for all-role"
|
||||
|
||||
## Prod facts / census / wipe
|
||||
|
||||
- **[cao] prod test-data wipe + FK + PE tree** · VIỆC: wipe prod test PE + retree by Hạng mục. KẾT-LUẬN: prod PE=10 active (MaPhieu A/031-040, all WorkItemId NULL) + Contracts=7 all `[DEMO]` V1; **FK: PE children CASCADE except `Quotes→PE NO_ACTION` (multi-path) — Plan R proved a single `DELETE FROM PurchaseEvaluations` works (NO_ACTION checked end-of-statement after Details→Quotes cascade)**; PE.ApprovalWorkflowId Restrict → wipe PE before deleting AW. Demo gate OK (SeedDemoContracts/PE inside `DemoSeed:Disabled`). FE tree = `pe/PurchaseEvaluationsListPage.tsx:138-179` Project>Year>Supplier, PeListItem already has workItemId/Name → **FE-only change**. BÀI-HỌC: ~10 orphan upload folders from S23 (files not deleted on wipe). BẤT-NGỜ: 20 REAL users batched 2026-06-11 06:01 (S58 seed-fix landed; `thanh.lethanh` now exists, correcting stale S57bis memory). · 2026-06.md · substring:"S59 recon — prod test-data wipe + PE tree"
|
||||
- **[cao] prod user census + pwd-policy env divergence** · VIỆC: why is the demo-user lock a no-op. KẾT-LUẬN: **`LockDemoSampleUsersAsync DbInitializer.cs:1552` hardcodes 14 named-person emails = a population that exists ONLY on Dev**; prod has 34 all-active users (20 UAT-matrix placeholders hand-created 2026-05-13, 9 real staff, `binh.lethanh`, a typo-domain `chuong.phan@solution.com.vn` dup, admin/catalog.manager/nv.test). **ROOT CAUSE seed-users-never-on-prod = prod `Identity:Password:RequiredLength=12` vs `DemoUserPassword="User@123456"`=11 chars → CreateAsync silent-fails every prod startup since 04-21; Dev fallback length 8 → Dev gets the 33 named users.** BÀI-HỌC: fix needs 20 real prod emails; keep nv.test (breaking it breaks CI smoke). BẤT-NGỜ: `bod.1@` never appears in git pickaxe → created by hand via admin UI, not seed. · 2026-06.md · substring:"S57bis lock no-op — prod user census"
|
||||
- **[cao] PE entity recon — 4 đầu việc** · VIỆC: PE Year/WorkItem/create-UI/perm. KẾT-LUẬN: PE has NO Year, NO WorkItem link (free-text detail); MaPhieu gen-AT-CREATE `PurchaseEvaluationCodeGenerator.cs:23` format `PE/{YYYY}/{A|B}/{Seq:D3}`; PE controller class-`[Authorize]` only (no policy → opening the menu is enough, no silent-403); Pe_* leaves NOT in `MenuKeys.All`. BÀI-HỌC: **extending InReviewScope must match `key == MenuKeys.PurchaseEvaluations` EXACT — the prefix "Pe" would also catch PeWorkflows (admin)**; root inherit cascades. BẤT-NGỜ: WorkItem write is Admin-only (`CatalogsController:113-130`) so CatalogManager has the menu but the API write is blocked. · 2026-06.md · substring:"S57bis PE recon — 4 đầu việc sếp"
|
||||
|
||||
## Pre-golive verify / FE-redesign / master-import
|
||||
|
||||
- **[cao] pre-golive logic verify — 4 streams PASS** · VIỆC: audit P11-B/D/E/F + ApproveV2 + catalogs + S55 wiring. KẾT-LUẬN: LeaveBalance deduction exactly-once (terminal DaDuyet, guard `Status!=DaGuiDuyet :296`); AttendanceReport classifies day-type in-memory; MaTicket gen-on-Create Serializable; ApproveV2 4-module flatten correct; master-data idempotency PROVEN (re-run → identical counts). BÀI-HỌC: **Travel/Vehicle ApproveV2 = 0 tests (cookie-cutter of tested Leave/OT) — add 2 smoke post-golive.** BẤT-NGỜ: **dept "IT"/Phòng CNTT DOES exist on prod (Id 65CC6307…) but has 0 active users → ItTicket auto-assign no-ops, SLA job no target** (corrects stale S52 "no IT dept"); ops fix = assign ≥1 user to dept IT (1 UPDATE). · 2026-06.md · substring:"S56 pre-golive verify — 4 logic streams"
|
||||
- **[vừa] FE-redesign Phase-2 recon** · VIỆC: 25-page redesign audit. KẾT-LUẬN: NOT a rewrite — S55 already redesigned ui-primitives+DataTable+shell so importers auto-inherit density; hover-hidden quick-win ~absent (only 1 real `opacity-0`×group-hover site, excluded); only 5/25 use `<DataTable>` (12 roll raw `<table>`); 3 Drawer candidates (Suppliers/Projects/Users-create); **no `Drawer.tsx` exists yet**; bậc-thang inline-edit reference already exists (`EmployeesListPage.tsx`). BÀI-HỌC: effort mostly S (auto-inherit) + a few M (Drawer/bậc-thang). · 2026-06.md · substring:"S56 Phase 2 FE-redesign RECON — 25 page audit"
|
||||
- **[cao] master-data Excel-import recon** · VIỆC: import 3 masters from Excel. KẾT-LUẬN: **WorkItem/"Hạng mục" master EXISTS** (`Domain/Master/Catalogs/WorkItem.cs`, full CRUD) — no new table needed; PE detail is pure free-text (no FK→WorkItem); **Project MISSING Year/Investor/Location/Package (only Note free-text)**; Supplier MISSING Status/bank-acct/legal-rep + "Cả hai" unmappable (Type single-valued); seed = idempotent `existingCodes.Contains→skip`; **no bulk import (Master is single-CRUD, POST one-at-a-time)**. BÀI-HỌC: nạp via idempotent DbInitializer mirror (NOT API loop) → reaches prod by design. BẤT-NGỜ: SeedDemoMasterData + SeedCatalogs run REGARDLESS of `DemoSeed:Disabled` (outside the if-block) → real+demo data mix on prod unless gated. · 2026-06.md · substring:"S55 master-data Excel-import recon"
|
||||
|
||||
## FE mirror / UI-insert recon
|
||||
|
||||
- **[cao] mirror Hồ sơ-NS fe-user→fe-admin = patch CSS first** · VIỆC: replicate the redesigned employee page from fe-user into fe-admin. KẾT-LUẬN: **VERDICT B — a plain page-copy BREAKS COLORS; must patch `fe-admin/src/index.css` FIRST then cookie-cutter.** fe-admin index.css = 86 lines (pinned `7feb53e`, pre-S58 redesign) → **missing 4 accent palettes (teal/amberx/violet/greenx, each 50/100/500/600/700) + utilities `.icon-chip`/`.app-gradient-brand`/`.card-accent`/`.stat-value`**; brand-50..900 hex already present (incl brand-800 #175685). The fe-user page really depends on text-brand-800 ×9, the 4 accents ×4 each, icon-chip ×3, app-gradient-brand ×1. **Wiring already complete in fe-admin (0 changes): route, identical `EmployeeCreatePage.tsx`, menu `Hrm_HoSo`+staticMap; ui-primitives/types/api all parity.** Scope = 3 files (index.css insert ~40 lines + overwrite EmployeesListPage.tsx + optional heading-700). BÀI-HỌC: mirror exactly like S35 (both apps committed together); write stays Admin-gated at BE. · 2026-06.md · substring:"S66 recon — mirror Hồ sơ NS fe-user"
|
||||
- **[cao] PE Section-E "Link hồ sơ" insert point** · VIỆC: add mục E "Link hồ sơ" right under mục D "Bản so sánh" in the PE form. KẾT-LUẬN: render in 4 files (SHA256-identical 2 apps): `components/pe/PeDetailTabs.tsx` + `PeWorkspaceCreateView.tsx` × {fe-user,fe-admin}; NOT tabs — 5 vertical `<Section>`. **Mục D lives in `ChonNccSection` (`PeDetailTabs.tsx:1302-1375`), d.Bản so sánh at :1337-1348 = `GeneralAttachmentsSection` upload filtered `supplierId===null` purpose=ComparisonTable. INSERT E at `PeDetailTabs.tsx:1348` (after mục D's `</div>`, before paymentTerms :1350); create-view at `PeWorkspaceCreateView.tsx:277`.** **BE `PurchaseEvaluation.cs` has NO URL field — 1 link = add `string? HoSoLink`(1000)+Mig+cmd+DTO+validator; many links = child entity (heavy).** BÀI-HỌC: attachments are IFormFile-only (`PurchaseEvaluationAttachmentFeatures.cs:18-55`) — cannot reuse for a URL. BẤT-NGỜ: comment :1314 claims "purpose=ComparisonTable OR supplier-row null" but the real filter :1315-17 is ONLY `supplierId===null`. · 2026-06.md · substring:"S65ter recon — Mục E"
|
||||
|
||||
## Governance / wave / harness
|
||||
|
||||
- **[vừa] Harness 1/2/3 adap-apply recon** · VIỆC: apply AI_INFRA broadcast. KẾT-LUẬN: roster 8→10 (+tooling-auditor H1, +harvest-curator H2); SE `hmw.js` is pre-wave (AI_INFRA = canonical); email id authoritative = `se`; `git check-ignore -v` = ground-truth B6 (wave patterns must sit AFTER `!.claude/**` last-match-wins). BÀI-HỌC: git-diff + chunk-count = defense-in-depth (caught 1 self-MEMORY write, 0 RAG-write). · 2026-06.md · substring:"Harness 1/2/3 adap-apply recon — 3 slice"
|
||||
- **[cao] wave h2-verify — B6 isolation + Bash surprise** · VIỆC: audit wave write-isolation. KẾT-LUẬN: B6 = 2 rules — transient `wave-*/`+`agent-teams/` gitignored (audit-noise=0) AND canonical `agent-memory/**/MEMORY.md` TRACKED (rogue writes surface in git status); all 10 MEMORY.md tracked. BÀI-HỌC: ordering gotcha — wave/team patterns MUST sit after `!.claude/**` or it un-ignores everything. BẤT-NGỜ: **Bash tool = `/usr/bin/bash` NOT PowerShell despite env=PowerShell → `Get-ChildItem`/`Select-String`/`Test-Path` fail (exit 2/127); read-only Bash-only subs MUST use POSIX.** · 2026-06.md · substring:"S50 wave `h2-verify` — B6 guardrail audit"
|
||||
|
||||
## Monthly drift audit
|
||||
|
||||
- **[vừa] 2026-06 monthly drift audit** · VIỆC: ground-truth counts vs docs. KẾT-LUẬN: migrations=42 (last AddLeaveBalances) / gotchas highest=#56 (file header has no self-count) / tests=154 / tables≈91; biggest drift = ef-core-migration SKILL (says 31 mig / 59 bảng / 111 test). BÀI-HỌC: schema-diagram migration table stops at Mig 16 → missing §§ for Mig 27-42. BẤT-NGỜ: STATUS backlog "Curate 4 agent MEMORY 35.7/…" is STALE (already curated S40) → remove. · 2026-06.md · substring:"MONTHLY DRIFT AUDIT"
|
||||
@ -8,3 +8,34 @@
|
||||
|
||||
|
||||
- **2026-06-08 (S51 gotcha #57 EXTENSION reachability audit — 6 candidate, RAG down, on-disk only):** ⭐ Bug class = soft-delete + bare `.IsUnique()` on Code → recreate-after-delete throws DbUpdateException 500. Verdict 6 cand: **FIX 3 (Master)** Department/Supplier/Project (`Department/Supplier/ProjectConfiguration.cs:18/24/19` bare unique). ALL = AuditableEntity + **GLOBAL `HasQueryFilter(!IsDeleted)`** + Delete via `.Remove()` → `AuditingInterceptor.cs` (State Deleted→Modified, IsDeleted=true) + Create `AnyAsync(x=>x.Code==req.Code)` NO `!IsDeleted` BUT global filter auto-hides soft-deleted → check passes → unfiltered index 500. **CONFIRMED-reachable** (`DepartmentFeatures.cs:76+125`, `ProjectFeatures.cs:87+147`, `CreateSupplierCommand.cs:45`+`DeleteSupplierCommand.cs:20`). **SKIP 3:** (a) **ContractClause** (`ContractClauseConfiguration.cs:18`) — NO Create/Update/Delete handler ANYWHERE (only `IApplicationDbContext.cs:32` DbSet; FormsController = templates only) → not CRUD-reachable. (b) **MeetingRoom** (`MeetingRoomConfiguration.cs:20`) — Delete sets `IsActive=false` NOT IsDeleted (`MeetingFeatures.cs:178`, comment :175 "FK Restrict → NOT soft delete") → index never gets soft-deleted row; Create also checks `&& !IsDeleted` :113. (c) **EmployeeProfile** (`EmployeeProfileConfiguration.cs:24/26` EmployeeCode+UserId) — Delete soft (`EmployeeFeatures.cs:437`) BUT Create BLOCKS reuse by design: UserId check `AsNoTracking().FirstOrDefault(UserId==)` (no HRM global filter) sees soft-deleted → throws ConflictException "Cần khôi phục" :160-163; EmployeeCode auto-gen atomic (never user-supplied/reused) → no collision. **Completeness (grep ALL `.IsUnique()`):** beyond 3 Master + 6 HRM-fixed (LeaveType/Holiday/Shift/OtPolicy/Vehicle/Driver all `.HasFilter([IsDeleted]=0)`), every OTHER bare-unique is either composite junction (Permission RoleId+MenuKey, *LevelOpinion, MeetingBookingAttendee, LeaveBalance, Attendance UserId+Date), nullable-code already filtered (`[Ma*] IS NOT NULL`: Contract/PE/Proposal/Budget/WorkflowApps), or no-soft-delete (WorkflowDefinition/ApprovalWorkflow Code+Version, ContractTemplate FormCode, WorkflowTypeAssignment, DepartmentApprovals). **Mig 46 = exactly 3 indexes (Departments/Suppliers/Projects Code).** Surprise: Master GLOBAL query filter MAKES the bug (auto-hides soft-deleted from check) — opposite of HRM where bug needs manual `!IsDeleted`; either way unfiltered index = 500. Tag `[gotcha57-ext, reachability-audit, master-global-filter, s51]`.
|
||||
|
||||
- **2026-06-01 (MONTHLY DRIFT AUDIT):** Ground truth code: **migrations=42** (last `AddLeaveBalances`, path `.../Persistence/Migrations/*.cs`) · **gotchas highest=#56** (file header NO self-count → drift chỉ ở file *reference* gotchas.md) · tests=154 (58 Domain+96 Infra, em main verified) · tables≈91 (45 config class nhưng Catalogs=4+ContractDetails=7+7 Identity untracked → khớp STATUS 91, KHÔNG cheap-exact). **Biggest drift: ef-core-migration SKILL** (frontmatter:3 "31 migration"→42, :19 history "31"→42 + thiếu rows Mig 27-42, :50 "59 bảng"→91, :80 "111 test"→154, :258/:267 "59 bảng"). dependency-audit SKILL:153 "49 bẫy"→56. CLAUDE.md:53 "40 mig→84 bảng"→42/91, :66 "130 test"→154, :133 "52 bẫy"→56. docs/CLAUDE.md:65 "52"→56. **schema-diagram GAP:** migration TABLE dừng Mig 16 (line 487); detail § cuối =§15=Mig 26 (§16=Related KHÔNG phải mig) → thiếu § cho **Mig 27-42** (16 mig). database-guide:4 "47 bảng/13 mig"→91/42. STATUS:97 backlog "Curate 4 agent MEMORY 35.7/35.3/30.9/28.4" STALE (đã curate S40 `78c9de3`, all ≤16KB) → REMOVE. NO-CHANGE: contract-workflow (historical counts OK), form-engine, iis-deploy (no count), HANDOFF (S43 current), PROJECT-MAP (no count). Tag `[drift-audit, monthly, 2026-06]`.
|
||||
|
||||
- **2026-06-01 (P11-C Vehicle+Driver catalog pre-flight):** Mig 44 next (latest=Mig 43 `FilterHolidayUniqueIndexByIsDeleted` S45). **NO Vehicle/Driver master exists** — chỉ `Office/VehicleBooking.cs` (request, Mig 39) dùng FREE-TEXT (`VehicleLicense`/`VehicleName`/`DriverName?` strings, :13-19 comment "defer catalog Phase 11"). **RECOMMEND home = extend HrmConfigs** (NOT new module): `Application/Hrm/HrmConfigFeatures.cs` mega 4-region + `HrmConfigsController` (`[Authorize]` read / `[Authorize(Roles="Admin")]` write) — add Region 5 Vehicle + 6 Driver (kind `vehicles`/`drivers`), pattern proven 12-bis. ⚠️ HRM entities KHÔNG global HasQueryFilter → manual `.Where(!IsDeleted)` + UNIQUE soft-delete cần `.HasFilter("[IsDeleted]=0")` (Holiday Mig 43 lesson, LeaveType/Shift UNIQUE Code chưa có filter → nếu Vehicle BienSo UNIQUE phải add filter). **FE cheap:** `HrmConfigsPage.tsx` declarative KIND_CONFIG Record — add 2 entry vào KIND_CONFIG + KINDS[] + `renderCells` branch + smart-defaults; NO new page. **Menu+perm:** add 6 const `MenuKeys.cs` (+`Hrm_Config_Vehicles/Drivers`), thêm vào `All[]` (:140) → Admin auto-grant qua `SeedAdminPermissionsAsync` loop (:1909 idempotent), +2 MenuItem `DbInitializer` :1757, +2 `menuKeys.ts` mirror. Hrm_Config KHÔNG inherit-root (4 root=Contracts/Workflows/Pe/PeWf only) → leaf cần row riêng (loop lo). **Fields (NamGroup XeCong DROPPED Mig 2026-05-15, ref response shape only):** Vehicle{Code/BienSo UNIQUE, Hang, MauXe, SoCho int, TrangThai, GhiChu}; Driver{Code/Hoten, SDT, GPLX, Hang bằng, TrangThai}. FK link defer: P11-C = catalog only, optional FK `VehicleBooking.VehicleId?/DriverId?` giữ free-text back-compat (Mig sau). Tag `[pre-flight, p11-c, vehicle-driver-catalog]`.
|
||||
|
||||
- **2026-06-07 (Harness 1/2/3 adap-apply recon — 3 slice, HMW wave):** Governance recon AI_INFRA broadcast harness-1/2/3. **H1/H2 (Harness 1):** roster 8→10 — CREATE 2 sub TÁCH BIỆT `tooling-auditor` (H1 freshness 4-mặt skill/sub-role/plugin/docs) + `harvest-curator` (H2 integrity 5-trục). H2 PARTIAL sẵn: `session-end.md` Phase 1.5 §L.b(d) spawn-record 4-field + (f) double-check moved-not-cut + (c) 0-byte AS-8 = Coverage+Completeness+Corruption (3/5); THIẾU Fidelity-escalate + Placement. RE-REPORT @session-start = 0 (chỉ generic Phase 2.7). 2 sub mirror inv-codebase read-set + store_memory strip + NO Write/Edit; color brown+teal (8 màu cũ hết). **H2 wave (Harness 2):** SE `hmw.js` = OLD pre-wave (no subMdPath/writeGuard/wave-block); AI_INFRA `hmw.js` = canonical template. ⭐ `git check-ignore -v` = ground-truth B6: `.claude/workflows/wave-test/wave.md` HIỆN match `.gitignore:83 !.claude/**` = TRACKED → wave pattern PHẢI đặt AFTER `!.claude/**` (last-match-wins, mẫu `hmw-mode.on` :87). Read-only sub (4)=inv-cb/inv-api/reviewer/cicd; Write sub (4)=impl×2/test/fe-designer. B5 depends H2 harvest-curator. **H3 email (Harness 3):** broadcasts/ absent; id authoritative = `se` (NOT solution_erp), 6 others short `{ai_infra,vipix,dyd,namgroup,ashico,bvaau}` từ `AI_INFRA/broadcasts/sister-commands/send-email.md:13-22` (folder name = 2nd source-truth); `adap-apply.md:14` base-path STALE flat → `outbox/all/*.md` (latent bug). broadcasts/ ở root → commit OK (no gitignore rule). **Containment post-P2:** git-diff bắt 1 file-write (inv-api self-MEMORY), chunk-count 2414=2414 (0 RAG-write) = defense-in-depth proven. Tag [harness-recon, governance, hmw-wave, 2026-06-07].
|
||||
|
||||
- **2026-06-09 (S56 pre-golive verify — 4 logic streams, all PASS):** Audited P11-B/D/E/F + ApproveV2 + catalogs + S55 master-wiring. **LeaveBalance** deduction exactly-once (terminal DaDuyet, guard `Status!=DaGuiDuyet` :296 blocks re-approve), FK guard Create+UpdateDraft→Conflict; **AttendanceReport** classify day-type IN-MEMORY (Holiday DateOnly HashSet), OtPolicy multiplier; **MaTicket** gen-on-Create Serializable IT/2026/NNN. Tests cover (LeaveBalance 9 + AttReport 2 + codegen 3 = 29 green). ApproveV2 4-module flatten Steps→Levels correct; **Travel/Vehicle ApproveV2 = 0 test** (cookie-cutter of tested Leave/OT — add 2 smoke post-golive). master-data **idempotency PROVEN** (DbInitializer re-run → counts identical, per-code guard :2310/:2404/:2422). **⚠️ PROD FACT (corrects stale S52 mem):** dept "IT"/Phòng CNTT DOES exist (Id 65CC6307…) but has **0 active users** on prod → ItTicket auto-assign no-ops, reassign dropdown empty, SLA job no notify-target. Pre-golive ops fix: assign ≥1 real user to dept IT (1 UPDATE, no code). Tag [s56, pre-golive-verify, logic-pass, dept-IT-empty-prod, travel-vehicle-untested].
|
||||
|
||||
- **2026-06-09 (S56 Phase 2 FE-redesign RECON — 25 page audit, on-disk):** ⭐ **NOT a rewrite** — S55 already redesigned ui-primitives + DataTable + shell → any page importing `ui/{Button,Input}`+`DataTable` AUTO-inherits density. **Hover-hidden quick-win nearly absent:** repo-wide grep `opacity-0`×`group-hover` = **only 1 real site** `ContractCreatePage.tsx:196` (EXCLUDED scope) + DataTable.tsx:15 is a comment forbidding it (good). In-scope = **0 hover-hidden fixes**. **DataTable adoption split:** only 5/25 use `<DataTable>` (Suppliers/Projects/Departments/Users/Forms); 12 pages roll RAW `<table>` (MeetingRooms/Catalogs/HrmConfigs/EmployeesList/Proposals/WorkflowApps/Attendance×2/MenuVisibility/Roles) → custom density pass needed. **Drawer ≥8-field candidates = 3:** Suppliers (9 fld, Dialog), Projects (10 fld, Dialog), Users-CREATE (~8 fld w/ roles multiselect, 4 Dialogs total but only create is big). All currently big-Dialog → convert. **NO `Drawer.tsx` exists** (`ui/` = Button/Dialog/Input/Label/Select/Textarea only) → build first. **Bậc-thang reference ALREADY EXISTS:** `EmployeesListPage.tsx` (1200L) = canonical inline add/edit-row for 5 satellites (`setEditing{X}Id` + `addingX` mutex, :256-356) → extract `InlineEditRow` pattern from here, reuse for Catalogs/HrmConfigs/MeetingRooms (these 3 currently edit via Dialog :251/:316/:232, ≤7 cols → bậc-thang candidates). **Modal-detail = NONE:** ProposalDetail:275 + WorkflowAppDetail:424 Dialogs are tiny action-confirm (just `<Textarea>` ý kiến), detail body already inline 2-col grid (:181) → NOT convert. **fe-user mirrors** office/master/hrm (SHA256-identical per comments) but NO system/forms/reports mirror. **Custom-layout heavy:** InternalDirectory (card-grid :124 not table), MeetingCalendar (693L FullCalendar), EmployeesList (2-panel), HrmConfigs (declarative KIND_CONFIG :45). Effort: Master 3×Drawer=M, Catalogs/HrmConfigs/MeetingRooms bậc-thang=M, Users Drawer=M, rest S (auto-inherit polish). Surprise: hover-hidden NAMGROUP-win essentially pre-solved (team already avoids opacity-0 pattern, DataTable comment enforces) → quick-win section nearly empty. Tag `[fe-redesign-p2, recon, drawer-3, basc-thang-ref-exists, s56]`.
|
||||
|
||||
- **2026-06-09 (S55 master-data Excel-import recon — 3 master + seed mechanism, on-disk):** ⭐ **"Hạng mục"/WorkItem master TỒN TẠI** — `Domain/Master/Catalogs/WorkItem.cs:6-14` (Code(50)UNIQUE-filtered/Name(200)/Category(100,idx)/DefaultUnit(50)/Description/IsActive), config `CatalogsConfiguration.cs:60-74`, full CRUD `CatalogsFeatures.cs:260-324` → group(VẬT TƯ/THẦU PHỤ/MEP)→Category, "1 Mat"→Code, item→Name. KHÔNG cần table/migration mới. **PE detail = pure free-text** (`PurchaseEvaluationDetail.cs` GroupCode/GroupName/ItemCode/NoiDung strings, NO FK→WorkItem) → load WorkItems non-breaking. **Project** (`Project.cs:5-14`, cfg `:14-21`): Code(50,UNIQUE `[IsDeleted]=0` Mig47)+Name(200) REQUIRED, StartDate/EndDate/BudgetTotal(18,2)/Note(1000)/ManagerUserId optional. ❌ **THIẾU Year/Investor/Location/Package** — chỉ Note free-text catch-all. Create cmd `ProjectFeatures.cs:67` dup-check `:87 AnyAsync(Code==)`. **Supplier** (`Supplier.cs:5-16`, cfg `:14-27`): Code/Name req + Type enum + TaxCode(20)/Phone/Email/Address/ContactPerson/Note. `SupplierType.cs`: NhaCungCap=1/NhaThauPhu=2/ToDoi=3/DonViDichVu=4/ChuDauTu=5. ❌ **THIẾU Status/TinhTrang (KHÔNG có field/enum nào)** + bank-acct + legal-rep (≠ContactPerson) + quality-score; "Cả hai" PHÂN LOẠI unmappable (Type single-valued). Create `CreateSupplierCommand.cs:10` dup `:45`. **Seed = idempotent `existingCodes.Contains→skip`** (`DbInitializer.SeedDemoMasterDataAsync:2149`, today 18 supplier `:2155` + 8 project `:2222`; WorkItems 15 rows tuple-loop `SeedCatalogsAsync:576-599`). **NO bulk import** — Master chỉ single CRUD; Import/Upload hits = Forms/PE/Employees attachment only; POST one-at-a-time. **Seed→prod:** `DbInitializer.InitializeAsync` chạy MỌI startup (`Program.cs:197` unless `--no-db-init`) → `MigrateAsync` THEN seed; demo gated `config.GetValue<bool>("DemoSeed:Disabled")` (`:80`) NHƯNG SeedDemoMasterData+SeedCatalogs chạy BẤT KỂ flag (ngoài if-block :108/:115) → seed method mới auto-reach prod next deploy. Rec: idempotent DbInitializer mirror (NOT API loop). Surprise: real+demo data sẽ trộn chung Suppliers/Projects/WorkItems (18/8/15 demo rows) → cân nhắc gate demo off prod. Tag `[master-import, workitem-exists, seed-idempotent, s55]`.
|
||||
|
||||
- **2026-06-10 (S57 perm-broaden blocks A/B/E/F — on-disk):** ⭐ **BE AUTHZ SPLIT (decision-critical for E):** Config controllers gate WRITE behind `[Authorize(Roles="Admin")]`, READ open to any-authed: `HrmConfigsController.cs:15` class `[Authorize]`+GET open `:19-21`, all POST/PUT/DEL `Roles="Admin"`; `CatalogsController.cs:14` same (write `:23+` Admin); `MeetingRoomsController.cs:15` same (comment `:9-10` explicit). → granting FE read on Hrm_Config/Catalogs/MeetingRooms = pure UI-visibility, BE already correct. **BUT Master 3 controllers = class `[Authorize]` ONLY, no per-action role:** `SuppliersController.cs:17`, `ProjectsController.cs:11`, `DepartmentsController.cs:11` — ANY authed user can POST/PUT/DELETE via API (FE menu perm is only gate, not BE-enforced). Flag em-main: making Suppliers/Projects/Departments visible-to-all ⇒ staff can write master incl. S55 prod data unless add `[Authorize(Roles="Admin,CatalogManager")]` or per-action policy. **S55 PROD DATA location:** `SeedRealMasterDataAsync` `:2267-2460` writes to **Projects**(62, `:2270`), **WorkItems**(71=CatalogWorkItems key, `:2430-2438`), **Suppliers**(3, `:2440-2456`) — all ungated idempotent per-code. **10 departments now** (`SeedDepartmentsAsync:2104`): 9 orig + IT (`:2115`). **31 demo users** (`SeedDemoUsersAsync:1553-1609`): dept spread BOD4/PM2/CCM9/PRO6/QS2/FIN2/ACT1/EQU1/HRA2; roles = mostly Drafter/CostControl/Procurement + 1 CatalogManager (`catalog.manager@`, dept PRO, `:1608`). NO user has zero-role; every demo user authenticatable. **Inherit-root display:** `GetMyMenuTreeQuery.cs:96 HasAccess = CanRead OR Children.Any(HasAccess)` → a non-inherit root (Hrm/Off/Master) auto-shows if ANY child has CanRead, so granting leaves is enough to reveal the root node (root row itself optional for display, but grant it too for cleanliness). Inherit-roots (Contracts/Pe/Wf/PeWf) cascade root→child so root-row-only suffices there. Menu already S57-edited: `Personal` group + `Off_ChamCong` re-parent Off→Personal via `parentBackfill:1908`. Tag `[s57-perm, be-authz-split, master-write-open, s55-data, 31user]`.
|
||||
|
||||
- **2026-06-10 (S57 perm-broaden RECON blocks C/D — RAG down, on-disk):** ⭐ **SEED MODEL:** `SeedAdminPermissionsAsync` `DbInitializer.cs:1939-1977` Admin loops `MenuKeys.All` CRUD=true skip-existing dedup (`:1950/:1952`). Calls 2 sub: (a) `SeedPurchaseEvaluationPermissionDefaultsAsync` `:2036-2098` → **7 roles** {Drafter,DeptManager,Procurement,CostControl,ProjectManager,Director,AuthorizedSigner} Read+Update on PE keys only; (b) `SeedCatalogManagerPermissionsAsync` `:1984-2029` → role **CatalogManager** full-CRUD 9 master keys. **NO generic per-employee Read seed** — plain Drafter user sees ONLY PE keys; DOESN'T see Off_*/Hrm_*/Master/Contracts. **Most non-admin staff today see ~nothing but PE.** GetMyMenuTree `:96` filters CanRead=true. **Permission entity** `Permission.cs:3-15`: RoleId/MenuKey/CanRead/Create/Update/Delete. Dedup=app-level skip-existing per(RoleId,MenuKey), NO DB upsert. **13 AppRoles** `AppRoles.cs:23`: Admin(system)+12 employee. **4 inherit-roots** `GetMyMenuTreeQuery.cs:56-84`: Contracts/Workflows/PurchaseEvaluations/PeWorkflows — root grant auto-cascades to child IF child no own row (`:66`). Hrm/Off/Master NOT inherit → each leaf needs own row or add to switch. **GRANT-ALL pattern (block D):** mirror CatalogManager seeder but loop `roleManager.Roles` (all 13) × chosen key-set, CanRead=true only, insertion AFTER SeedCatalogManagerPermissionsAsync `:1976`. Tag `[s57-perm-recon, seed-model, no-employee-default, inherit-4root]`.
|
||||
|
||||
- **2026-06-10 (menu-order cross-repo recon SE↔NAMGROUP, RAG down, on-disk):** ⭐ **SE menu seed = `SeedMenusAsync` `DbInitializer.cs` tuple-list** (NOT partial). Văn phòng số root `Off` (Order=29) `:1769-1792`; HR root `Hrm` "Nhân sự" (Order=28) `:1754-1767` (+`Hrm_Dashboard` appended out-of-order `:1791`). ⚠️ **HR SCATTERED 2 roots:** `Hrm` holds only Hồ sơ+Cấu hình HRM(6 leaf)+Dashboard; transactional HR (Nghỉ phép/OT/Công tác/Đặt xe/Chấm công/Báo cáo CC) live under `Off` as `Off_DonTu_*`/`Off_DatXe`/`Off_ChamCong`/`Off_AttendanceReport`. **SEED = UPSERT that RE-SETS Order** (`:1845-1871` `if(existing.Order!=o){existing.Order=o}`) → reorder in code propagates to Dev/prod next deploy, NO migration. BUT Label/ParentKey/Icon NOT touched on existing rows (`:1855` comment) — rename needs separate `labelBackfill` dict `:1874`. **Order = BE-only:** `GetMyMenuTreeQuery.cs:35 OrderBy(m.Order)`; both FE `Layout.tsx` render `useAuth().menu` as-is, `staticMap`+`menuKeys.ts` = key→route ONLY (no sort). FE needs NO edit for pure reorder. **NAMGROUP "Puro" = hardcoded FE array (NOT DB seed),** index=order: client `InternalLayout.tsx:83-118` (Nhân sự 3-item / Văn phòng số 6-item FLAT / Chấm công under separate "Cá nhân" group); admin `AdminLayout.tsx:87-119` splits by function not HR/office. NAMGROUP `Đơn từ`/`Phòng họp`/`Đề xuất` = flat single links (no sub-children) vs SE deep-nested. Tag `[menu-order, se-namgroup, seed-upsert-order, fe-be-driven, s57]`.
|
||||
|
||||
- **2026-06-11 (S59 recon — prod test-data wipe + PE tree Hạng mục, prod+on-disk):** ⭐ **Prod:** PE=10 active (1 Nháp + 1 DaDuyet(7) + 8 ChoDuyet(10), MaPhieu A/031-040, ALL WorkItemId NULL) + child 20/10/20/28/138/18/18 (Sup/Det/Quote/Appr/Chg/Att/LvlOp); Contracts=7 ALL `[DEMO]` 05-08 pin V1 (AwId NULL) + Appr15 + details15; Budgets/WorkflowApps/Proposals/Attendances/Meetings ALL 0; Notifications 64. Seq: PE/2026/A=40 B=1; CT=7 demo prefix LastSeq=1. **FK:** PE child CASCADE trừ `Quotes→PE NO_ACTION` (multi-path; Plan R S23 proved single `DELETE FROM PurchaseEvaluations` OK — NO_ACTION check end-of-statement sau cascade Details→Quotes). Contract child ALL CASCADE. PE.ApprovalWorkflowId Restrict → wipe PE trước khi xóa AW QT-DN-V2-001 v1 (inactive, còn 1 PE pin). AW V2=8: 7 ghim KEEP. **Uploads orphan:** purchase-evaluations/ 19 folder vs 10 PE → ~10 orphan từ S23 (file không xóa); contracts/ 1. **Demo gate OK:** SeedDemoContracts/PE TRONG `DemoSeed:Disabled` (DbInitializer:80,131-132) → wipe không resurrect. **Surprise:** Users 55 total / 21 active — 20 user THẬT batch 2026-06-11 06:01 (S58 seed fix ăn; thanh.lethanh NOW EXISTS — stale S57bis mem; chuong.phan typo-domain VẪN active song song twin). **FE tree:** `pe/PurchaseEvaluationsListPage.tsx:138-179` Project>Year(createdAt :150)>Supplier; SHA256 identical 2 app; PeListItem ĐÃ có workItemId/Name (types :116-118, BE Features :514/570/644) → đổi tree FE-only. Tag `[s59-recon, prod-wipe, pe-tree-workitem]`.
|
||||
|
||||
- **2026-06-11 (S57bis lock no-op — prod user census, on-disk+prod):** ⭐ `LockDemoSampleUsersAsync` (DbInitializer.cs:1552, chạy CUỐI :98) hardcode 14 named-person email (bod.huynh/pm.nguyen/fin.do/qs.hoang…) = population CHỈ CÓ TRÊN DEV. **Prod 34 user ALL-active:** 20 UAT-matrix placeholder hand-created batch 2026-05-13 15:04-05, scheme `{act,equ,fin,hra,pm,qs}.{nv,pp,tp}@` + `bod.{1,2}@` (FullName tự khai "ACT NV - Drafter+Accounting", "[Bypass]"/"[SkipFinal]" = test Mig 29-31 flags) + 9 real staff hand-created 05-04→05-12 + `binh.lethanh@` (người thật Lê Thanh Bình — seed dùng `thanh.lethanh@` KHÔNG tồn tại prod) + `chuong.phan@solution.com.vn` TYPO-domain dup (twin đúng tạo 05-12) + admin/catalog.manager/nv.test. **ROOT CAUSE seed-user never-on-prod:** prod `Identity:Password:RequiredLength=12` (appsettings.Production.json) vs `DemoUserPassword="User@123456"`=11 chars → CreateAsync silent-fail MỌI startup từ prod-init 04-21 (code comment :1675-79 đã biết); Dev fallback 8 (DependencyInjection.cs:67 `?? 8`, Development.json no Identity section) → Dev đủ 33 user named-person. `bod.1@` NEVER in git pickaxe = tạo tay qua admin UI, không phải seed. Surprise: _Dev hiện CŨNG chưa khóa (Locked=0; LockoutEnd=MaxValue sẽ persist qua reconcile re-activate :1714 nếu từng chạy) → lock chưa từng execute against _Dev runtime. Fix cần 20 email prod-thật; GIỮ binh.lethanh + 9 real + admin/catalog.manager; `nv.test@` = creds smoke-verify (khóa = vỡ cicd smoke). Tag `[s58, s57bis-lock-noop-recon, prod-user-census, pwd-policy-env-divergence]`.
|
||||
|
||||
- **2026-06-11 (S57bis PE recon — 4 đầu việc sếp, on-disk):** ⭐ PE entity NO Year, NO WorkItem link (`PurchaseEvaluation.cs:15` ProjectId req; Detail free-text `PurchaseEvaluationDetail.cs:10-13`). Create cmd `PurchaseEvaluationFeatures.cs:19-30`; MaPhieu gen-AT-CREATE `:114-116` format `PE/{YYYY}/{A|B}/{Seq:D3}` (`PurchaseEvaluationCodeGenerator.cs:23`). Main create UI = `PeWorkspaceCreateView.tsx` (:151 workflow-select isUserSelectable ĐẦU TIÊN → tenGoiThau → projectId → DiaDiem → MoTa → PaymentTerms → budget; canSubmit :129 = wf+project+ten). PE controller class-`[Authorize]` ONLY no policy → mở menu là đủ, no silent-403. Pe_* leaves NOT in `MenuKeys.All` (chỉ root :156); PE defaults 7 role × 11 key (root + 2type×{group,WfView,List,Create,Pending}) `DbInitializer.cs:2098-2160`. S57 `SeedAllRolesReviewReadPermissionsAsync:1993-2001` InReviewScope EXCLUDES Pe; extend đúng = `key == MenuKeys.PurchaseEvaluations` EXACT (prefix "Pe" sẽ dính PeWorkflows admin!) — root inherit cascade (`GetMyMenuTreeQuery.cs:49-82`). Demo gate: prod `appsettings.json:35 DemoSeed:Disabled=true` → 7 `[DEMO]` HĐ + 4 `[DEMO]` PE (MaPhieu `[DEMO]-A-001`) KHÔNG lên prod; UNGATED trên prod = 31 users + 18 demo NCC + 8 demo project (:2244-2315) + real 62/71/3 (:2329-2522). ⚠️ Clear-demo gotcha: seed re-add per-code idempotent MỖI startup → xóa DB-only sẽ resurrect, phải gỡ khỏi DbInitializer code. WorkItem write Admin-only (`CatalogsController:113-130`) — CatalogManager có menu-perm nhưng API write bị chặn. Tag `[s57bis, pe-recon, demo-inventory]`.
|
||||
|
||||
- **2026-06-16 (S66 recon — mirror Hồ sơ NS fe-user→fe-admin, on-disk):** ⭐ **VERDICT (B): vá `fe-admin/src/index.css` TRƯỚC rồi cookie-cutter SẠCH.** Copy page thuần = VỠ MÀU. **fe-admin index.css = 86 dòng (chốt `7feb53e`, TRƯỚC redesign S58 `e959f72`/`c98030f`) → THIẾU:** 4 accent palette `teal/amberx/violet/greenx` (mỗi cái 50/100/500/600/700) + 3 utility `.icon-chip`/`.app-gradient-brand`/`.card-accent`/`.stat-value`. **CÓ SẴN:** `--color-brand-50..900` (hex y hệt fe-user, incl brand-800 #175685 :15), `.label-eyebrow` :54, font Be Vietnam Pro :22. ⚠️ heading-weight CẦN CHECK (fe-user S66 `h1-h4 font-weight:700 color:#0b1220`; fe-admin có thể còn 600). **Page fe-user phụ thuộc THẬT:** text-brand-800 ×9, teal/amberx/violet/greenx-50/500/700 ×4 mỗi, icon-chip ×3, app-gradient-brand ×1. **Wiring fe-admin ĐỦ SẴN (0 đụng):** route `/employees`+`/new` `App.tsx:82-83` · `EmployeeCreatePage.tsx` **identical** (diff rỗng) · menu `Hrm_HoSo` `menuKeys.ts:33`+staticMap `Layout.tsx:53`. **Lib parity ✅:** ui/{Input,Select,Textarea,Button} prop-sig **identical** (HTMLAttributes passthrough, content khác chỉ className=chủ ý non-breaking) · EmptyState/cn/api/apiError ✅ · `types/employee.ts` **identical** · Paged `types/master` ✅ · `DepartmentTreeNode` định nghĩa INLINE trong page (:65 mirror BE DepartmentTreeNodeDto, đi theo copy — không cần type file). **KHÔNG khác chủ ý admin/user** — mirror y hệt như S35 (`9616ae2`/`c3cd343` cùng commit 2 app); write Admin-gated ở BE controller. **Cấu trúc:** admin=1200 dòng (S35 cũ, 2-panel + 5 `<details>` + 5 satellite mutex); user=1602 dòng (redesign: cây "SOLUTION COMPANY" đệ quy + list cột trái dọc · detail phải 5 tab accent + avatar gradient). **Scope: 3 file** = index.css (chèn ~40 dòng token+class block từ fe-user :29-51+:100-160) + overwrite EmployeesListPage.tsx (import path KHÔNG chỉnh, đều `@/`) + (tùy chọn heading-700). KHÔNG đụng route/menu/types/primitives/CreatePage. Tag `[s66, mirror-employee-page, accent-token-missing-fe-admin, verdict-B, cookie-cutter-after-css]`.
|
||||
|
||||
- **2026-06-16 (S65ter recon — Mục E "Link hồ sơ" phiếu PE, on-disk):** ⭐ Anh Kiệt: chèn mục E "Link hồ sơ" NGAY DƯỚI mục D "Bản so sánh". **Render 4 file** (SHA256-identical 2 app): `components/pe/PeDetailTabs.tsx` (detail+edit, 2770 LOC) + `PeWorkspaceCreateView.tsx` (create) × {fe-user,fe-admin}. KHÔNG tabs — 5 `<Section>` dọc, tiêu đề "1./2./3./4." + sub-item chữ thường "a./b./c./d." (label cột trái w-44). **Mục D ∈ Section "3. Đơn vị NCC/TP" = `ChonNccSection`** (`PeDetailTabs.tsx:1302-1375`): a.NCC(:1321) · b.Tổng hợp NS trình ký(:1324 `PeBudgetSummaryTable` — S61 thay Budget) · c.Giá chào thầu(:1326 auto) · **d.Bản so sánh(:1337-1348)** = `GeneralAttachmentsSection`(:2613) upload N FILE filter `supplierId===null` purpose=ComparisonTable(4). **INSERT E: `PeDetailTabs.tsx:1348`** (sau `</div>` mục D, trước paymentTerms :1350); mẫu = block :1337-1348. Create: `PeWorkspaceCreateView.tsx:277` (sau FormRow d). **BE: `PurchaseEvaluation.cs`(:1-72) KHÔNG có field URL** — DiaDiem/MoTa/PaymentTerms semantic khác. 1 link → `string? HoSoLink`(1000)+Mig AddColumn+cmd+DTO+validator; nhiều link → entity con `PurchaseEvaluationLink`+CREATE TABLE+CRUD (nặng). **Attachment KHÔNG reuse URL** — `PurchaseEvaluationAttachmentFeatures.cs:18-55` IFormFile thuần (FileSize>0+ContentType whitelist+IFileStorage). Mục D multi-row → E nên multi-row đối xứng. ⚠️ Surprise: comment :1314 nói "purpose=ComparisonTable hoặc supplier-row null" SAI — filter thực :1315-17 CHỈ `supplierId===null`. Tag `[s65ter, pe-section-e-link, attachment-file-only, insert-1348]`.
|
||||
|
||||
- **2026-06-16 (S65 recon — public HRM module for all-role, on-disk):** ⭐ **Mục 6 CRITICAL (gotcha #44 family) RESOLVED-FAVORABLE:** `EmployeesController.cs:23-25` = class `[Authorize(Policy="Hrm_HoSo.Read")]` (NOT `Roles="Admin"`) + per-action `Hrm_HoSo.{Create/Update/Delete}` (:45/:54). Policy resolves THROUGH permission matrix (`MenuPermissionHandler.cs:40-52` baseQuery role×menuKey CanRead; Admin-bypass :27) → seed CanRead row = API ALSO unlocked, NO 403. `HrDashboardController.cs:8-11` = `[Authorize]` any-auth only (`/api/hr/dashboard`). GET list = `/api/employees` (:28). ⇒ **seed BE permission ĐỦ, không cần đụng controller.** **Menu keys dưới `Hrm` (prefix THẬT = `Hrm_`):** root `Hrm`="Nhân sự" parent=null Order=28 (`DbInitializer.cs:1805`); `Hrm_Dashboard`="Dashboard NS" parent=Hrm Order=1 (:1850); `Hrm_HoSo`="Hồ sơ Nhân sự" parent=Hrm Order=2 (:1806). Hrm_Config* (6 leaf: LeaveTypes/Holidays/Shifts/OtPolicies/Vehicles/Drivers) parent=**Master** Order=25 (S57 re-parent :1812 — KHÔNG dưới Hrm). **Revoke (Mục 2):** `RevokeTemporarilyHiddenModulesAsync` :2151 — match `StartsWith("Hrm")||StartsWith("Off")||==Personal` AND role!=Admin AND any-flag-true (:2162-67) → set 4 cờ CRUD=false. ⚠️ **THỨ TỰ: gọi CUỐI CÙNG `:2040` trong SeedAsync, SAU grant `:2033`** → revoke THẮNG mọi grant trước nó. Mở Hrm = phải (a) sửa revoke loại trừ Hrm_HoSo/Hrm_Dashboard HOẶC (b) thêm grant SAU :2040. **Pe pattern (Mục 3):** `SeedAllRolesReviewReadPermissionsAsync:2055` — `roleManager.Roles.ToListAsync():2090` loop ALL role × reviewKeys, upsert CanRead (+CanCreate cho Pe_*), additive idempotent (skip-existing non-Pe :2115). **Seed entity (Mục 4):** `Permission`(RoleId,MenuKey,4 CRUD); idempotent = app-level skip per (RoleId,MenuKey); **13 role** `AppRoles.All` (Admin/Drafter/DeptManager/ProjectManager/Procurement/CostControl/Finance/Accounting/Equipment/Director/AuthorizedSigner/HrAdmin/CatalogManager). `Hrm_HoSo`+`Hrm_Dashboard` ĐỀU ∈ `MenuKeys.All:153,160` (khác Pe_* leaf NOT in All). **FE (Mục 5):** menu-tree-API-driven via `GetMyMenuTreeQuery.cs` (`/api/menus/me`); Hrm NOT inherit-root (chỉ 4: Contracts/Workflows/Pe/PeWf :51-59) → MỖI leaf cần CanRead row riêng, NHƯNG root `Hrm` auto-hiện nếu child có access (`HasAccess:96` CanRead OR child). `Layout.tsx:145 USER_HIDDEN_KEYS`={System,Users,Roles,Permissions,Forms,Reports} — KHÔNG chứa Hrm → fe-user auto-render; `staticMap Hrm_HoSo→/employees :75`, `Hrm_Dashboard→/hr/dashboard :104`. NO PermissionGuard per-route fe-user. ⇒ **chỉ seed BE, FE tự hiện.** Tag `[s65-recon, public-hrm, policy-based-authz-not-roles, revoke-runs-last]`.
|
||||
|
||||
|
||||
59
.claude/agent-memory/investigator-codebase/archive/_INDEX.md
Normal file
59
.claude/agent-memory/investigator-codebase/archive/_INDEX.md
Normal file
@ -0,0 +1,59 @@
|
||||
# Archive Index — investigator-codebase L2
|
||||
|
||||
> **Purpose:** table-of-contents for the L2 archive (verbatim history NOT in RAG). One line per record. Use this to locate an entry, then open the cited file and Ctrl-F the substring.
|
||||
> **Pointer convention:** `substring:"…"` = a verbatim string that occurs EXACTLY ONCE in the named file (Ctrl-F / `grep -F`). Primary lookup = the substring. No line numbers (archives are append-only and line numbers drift). Fallback = the date + workType.
|
||||
> **Archives are FROZEN + additive:** verbatim entry bytes are never edited; new entries are appended at end. This index + the `.gist.md` files are the only derived layers.
|
||||
> **Sorted by DATE (ascending).** `[meta]` = curate/setup bookkeeping note (low signal). `[stub→git]` = FIFO-trim pointer whose full text lives in git, not on disk.
|
||||
> **Files covered:** `2026-05-q1.md` (11) · `2026-05-q2.md` (4) · `2026-05-q3.md` (4) · `2026-05-q4.md` (3) · `2026-06.md` (18) = 40 records.
|
||||
|
||||
---
|
||||
|
||||
## 2026-05
|
||||
|
||||
| Date | workType | verdict | pointer |
|
||||
|---|---|---|---|
|
||||
| 2026-05-11 | [meta] agent setup | baseline load 44 gotcha/27 mig/81 test, no investigation yet | q1 · substring:"2026-05-11 (setup)" |
|
||||
| 2026-05-11 | [meta] agent setup (dup) | same baseline note, re-archived S34 | q3 · substring:"2026-05-11 — Setup baseline" |
|
||||
| 2026-05-13 | em-main-solo log | no spawn; bug #45 + F1/F2/F3 Mig28/29, per-NV scope pattern saved | q1 · substring:"S21 t3-t5, no spawn" |
|
||||
| 2026-05-13 | state-cumulative log | no spawn; Mig30 F4 per-Level, pwd ≥12 chars, gotcha #38/#30 reinforced | q1 · substring:"S22, no spawn — em main solo throughout" |
|
||||
| 2026-05-14 | pre-flight audit | Plan K F2 refactor: Mig30 state map, F2 Drafter branch :119-161, prod 33u/4 flagged | q1 · substring:"S23 t1 spawn K0 — Plan K F2 refactor pre-flight" |
|
||||
| 2026-05-15 | RCA / bug audit | F3 OK / F4 BROKEN :973 `!readOnly` short-circuits before isApproverChoDuyet; ~3-5 LOC | q1 · substring:"S23 t2 spawn L2 — F3+F4 edit menu Duyệt audit" |
|
||||
| 2026-05-15 | semantic audit | F1-F4 all keep Phase=ChoDuyet except F1.Drafter (TraLai=98); code correct, bro mental-model disconnect | q1 · substring:"S23 t2 spawn M0 — Plan M F1+F2+F3+F4 ChoDuyet semantic audit" |
|
||||
| 2026-05-15 | RCA / bug audit | HYP-B BE :765 FirstOrDefault(Order==) picks wrong slot post-Mig29; +ApproverUserId ~2-3 LOC; bug since Mig29 deploy not S23 | q1 · substring:"S23 t3 spawn — UAT bug Allow* flags không hiện cho actor non-row1" |
|
||||
| 2026-05-15 | FE wire audit | Plan P BE-only: Controller `TransitionPeBody` record drops 3 fields; ~6 LOC | q1 · substring:"S23 t6 spawn Plan P FE wire audit" |
|
||||
| 2026-05-15 | pre-flight cleanup | Plan R: PE.ApprovalWorkflowId Restrict + AW no-soft-delete → hard-DELETE; bro chose Option A | q1 · substring:"S23 t8 spawn Plan R pre-flight cleanup audit" |
|
||||
| 2026-05-15 | pre-flight (5Q) | Plan AA matrix view + sidebar widen; class-[Authorize] bare fixed S18 (gotcha #44); Tailwind JIT full-class | q1 · substring:"S24 t1 spawn Pre-A — Plan AA User Workflow Matrix view" |
|
||||
| 2026-05-15 | em-main-solo wrap | Plan AA wrap 7 commits; Tailwind JIT needs full class strings array (dynamic bg-${c} purged) | q1 · substring:"S24 t1-t4 post-spawn, em main solo 4 polish chunks" |
|
||||
| 2026-05-19 | RCA / bug audit | 2 changelog bugs: Budget logs Header/Update OK (FE filter); ApplyReturnModeAsync :215-378 ZERO log; EntityType.Workflow=5 unused | q2 · substring:"S25 t1 spawn audit 2 bug critical UAT" |
|
||||
| 2026-05-19 | [meta] em-main wrap | Plan AB Bug1+2 wrap; gotcha #48 SQLite tie-break pending docs | q3 · substring:"S25 wrap Plan AB Bug 1+2 audit" |
|
||||
| 2026-05-21 | pre-flight (5Q) | PE list "đám rừng": Phase1 FE group view ~160 LOC, Phase2 ProjectPackage defer | q2 · substring:"S26 spawn Plan AG 5Q audit" |
|
||||
| 2026-05-21 | deep research | RAG distribution 4 study-cases (Cursor/Cline/Continue/Cody); Voyage 200M free; Pattern C user-global MCP | q2 · substring:"S26 spawn Plan AI RAG distribution research" |
|
||||
| 2026-05-22 | [meta] retrospective | spawn-Investigator S20-S26 maybe misattributed general-purpose (registry not loaded S27) — FLAG | q3 · substring:"S27 wrap-up retrospective em main proxy" |
|
||||
| 2026-05-22 | [meta] curate note | archived 10 verbose S21→S24 to q1; KEEP list noted | q3 · substring:"Curate session em main S29 era" |
|
||||
| 2026-05-22 | pre-flight (5Q) | Plan B Contract V2: Mig22/23/24 cookie-cutter, ApproveV2Async :446-634 189-LOC clone; coexist V1+V2 | q2 · substring:"Plan B Contract V2 wire pre-flight audit" |
|
||||
| 2026-05-29 | cross-project port | S39 BVAAU 7-agent extract: split 4→7, repo=domain/EF=infra/test=specialist; aspirational not battle-tested | q4 · substring:"S39 BVAAU 7-agent extract" |
|
||||
| 2026-05-29 | state grounding | S40: mig=40 / gotcha=55 / tables=84 (ToTable not DbSet) / FE 65 pages; count ToTable lesson | q4 · substring:"S40 STATE GROUNDING" |
|
||||
| 2026-05-30 | pre-flight | P11-A WorkflowApps: schema pinned Mig39, Proposal=mirror; ⚠️ ApprovalWorkflow.cs:72 "Level NOT OR-of-N" contradicts old mem | q4 · substring:"P11-A WorkflowApps wire pre-flight" |
|
||||
|
||||
## 2026-06
|
||||
|
||||
| Date | workType | verdict | pointer |
|
||||
|---|---|---|---|
|
||||
| 2026-06-01 | monthly drift audit | ground truth mig=42/gotcha=#56/tests=154/tables≈91; biggest drift ef-core-migration skill | 2026-06.md · substring:"MONTHLY DRIFT AUDIT" |
|
||||
| 2026-06-01 | pre-flight | P11-C Vehicle+Driver = extend HrmConfigs (not new module); BienSo UNIQUE needs HasFilter([IsDeleted]=0) | 2026-06.md · substring:"P11-C Vehicle+Driver catalog pre-flight" |
|
||||
| 2026-06-07 | governance recon | Harness 1/2/3 adap: roster 8→10, hmw wave-mode, email id=`se`; git-diff containment proven | 2026-06.md · substring:"Harness 1/2/3 adap-apply recon — 3 slice" |
|
||||
| 2026-06-07 | wave guardrail audit | B6 isolation 3/3 PASS; `git check-ignore -v` ground-truth; Bash=/usr/bin/bash NOT PowerShell despite env | 2026-06.md · substring:"S50 wave `h2-verify` — B6 guardrail audit" |
|
||||
| 2026-06-08 | add-kind verify | HrmConfigs no kind-enum BE; add 1 kind = 11-spot stack; gotcha #57 still bare on LeaveType/Shift/OtPolicy | 2026-06.md · substring:"S50 P11-C Vehicle+Driver — HrmConfigs add-kind pattern VERIFIED" |
|
||||
| 2026-06-08 | reachability audit | gotcha #57 EXT: FIX 3 Master (Dept/Supplier/Project), SKIP 3 (ContractClause/MeetingRoom/EmployeeProfile); global filter MAKES bug | 2026-06.md · substring:"S51 gotcha #57 EXTENSION reachability audit" |
|
||||
| 2026-06-09 | pre-golive verify | P11-B/D/E/F + ApproveV2 all PASS; dept IT exists 0 user prod; Travel/Vehicle ApproveV2 untested | 2026-06.md · substring:"S56 pre-golive verify — 4 logic streams" |
|
||||
| 2026-06-09 | FE-redesign recon | NOT a rewrite (S55 primitives auto-inherit); hover-hidden ~absent; 3 Drawer candidates; bậc-thang ref exists | 2026-06.md · substring:"S56 Phase 2 FE-redesign RECON — 25 page audit" |
|
||||
| 2026-06-09 | master-import recon | WorkItem master EXISTS; Project missing Year/Investor/Location/Package; seed idempotent reaches prod | 2026-06.md · substring:"S55 master-data Excel-import recon" |
|
||||
| 2026-06-10 | perm-broaden recon | BE authz split: Suppliers/Projects/Departments class-[Authorize] ONLY (write-open); S55 data :2267-2460; 31 users | 2026-06.md · substring:"S57 perm-broaden blocks A/B/E/F" |
|
||||
| 2026-06-10 | perm-broaden recon | seed model: no generic per-employee Read; most staff see only PE; 4 inherit-roots; grant-all pattern | 2026-06.md · substring:"S57 perm-broaden RECON blocks C/D" |
|
||||
| 2026-06-10 | menu-order recon | SE menu = DbInitializer tuple UPSERT re-sets Order; HR scattered 2 roots; NAMGROUP Puro = hardcoded FE array | 2026-06.md · substring:"menu-order cross-repo recon SE↔NAMGROUP" |
|
||||
| 2026-06-11 | prod-wipe recon | PE 10 active, Quotes→PE NO_ACTION (single DELETE OK); 20 real users batch 06-11; FE tree FE-only change | 2026-06.md · substring:"S59 recon — prod test-data wipe + PE tree" |
|
||||
| 2026-06-11 | prod-user census | LockDemoSampleUsers no-op (dev-only pop); prod 34u all-active; pwd-policy env divergence (12 vs 11) silent-fail | 2026-06.md · substring:"S57bis lock no-op — prod user census" |
|
||||
| 2026-06-11 | PE recon | PE no Year/no WorkItem link; MaPhieu gen-at-create; extend InReviewScope EXACT `==PurchaseEvaluations` not prefix | 2026-06.md · substring:"S57bis PE recon — 4 đầu việc sếp" |
|
||||
| 2026-06-16 | public-HRM recon | EmployeesController policy-based authz (NOT Roles=Admin) → seed CanRead unlocks API; RevokeTemporarilyHiddenModules runs LAST :2040 beats all grants; 13 roles | 2026-06.md · substring:"S65 recon — public HRM module for all-role" |
|
||||
| 2026-06-16 | PE Section-E recon | insert mục E "Link hồ sơ" at `PeDetailTabs.tsx:1348` (after mục D); BE PurchaseEvaluation.cs has NO URL field → add `HoSoLink` nvarchar(1000); attachments are IFormFile-only (no URL reuse) | 2026-06.md · substring:"S65ter recon — Mục E" |
|
||||
| 2026-06-16 | FE-mirror recon | mirror Hồ sơ-NS fe-user→fe-admin = VERDICT B (patch `fe-admin/src/index.css` FIRST then cookie-cutter); fe-admin index.css 86-line missing 4 accent palettes + icon-chip/app-gradient-brand; scope 3 files | 2026-06.md · substring:"S66 recon — mirror Hồ sơ NS fe-user" |
|
||||
52
.claude/agent-memory/memory-budget.json
Normal file
52
.claude/agent-memory/memory-budget.json
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"_note": "Harness-9 (S70, 2026-06-17) memory budget. Caps SEEDED BY MEASUREMENT (scripts/measure-agent-memory.ps1), NOT imagined headroom. Budget-audit @session-start (session-start.md §2.1.2): if curate-to-fit is dropping important markers, BUMP the relevant cap rather than cut markers. Re-measure with the script; never hand-edit measured_bytes.",
|
||||
"seeded_date": "2026-06-18 (S71 re-measure after G1 curate — reviewer 36.7KB + investigator 29.8KB over-cap from S71 same-role race, curated L1->L2 back under auto-inject cap)",
|
||||
"last_sleep_at": "2026-06-18",
|
||||
"_last_sleep_at_note": "Harness-10b sleep-recovery (S72): timestamp lan cuoi chay /sleep-recovery-memory-l2. null = chua tung. session-start §2.1.2 + session-end §L.b(c) doc field nay -> INFORM goi-y nen L2 neu null hoac today-last_sleep_at>=7 ngay. Lead=single-writer (chi command sleep set field nay).",
|
||||
"tiers": {
|
||||
"l1_hot": {
|
||||
"file": "MEMORY.md",
|
||||
"injected": "auto (harness injects ~first 200 lines / 25KB on spawn)",
|
||||
"autoinject_cap_bytes": 25600,
|
||||
"soft_cap_bytes": 30720,
|
||||
"rule": "keep MEMORY.md < autoinject_cap so the WHOLE hot file injects; over soft_cap => curate L1->L2"
|
||||
},
|
||||
"l2_index": {
|
||||
"file": "archive/_INDEX.md",
|
||||
"injected": "read-on-demand map (inject the map, not the territory)",
|
||||
"cap_bytes": 20480,
|
||||
"seeded_from": "max measured _INDEX = cicd-monitor 16779B (+ ~22% headroom). cicd-monitor at ~82% = WATCH-agent (grows with each run); when it nears cap -> gist-of-index or split, do NOT drop markers",
|
||||
"pointer_style": "substring (git-sha / Run#NNN / unique-phrase keyed), fallback Ctrl-F. NO line-hints (additive appends shift lines)"
|
||||
},
|
||||
"l2_verbatim": {
|
||||
"file": "archive/<period>.md",
|
||||
"injected": "read-on-demand content (frozen, additive-only). NO inject cap",
|
||||
"rule": "NEVER rewrite existing bytes; curated L1 entries APPEND to end only"
|
||||
},
|
||||
"l2_gist": {
|
||||
"file": "archive/<period>.gist.md",
|
||||
"injected": "read-on-demand 4-field distill (distill-gen counter guards re-distill). NO inject cap",
|
||||
"rule": "coverage-diff GATE: every surprise/guard/file:line/root-cause/gotcha# in verbatim must survive in gist (or marked N/A)"
|
||||
}
|
||||
},
|
||||
"archive_gate": {
|
||||
"_note": "Harness-11 PART-A (S73, 2026-06-18) params for scripts/memory-archive-gate.ps1 (DRY-RUN planner, NO-API, grep+measure only). ADDITIVE — does not touch measured/tiers/last_sleep_at. A4 hysteresis = drain to BELOW low_watermark (not just to the line). A5 = never auto-drain below keep_floor newest entries (WARN instead). A6 = only PROPOSE archive after strike_threshold consecutive over-cap runs (stateless script persists strikes in .claude/agent-memory/.archive-strikes.json).",
|
||||
"autoinject_cap_bytes": 25600,
|
||||
"low_watermark_ratio": 0.85,
|
||||
"keep_floor_entries": 5,
|
||||
"strike_threshold": 2
|
||||
},
|
||||
"measured": {
|
||||
"cicd-monitor": { "l1_hot": 23653, "l2_verbatim": 178856, "l2_index": 16779, "l2_gist": 29737, "rollout": "done" },
|
||||
"investigator-codebase": { "l1_hot": 23187, "l2_verbatim": 68612, "l2_index": 9234, "l2_gist": 27297, "rollout": "done (re-curated S71: moved 3 oldest, gist gen:2)" },
|
||||
"reviewer": { "l1_hot": 24844, "l2_verbatim": 54901, "l2_index": 7370, "l2_gist": 19114, "rollout": "done (re-curated S71: moved 10 oldest, gist gen:2)" },
|
||||
"implementer-backend": { "l1_hot": 17692, "l2_verbatim": 59233, "l2_index": 10105, "l2_gist": 23079, "rollout": "done" },
|
||||
"frontend-designer": { "l1_hot": 24004, "l2_verbatim": 0, "l2_index": 0, "l2_gist": 0, "rollout": "n/a (no archive; watch L1 near cap)" },
|
||||
"test-specialist": { "l1_hot": 23919, "l2_verbatim": 0, "l2_index": 0, "l2_gist": 0, "rollout": "n/a (no archive; watch L1 near cap)" },
|
||||
"harvest-curator": { "l1_hot": 18952, "l2_verbatim": 0, "l2_index": 0, "l2_gist": 0, "rollout": "n/a (no archive)" },
|
||||
"tooling-auditor": { "l1_hot": 18431, "l2_verbatim": 0, "l2_index": 0, "l2_gist": 0, "rollout": "n/a (no archive)" },
|
||||
"implementer-frontend": { "l1_hot": 12169, "l2_verbatim": 0, "l2_index": 0, "l2_gist": 0, "rollout": "n/a (empty archive)" },
|
||||
"investigator-api": { "l1_hot": 8510, "l2_verbatim": 0, "l2_index": 0, "l2_gist": 0, "rollout": "n/a (empty archive)" },
|
||||
"database-agent": { "l1_hot": 5917, "l2_verbatim": 0, "l2_index": 0, "l2_gist": 0, "rollout": "n/a (no archive)" }
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
> **Persistent diary cross-session.** Auto-injected first ~200 lines at spawn (L1 HOT).
|
||||
> Update BEFORE every stop. Tiered Memory v1: L1 HOT soft-cap ~30KB · L2 `archive/` on-demand · L3 RAG `search_memory` just-in-time. Keep entry ≤ 1.5K chars (gotcha #53).
|
||||
> Full verbatim history pre-S40 → git `d2f52ba` + `archive/2026-05-q1..q2.md`.
|
||||
> Full verbatim history pre-S40 → git `d2f52ba` + `archive/2026-05-q1..q2.md`; S51→S57 detail → `archive/2026-06.md` (S69 curate). Archive map: `archive/_INDEX.md` + per-period `.gist.md`.
|
||||
|
||||
## 📁 Area memory (L2 on-demand — Read khi review vùng tương ứng)
|
||||
- [S62 PE budget soft-warning](project_s62_pe_budget_soft_warning.md) — PASS: hard-block→soft-warning; submit-guard intact + validator giữ `BudgetPeriodAmount>0`, row8 negative-safe (additive-only). Validator class `PurchaseEvaluationFeatures.cs:317` (reconcile S63: stray cwd-misland → canonical, anchor verified).
|
||||
@ -61,35 +61,28 @@ Adversarial pre-commit reviewer SOLUTION_ERP. Read-only verify + live curl prod
|
||||
|
||||
## 📅 Recent activity (FIFO — older → archive/git)
|
||||
|
||||
- **2026-06-12 (S60 đợt1 PE submit-guard + drafter-bypass gate — KHÔNG DELIVER, die mid-run, on-behalf em main ghi hộ, H2-proposed):** Task: review `37122f0` cross-stack (BE TransitionAsync submit-guard đủ-4-thông-tin mục 3 + bypass người-soạn-trong-chuỗi V2 BƯỚC-ĐẦU-only + FE PeDetailTabs ×2 + 14 PeSubmitGuardAndBypassTests 240→254). Die mid-run #53-class (commit body tự khai "Reviewer die mid-run → em main self-gate evidence-checklist PASS 0 blocker") → ship Run #283 PASS prod-verified, bundle rotate both. LEARNED: self-gate em main đứng vững lần 2 (sau S57bis) — checklist deterministic (test gate + diff scope + prod smoke 401/404-control) đủ cho PE refinement cross-stack. SURPRISE: die lần 3 trong 2 ngày (S57bis die-0-byte ×2 + S60 mid-run) DÙ promote-tier inherit Fable 5 → model-tier KHÔNG phải nguyên nhân die (nghi resume-kill/harness class) — trend data cho Harness-4. Tag `[s60, die-mid-run-3rd, self-gate, on-behalf]`.
|
||||
- **2026-06-11 (S57bis product gate — KHÔNG DELIVER, die-0-byte ×2, on-behalf em main ghi hộ, H2-proposed):** Cả 2 spawn (email-gate đầu + final gate) chết 0-byte output 0 return (resume-kill class #3, ref `feedback_agent_kill_recovery`) → em main SELF-GATE evidence-checklist: grep authz key-set + role-string vs AppRoles + Mig 49 Up/Down reversible + 240 test + Run #381 + prod smoke 401/404-control. LEARNED: output-file size=0 + im >5 phút = chết, KHÔNG đợi thêm; KHÔNG re-spawn >2 lần trong session có `--resume`. SURPRISE: khác S52 killed-with-partial — lần này 0-byte tuyệt đối (không gì recover được từ return). Tag `[s57bis, die-0-byte-x2, self-gate, on-behalf]`.
|
||||
- **2026-06-10 (S57-resume Harness-4 two-tier adopt gate — PASS-with-fixes, 0 blocker):** Gate trước send-email + commit (governance, không product code). Self-report spawn: `claude-fable-5[1m]` (reviewer = promote-list inherit → direct promote-tier evidence, em main cite được). Independent re-verify ALL GREEN: grep frontmatter = đúng 7 pin `claude-opus-4-8` + 4 `inherit` + 0 `[1m]`-in-frontmatter (2 body-text hits hợp lệ: database-agent.md:46 + README.md:9 MỚI — adap-report "match duy nhất" stale-by-own-edit) + 0 project-pin settings. Evidence track-record **8/8 REAL** vs HANDOFF/STATUS/own-memory (S51 MAJOR · S54 QTV-decoy · S53 Mig46 · S56 H2-4.5/5 + dept-IT-0-user · S57 ×3 controller +5/+5/+5 `[Authorize(Roles="Admin,CatalogManager")]` working-tree). Nấc G-011 đúng mọi chỗ load-bearing (demote = executed-file·pending-restart, 0 overclaim runtime). Fixes: hash PLACEHOLDER trước send (`nac: sent` + "SENT ✓" premature = đúng status-verb class broadcast cảnh báo) · STATUS "(runtime resolve 1M)" thiếu attribution AI_INFRA-s20 · hmw.js:91 log "same-model inherit" stale + :9 "8-agent" vs 9 roles · adap-report "(13)" vs "11" count · invalid-role typo → rơi 'opus' (fail-direction xuống vs H4.5 nghiêng-quality). **Learned:** gate adopt-governance = re-run MỌI grep claim + cross-check evidence vs HANDOFF nguyên văn; n=2 demoted spawn-test double-duty làm inherit-chain proof là HỢP LỆ (registry cached = chạy config cũ) nhưng cần phrase rõ kẻo đọc nhầm thành promote-list spawn-test. Tag [s57, harness-4, two-tier-gate, pre-send-gate, g011].
|
||||
- **2026-06-09 (S56 pre-golive authz live-curl — PASS, 0 blocker):** Live prod curl 8 new endpoints. **8/8 return 401 unauth**; admin-authed: hrm-configs/vehicles(2)+drivers(2), leave-balances/my(5 lazy), attendances/report+excel(200, 6797B xlsx) all 200; non-admin Drafter correctly 403 on the 2 Admin-only attendance endpoints. **gotcha #44 silent-403 sweep CLEAN:** capability GET /it-tickets/assignable-staff returns HTTP 200 `{canReassign:false,staff:[]}` for non-IT Drafter (NOT swallowed 403) + `{true,[]}` admin — handler returns flag, doesn't throw (`WorkflowAppsFeatures.cs:466`). assign-mutation guard fail-closed (:504). E2E: GET /projects payload has all +4 fields (70/70), CAL01 Investor live. Off_AttendanceReport menu key in admin /menus/me. **1 MINOR (non-block, defense-in-depth):** PUT /it-tickets/{id}/assign checks NotFound BEFORE Admin-OR-IT Forbidden (`WorkflowAppsFeatures.cs:496-508`) → existence-oracle leak; mutation itself fail-closed → post-golive hardening only. Tag [s56, pre-golive-verify, authz-clean, gotcha44-clean, notfound-before-forbidden-minor].
|
||||
- **2026-06-19 (S76 Part2+3 PE budget-edit BADGE display-only — Lens-2 FE badges+render-safety, PASS, 0 blocker):** 2 cờ bool CanEditProBudget/CanEditCcmBudget vào 2 DTO approver (AwLevelDto designer + PurchaseEvaluationApprovalLevelApproverDto PE-flow), suy từ ROLE (KHÔNG đổi authz). Badge "✎ NS PRO" (amber) / "✎ NS CCM" (sky). **PASS Lens-2:** (1) **role-set KHỚP gate** — `proBudgetEditors=GetUsersInRoleAsync(Procurement)∪Admin` / `ccmBudgetEditors=CostControl∪Admin` (ApprovalWorkflowV2AdminFeatures.cs:155-157 + PurchaseEvaluationFeatures.cs:976-978) đảo-chiều set-lookup 3-query/req no-N+1; khớp 1:1 gate thật canEditPro/canEditCcm `:800-801`=isAdmin‖Procurement / isAdmin‖CostControl; AppRoles consts verified (`AppRoles.cs:5,9,10`). (2) **role-set DEFINE trước cả 2 site** — PE-flow define `:978` < approvalFlow `:1045` < currentApproval `:1064` (cùng V2-branch scope); designer define `:155` < ToDto `:184`. (3) **DTO flag flows CẢ flow+current** — `PurchaseEvaluationApprovalFlowLevelDto.Approvers` (`:152`) + `PurchaseEvaluationCurrentApprovalDto`-path (`:145`) đều dùng `PurchaseEvaluationApprovalLevelApproverDto` → badge hiện ở Panel-flow lẫn current. (4) **PeWorkflowPanel `.join('/')→map`** giữ separator "/" (`{i>0 && <span>/</span>}`), giữ case rỗng `length===0?'(chưa cấu hình)'`, key=`a.userId` SAFE (validator `HaveNoDuplicateApproverInSameLevel:289` chặn dup ApproverUserId trong 1 level → no React key-collision). (5) **render-safety** — designer wrapper `flex`→`flex flex-wrap` + panel `flex flex-wrap gap-x-1 gap-y-0.5` → badge KHÔNG vỡ layout khi nhiều approver/badge. (6) **mirror 2-app PERFECT** — PeWorkflowPanel fe-admin==fe-user byte-IDENTICAL cả base lẫn now (diff empty); types +2 cờ ở CẢ 2 app (admin:235-236/user:238-239), block diff-identical (chỉ pre-existing inline-comment `//0-based` lệch = noise KHÔNG do change này). (7) **FE typecheck CLEAN** cả 2 app (`tsc --noEmit` exit 0). no mock/alert/TODO. **⚠️ SPEC-vs-DIFF MISMATCH (em-main framing wrong, NOT a code bug):** spec nói "KHÔNG migration" nhưng diff BUNDLES S76 Part1 (migration `AddProBudgetSplitToPeWorkItemBudget` + PeWorkItemBudget domain + PeBudgetSummaryDto + PeDetailTabs 306-LOC matrix rewrite WIRED PUT /budget/pro). Part1 spot-check sound (mig 3-file OK pure-ASCII gotcha#30-respect, PUT wired real not-mock). **🟡 MAJOR race STILL PRESENT (carry-over Part1, line 64 entry):** PeDetailTabs 2 PRO cell (`:1283` proInitial + `:1305` proAdjust) cùng `proMut.mutate` echo sibling từ `bs` server-snapshot, `invalidate()` fire-and-forget (`:1161` không await) → double-Save<refetch wipes sibling. Sev MAJOR data-loss tiềm ẩn, prob thấp. **LEARNED:** display-only capability-flag review = (a) confirm flag-compute KHỚP gate-thật bit-for-bit (đảo-chiều set-lookup must mirror the forward Roles.Contains check) + (b) confirm DEFINE-before-all-consumer-site trong cùng scope + (c) which DTO carries flag determines which UI surface shows badge (flow vs current — both here). For `.join→map` refactor verify separator+empty-case preserved + key-uniqueness backed by a BE validator. SURPRISE: adversarial value here = catching the spec's "KHÔNG migration" claim is FALSE (diff is combined Part1+2+3) — don't trust em-main's scope framing, read the actual changed-set. Tag [s76-part23, pe-budget-badge, display-only-capability-flag, role-set-mirrors-gate, join-to-map-separator-preserved, key-uniqueness-validator-backed, mirror-2app-byte-identical, spec-vs-diff-mismatch, major-race-carryover].
|
||||
- **2026-06-19 (S76 Part1 PE budget MA-TRẬN 3 cột Mig 56 — uncommitted, PASS w/ 1 MAJOR race + 2 MINOR):** Form ngân sách 1-cột→ma-trận [Dự án|PRO|CCM]. Entity +ProInitialAmount/+ProAdjustmentAmount (cột PRO mirror CCM Initial/Adjustment); ProEstimateAmount→LEGACY, Mig 56 Sql() UPDATE migrate idempotent (`WHERE ProEstimate NOT NULL AND ProInitial NULL` — chạy-1-lần-safe). **PASS:** authz fail-closed Forbidden TRƯỚC side-effect 2 handler (PRO=Admin‖Procurement `:90`, CCM=Admin‖CostControl `:160`; Admin nhập cả 2 đúng ý; neither-role blocked); compute `fullAmount`=CCM nếu hasCcm else proFull(ProInit+ProAdj) `:852`, migrate-value flow đúng (legacy ProEstimate→ProInitial→hasPro=true→fullAmount); `fullIsEstimate` `!hasCcm`→`!hasCcm&&hasPro` (improve, no badge khi empty); DTO 17-arg positional khớp def (2 new appended last, build-PASS compiler-checked); Block B 9-row dùng `full` authoritative INTACT; Mig 3-file OK (.cs+Designer+snapshot 18,2); FE 2-app SHA-twin `a93c8aa0`; 32 PeWorkItemBudget tests PASS (+5 S76: set-both-neg, validator neg-initial-fail/neg-adjust-pass, full-proFull-150, neg-proAdjust-70); no mock; anti-fiddle clean. **🟡 MAJOR race (pre-existing pattern, S76 WORSENS):** BudgetCell cross-field echo từ `bs` (server snapshot) KHÔNG local-state — PRO "Ban hành" save gửi `proAdjustmentAmount: bs.proAdjustmentAmount`, "V0" save gửi `proInitialAmount: bs.proInitialAmount`. `invalidate()` fire-and-forget (`:1170` không await refetch). 2 PRO cell nay đồng-cột → click Save cell-2 trong window [onSuccess fires (isPending→false, btn re-enabled) → refetch lands] đè cell-1 về STALE (vd: lưu Ban-hành=100 → ngay lưu V0=50 trước refetch → Ban-hành WIPED null). Trước S76 PRO chỉ 1 số nên window này vô hại; ma-trận 2-cột-PRO làm reachable. Sev MAJOR (data-loss tài-chính tiềm ẩn) nhưng prob thấp (cần double-click <refetch-latency); fix gợi-ý: disable sibling-cell Save khi mut.isPending HOẶC onMutate optimistic-merge HOẶC await invalidate. **🔵 MINOR:** (a) `parseVnd` strip `.` → "1.5"→15 (input cho `.` nhưng VND whole-number nên harmless); (b) stray `fe-user/.claude/agent-memory/implementer-frontend/MEMORY.md` NOT-gitignored (cwd-misland gotcha) → em main ĐỪNG `git add -A`. **LEARNED:** cross-field echo-from-server an-toàn khi 1-field/cột; thành race khi N-field cùng-cột share 1 mutation + fire-and-forget invalidate — window mở SAU isPending=false (btn enable) chứ không phải lúc in-flight; load-bearing = đếm field-cùng-cột share mutation + check invalidate awaited. Tag [s76, pe-budget-matrix, mig56-migrate-idempotent, cross-field-echo-race-worsened, fullIsEstimate-improve, cwd-misland-stray, parseVnd-dot-minor].
|
||||
- **2026-06-18 (S72ter-WIRE Mig 54 cross-stack-wire + verify-fix lane — uncommitted priceMissing, PASS no-new-deadlock):** Complement to S72ter-AUTHZ below (same fix, deadlock-lens). Fix = `priceMissing` old `length>0 && !source` → new `(length===0 || !source)`, 2-app SHA-twin `4d6c89d9`. **No new deadlock — 4-fact:** (1) fix CHỈ THÊM disable-cond `length===0` lên branch đã unreachable-by-invariant (submit-guard `:194` hard-block winnerQuoteTotal<=0 ALL-paths → Ncc candidate luôn ≥1 ở ChoDuyet) ⇒ không sinh lockout mới; (2) có giá→chọn→source set→`priceMissing=false`→nút "Xác nhận" mở→duyệt OK; (3) empty (giả định)→nút khoá + amber `:537` "nhập PRO/CCM hoặc chọn NCC" = lối-thoát RÕ, setter-path KHÔNG phase-gated (mirror-budget) cho nhập giá bất kỳ lúc→candidate xuất hiện→mở lại (no hard-lock); (4) intermediate-approve `shouldPickPrice=false` (chỉ `currentIsFinalApprover||finalizeByCcm`)→nút mở bình thường, khớp BE ApplyApprovedPrice chỉ terminal `:885`+CCM-deleg `:853` (intermediate advance `:870/:893` không gọi). 7-layer threading 0-drop re-confirm (ctrl `:129/:337`→cmd `:462`→handler `:515`→iface `:30`→svc `:47`→ApproveV2 `:822`). OR-of-N `currentIsFinalApprover` true mọi viewer cấp cuối (ComputeLevelStatus `:987` position-based) nhưng nút dialog `disabled=blockedByV2Level`+`!isDisabled&&setTarget` `:310/:320`→non-approver không mở→price-selector vô hại. **LEARNED:** "fix tạo deadlock?" = THÊM-disable lên branch-unreachable-by-invariant không thể sinh lockout; verify lối-thoát = setter KHÔNG phase-gated (giá nhập-được bất kỳ lúc) + amber-message ⇒ user luôn thoát empty-state. Tag [s72ter-wire, verify-fix, no-new-deadlock, escape-hatch-amber, 7layer-0drop].
|
||||
|
||||
- **2026-06-09 (S55 Phase-1 FE visual redesign pre-commit — PASS, 0 blocker, verdict-first survived):** 14 fe-admin files VISUAL/CSS-only (NAMGROUP density + SOLUTION brand). **Independent re-verify GREEN:** `npm run build` fe-admin = ✓ 607ms, 1945 modules, 0 TS err (only PRE-EXISTING warns: CSS @import-order + >500KB chunk + INEFFECTIVE_DYNAMIC_IMPORT realtime.ts — git-confirmed none introduced, @import lines untouched in diff). **Regression Cat1 ALL preserved:** Button cva variant keys (primary/secondary/outline/ghost/danger) + size (sm/md/lg) STABLE — only Tailwind class VALUES swapped, defaultVariants intact (51 call-sites safe); Input/Select/Textarea/Label = `forwardRef`+`...props`+`className` passthrough unchanged, only `cn()` literal; Dialog `{open,onClose,title,children,footer,size}` destructure + sm/md/lg→max-w map intact (+aria-label="Đóng" = a11y GAIN); DataTable `Column<T>` type UNCHANGED (diff starts after type def) — render/sortable/align/width + sort + Pagination props intact, RowActions/RowActionButton purely ADDITIVE; Layout MenuLeaf className-only (brand left-rail via before:), nav/resolver/permission-filter/routing untouched; PhaseBadge phase→ContractPhaseColor/Label map intact; PageHeader/EmptyState/TopBar pure class. DashboardPage data-flow (useQuery/navigate/fmtMoney/BarChart/PhaseBadge) preserved, STAT_TONE+SectionLabel additive, +`cn` import only. **Brand Cat3:** Be Vietnam Pro KEPT (grep: @import:3 + --font-sans:22 + font-family:34 all unchanged — initial blocker RETRACTED after grep); only brand-/slate/semantic colors, 0 off-brand hex/indigo. **a11y:** focus-visible rings present everywhere (brand-500); Label self-documents slate-500 (~4.6:1 AA-pass) chosen over NAMGROUP zinc-400. **Tailwind v4 (^4.2.3)** — `ring-current/15`, `shadow-xs`, slash-opacity all valid v4. **noScopeCreep:** exactly 14 fe-admin, 0 fe-user, 0 BE/src (only noise = frontend-designer/MEMORY.md agent file). **2 MINOR (non-block, a11y-floor):** `text-slate-400` on white for small hint/empty text (DashboardPage hints ~line 50/64, DataTable empty-cell, EmptyState was-400-stays-400) ≈3.5-4:1 — borderline-fail WCAG-AA for <18px, but these are de-emphasized hints not primary content + PRE-EXISTING tone (redesign mostly UPGRADED slate-400→500 on EmptyState desc + Pagination); accept for hint role, revisit if audit. **Learned:** font-drop scare = grep the 3 load-bearing lines (@import/--font-sans token/font-family) BEFORE flagging — diff hunk lower in file ≠ font removed; emit PASS/FAIL line-1 FIRST (gotcha #53 truncation survival, mirror S51/S55). **surprise:** Tailwind v4 `shadow-xs` is real (v3's shadow-sm renamed) — don't flag as typo; v4 slash-opacity on currentColor (`ring-current/15`) is valid. Verdict PASS — safe commit+deploy. Tag [s55-fe, visual-redesign, namgroup-density, verdict-first, regression-clean, slate400-minor].
|
||||
- **2026-06-18 (S72ter Mig 54 AUTHZ+SECURITY lane double-check — uncommitted priceMissing FE-fix + committed 1d86abc re-verify, PASS, 0 issue):** anh giao 3 lane laser (a setters / b CCM-finalize bypass / c controller authz) on commit 1d86abc (deployed Run #313) + 1 uncommitted FE-fix. **Uncommitted diff = 2 LOC product only** (`priceMissing` both apps, SHA-identical `4d6c89d9`) + memory/ledger noise — em main đúng kỷ luật chỉ-touch-2-file. **(a) PASS** — `PeSuggestedPriceFeatures.cs` cả 2 setter ForbiddenException TRƯỚC mọi mutate+SaveChanges (load+NotFound→role-gate `:40-41`/`:109-110`→mutate); role đúng PRO=Admin‖Procurement, CCM=Admin‖CostControl; AppRoles consts tồn tại (`:5,9,10`). Phase-guard cố-tình-thiếu, documented mirror-budget S61 (non-regression). **(b) PASS no-bypass — 3 gate trực-giao chặn non-CostControl finalize-bỏ-CEO, TẤT CẢ throw TRƯỚC `Phase=DaDuyet`(:854):** (1) approver-match `:702-713` non-admin phải ∈ pendingLevel.ApproverUserId else Forbidden → forged-caller-not-at-level KHÔNG tới được finalize block; (2) `finalizeByCcmDelegation:830-851` threshold-null→Conflict / role≠CostControl→Forbidden / `winnerQuoteTotal>=ceoThreshold` strict-`<`→Conflict — 3 throw trước set; (3) block `return` no-fallthrough. `winnerQuoteTotal` recompute server-side từ Suppliers+Quotes.ThanhTien của SelectedSupplier (`:839-847`) KHÔNG trust client; threshold từ DB `aw.CeoApprovalThreshold`. skipToFinal+finalizeByCcm combo safe (skipToFinal `:818` return non-last-slot HOẶC `:797` no-op fall-through last-slot → finalize once, 3 guard vẫn áp). **(c) PASS** — class `[Authorize]:14` → 2 endpoint mới inherit any-auth, fine-grained ở handler Forbidden (gotcha#44-safe KHÔNG class-Policy-overstrict). **FE-fix sound strict-tightening:** old `length>0 && !source` để nút ENABLED khi candidates-empty → click → BE Conflict "Chọn 1 giá chốt"; new `(length===0 || !source)` disable nút khớp amber empty-state `:537` (trước fix message-hiện-cùng-nút-enabled = UX mâu thuẫn). `winnerQuoteTotal:number` non-null → candidates-non-empty thực tế (submit-guard >0), fix thuần defensive nhưng đúng. **LEARNED:** "finalize-bypass?" load-bearing proof = đếm guard giữa caller-entry và state-mutation + xác nhận MỖI guard throw TRƯỚC mutation đầu tiên (đây Phase=DaDuyet) + recompute-vs-trust-client của giá-trị-so-ngưỡng (winnerQuoteTotal server-Sum, không nhận body) → 3 gate độc lập (approver-match ∩ role ∩ amount<threshold) mạnh hơn 1; client chỉ chọn-source-label, BE tự tính amount-vs-threshold. **SURPRISE:** uncommitted-fix chỉ là edge defensive (candidates thực tế luôn ≥1 do submit-guard) nhưng vẫn đáng — nó xoá UX-mâu-thuẫn enabled-button-cùng-amber-empty + chống regression nếu submit-guard nới sau này. Tag [s72ter, mig54-authz-lane, finalize-bypass-3gate-proof, server-recompute-not-trust-client, fe-fix-strict-tighten, phase9-uat-pass].
|
||||
|
||||
- **2026-06-09 (S55 master-data import pre-commit — PASS [em main proxy — reviewer return truncated gotcha #53 before verdict, mirror S51]):** Reviewed Mig 48 `AddProjectMasterFields` (Project +4 nullable col Year/Investor/Location/Package) + `SeedRealMasterDataAsync` (62 Project+71 WorkItem+3 Supplier per-code idempotent ungated) + FE ProjectsPage form +4 ×2 app. Reviewer ran 293s/31-tools nhưng truncated mid-thought (nghi cached-binary 2.76s build → muốn forced clean rebuild + Project tests). **Em main COMPLETED đúng việc nó định làm:** `dotnet test SolutionErp.slnx` = clean rebuild + **216 PASS** (58+158, 0 fail/skip) → giải tỏa cached-binary concern (test = fresh build). 10 dims GREEN: Mig Up=4 AddColumn/Down=4 DropColumn reversible + 3-file; seed 62/71/3 0-dup; per-code idempotent ungated line 118 (reaches prod); FLOCK01 collision skip-demo-wins; FE↔BE 4 nullable both sides (tránh S51 mismatch); test-file compile-fix +4 null legit; gotcha #57 index untouched; **runtime Dev proof** (data landed, Investor col populates). 0 rogue write (read-only respected, git clean of code). **Learned:** long adversarial review return truncates (gotcha #53) → reviewer nên emit PASS/FAIL verdict SỚM (trước deep re-verify) để sống sót truncation; em main complete được đúng pending-check (clean dotnet test) deterministic. Verdict PASS — safe commit. Tag [s55, master-import, em-main-proxy-truncate, runtime-dev-proof].
|
||||
- **2026-06-18 (S72 Q2 phản-biện CONFIRM finding isReal=false / not-an-issue — Mig 54 isSystem-exempt dead-branch):** anh giao bác-bỏ finding "isSystem miễn-chọn-giá AN TOÀN/dead-code". Cố refute ×3 angle, KHÔNG bác được → finding ĐÚNG mọi điểm. (1) `IPurchaseEvaluationWorkflowService.TransitionAsync` = **DUY NHẤT 1 caller** backend-wide (`PurchaseEvaluationFeatures.cs:505` human handler, throws Unauthorized nếu UserId null `:499`, pass `currentUser.UserId` non-null `:508`) → `isSystem` (`:54` cần actorUserId null) LUÔN false trên PE-path. (2) `SlaExpiryJob:63` inject `IContractWorkflowService` (KHÔNG PE) + query `db.Contracts` only → caller AutoApprove duy nhất KHÔNG chạm PE. (3) `ApplyApprovedPriceOnFinalize` (`:908`) chỉ gọi từ `ApproveV2Async` (`:853,885`) reachable chỉ qua approve-block `:243` gate `decision==Approve`, còn isSystem cần `AutoApprove` → mutually-exclusive (lý do độc lập #2). (4) KHÔNG PE SLA hosted-service (chỉ SlaExpiryJob/Contract + ItTicketSlaJob). (5) non-admin AutoApprove tới ChoDuyet → `throw Conflict :275` (no alt-finalize bypass price). Test header `PeApprovedPriceFinalizeTests.cs:27-31` tự-ghi OBSERVATION report-em-main KHÔNG-fix + reflection-invoke isolation. **Finding line# lệch** (cite 911-916/SlaExpiryJob 76,100, actual 908-923) nhưng substance khớp. Dead-branch harmless defensive-return. **LEARNED:** "dead-code an-toàn?" load-bearing proof = đếm CALLER của method chứa branch (grep `\.TransitionAsync\(` + verify mỗi caller's service-TYPE qua DI) — single-human-caller + actorUserId-non-null-invariant kills isSystem độc lập với decision-gate; 2 lý do trực-giao mạnh hơn 1. Tag [s72, q2-phan-bien, isSystem-dead-branch, single-caller-proof, not-an-issue, confirm-finding].
|
||||
|
||||
- **2026-06-08 (S54 ItTicket reassign authz Admin-OR-dept-IT cross-stack pre-commit — PASS, 0 blocker, gotcha #44 disarmed correctly):** Controller `/assign` hạ `[Authorize(Roles="Admin")]`→`[Authorize]` any-auth; authz moved INTO `AssignItTicketHandler` (Admin-OR-IT Forbidden + assignee-must-IT Conflict) + new `GetAssignableItStaffQuery` capability endpoint. **Independent re-verify GREEN:** `dotnet test SolutionErp.slnx` = **216 PASS** (58 Dom + 158 Infra, Failed:0 Skipped:0, +13 matches 203→216 claim exactly) · fe-admin + fe-user `tsc -p tsconfig.app.json --noEmit` BOTH exit 0 clean. **#2 CHÍ MẠNG role-string "Admin" CONFIRMED REAL (full chain traced):** `AppRoles.Admin="Admin"` literal (AppRoles.cs:5) → SeedRolesAsync `Name=roleName` (DbInit:1485) so DB Role.Name=="Admin" → Identity GetRolesAsync returns NAMES → JwtTokenService:32 `new Claim(ClaimTypes.Role, r)` → CurrentUserService:30-31 `FindAll(ClaimTypes.Role)` → `cu.Roles.Contains("Admin")` CORRECT. "QTV" (DbInit:1458 RoleLabels) = ShortName DISPLAY label only = decoy. Program.cs JWT sets NO `RoleClaimType` override (ClaimTypes.Role symmetric write/read). Same proven pattern as every existing Roles="Admin" endpoint. **Bypass airtight:** controller any-auth → handler sole gate; guard `if(!isAdmin && !(itDeptId is Guid mine && myDeptId==mine)) Forbidden` fail-CLOSED when itDeptId null (non-admin blocked; admin still passes role-branch). `Department.Code=="IT"` IS seeded (DbInit:2082 "Phòng CNTT") so live non-null. **Capability 0-leak:** non-auth → `{canReassign:false, staff:[]}` (no name leak), `[Authorize]` any-auth → 0 silent-403. **Defense-in-depth intact:** FE nút `{canReassign&&}` but PUT still hits handler guard → 403/409 → `onError: toast.error(getErrorMessage(err))` surfaces (NOT swallowed). **FE SHA256 page 4bcaf2f IDENTICAL both apps** (types.ts correctly NOT identical — admin AttendanceReportDto vs user HrDashboardDto diverge below; added AssignableStaff block byte-identical); old fe-user page was read-only kanban (NO app-specific logic lost — diff purely additive); fe-user has all imports (apiError.getErrorMessage, Dialog size/footer/onClose props match, Select passthrough, sonner). **Test 13-fact NOT happy-path:** Case5 Forbidden side-effect assert `AssignedToUserId.Should().BeNull()` (red-able by contrast vs Case6/7 same-handler success), Case3/3b empty-staff 0-leak, Case8 Conflict msg-exact. prove-by-contrast ĐỦ CHẶT (partition non-IT-throws vs IT/admin-succeed identical handler). **1 MINOR defer:** assignee-must-IT NEW vs old handler (git HEAD: old allowed admin→ANY active user); itDeptId-null → even admin Conflict — fail-closed acceptable+spec-requested, cosmetic (prod IT-dept seeded). **Learned:** authz role-string review = trace FULL chain const→seed Name→GetRolesAsync(names not codes)→Claim(ClaimTypes.Role)→reader AND grep JWT cfg for `RoleClaimType` override (none=symmetric) — display-code (QTV) in RoleLabels/ShortName dict = classic decoy. **surprise:** moving authz controller→handler is the CORRECT gotcha #44 fix (not a smell) when paired with BE-computed capability flag for FE gating + handler as sole gate. Verdict PASS — safe commit. Tag [s54, it-ticket-reassign-authz, gotcha44-disarmed, role-string-chain-verified, cross-stack-clean].
|
||||
- **2026-06-18 (S72bis Mig 54 RE-REVIEW commit 1d86abc — anh 4 regression-Q a/b/c/d focus, PASS, 0 finding):** Independent 2nd pass on SAME commit as S72 entry below, anh asked 4 targeted Qs. **(a) AUTO→OPT-IN regression — in-flight + V1 SAFE:** S69 auto-finalize block REMOVED; in-flight V2 phiếu mid-ChoDuyet carry NO new flags (come from NEW request not stored state) → intermediate levels advance normal (no ApplyApprovedPrice call line 870/894), terminal calls ApplyApprovedPrice → final approver picks price (candidate guaranteed exist, see d). CCM below-threshold WITHOUT tick now advances to CEO (intended safer, no strand). **V1 legacy `ApproveV1LegacyAsync` signature does NOT receive new params + terminal line~990 does NOT call ApplyApprovedPrice** → V1 finalize 100% unchanged, ApprovedPrice* stays null per entity comment. Tests cover: NoFlag→advance-CEO, AtLastSlot-no-double, all 3 fail-closed guards throw-before-mutate. **(b) Mig 54 safe:** 5-col additive-nullable, 0 backfill, 0 lock (AddColumn nullable=metadata-only SQL Server no rebuild); Down() drops all 5 reversible; 3-file rule OK (.cs+Designer+snapshot all 5 cols); EF config HasPrecision(18,2)×4 + HasMaxLength(20) match migration types. **(c) DTO positional OK:** 7 fields inserted between CeoApprovalThreshold↔ApprovalWorkflowId in BOTH record def + construction, same order (ProMin/ProMax/Ccm/ApprovedAmount/ApprovedSource/canEditPro/canEditCcm), types match (5 nullable + 2 bool); build-PASS confirms compiler-checked positional. **(d) NO deadlock — decisive:** submit-guard `PurchaseEvaluationWorkflowService.cs:174-216` enforces (ALL paths incl Admin/system) winnerQuoteTotal>0 (line194 "chưa có giá chào thầu" if<=0) → Ncc candidate `{amount:winnerQuoteTotal}` ALWAYS present+positive at final approval → `priceCandidates.length>=1` always → amber "Chưa có giá nào" (length===0) is DEAD UI unreachable → human always can pick ≥Ncc → priceMissing disables btn til pick → BE never gets null human-path → no Conflict-loop. `winnerQuoteTotal` BE `Sum()` over empty=0m (never null), FE type `number` non-null. **OR-of-N currentIsFinalApprover** = `lastFlowLevel.status==='Current'` true for EVERY viewer at last level (position-based BE ComputeLevelStatus:987), BUT approve buttons `disabled=blockedByV2Level` + onClick `!isDisabled&&setTarget` (line310-321) → non-approver can't open dialog → price-selector-for-all harmless. **FE mirror:** PeWorkflowPanel+PeDetailTabs byte-identical 2 apps (hash df2975a/ab08dad); type files differ pre-existing BUT Mig54 fields diff-identical. Setter handlers fail-closed Forbidden-before-side-effect, PRO Min<=Max validator, NO phase-guard (documented intentional mirror-budget S61). **LEARNED:** for "deadlock?" Q the load-bearing proof is tracing the SUBMIT-guard invariant (winnerQuoteTotal>0) forward to the finalize candidate-set — the FE dead-UI branch (length===0) is provably unreachable BECAUSE submit already rejected zero-price phiếu; never assess FE button-enable in isolation. **SURPRISE:** isSystem-exempt in ApplyApprovedPrice = dead via public ApproveV2Async (needs decision==AutoApprove + PE has no SLA-job) — test-specialist self-flagged OBSERVATION header, honest. Tag [s72bis, mig54-reReview, regression-Q-abcd, submit-guard-invariant-forward-trace, no-deadlock-proof, v1-untouched, or-of-n-safe].
|
||||
|
||||
- **2026-06-08 (S52-late Task C ItTicket admin reassign + Task D AttendanceReport menu-key pre-commit — PASS, 0 blocker):** Migration-FREE (menu = idempotent DbInitializer seed). 5 prod files: MenuKeys.cs (const+All[]), DbInitializer.cs (1 seed tuple), fe-admin {menuKeys.ts, Layout.tsx staticMap, ItTicketsPage.tsx}. **Independent re-verify GREEN:** `dotnet build SolutionErp.slnx` 0-warn/0-err · `npm run build` fe-admin tsc-b+vite OK (1945 modules, only pre-existing CSS @import + >500KB + ineffective-dynamic-import warns). **menuKeyMatchOk:** `"Off_AttendanceReport"` byte-identical 4 places (MenuKeys.cs:125 const == menuKeys.ts:68 == seed parent key == Layout staticMap:87); seed leaf parent=`MenuKeys.Off` order=8 icon=`FileBarChart` (verified valid lucide alias `FileChartColumn as FileBarChart` — getIcon resolves via Icons[name]); App.tsx route `/attendance/report → AttendanceReportPage` PRE-EXISTING committed S52 (6a66429, no diff, 170-LOC real page not stub) — Layout maps to REAL route. `types/menu.ts` correctly NOT mirrored (`key:string` not typed union). admin auto-perm via `SeedAdminPermissionsAsync` iterating `MenuKeys.All` (DbInit:1917, idempotent Contains:1919); new-leaf-on-existing-DB confirmed (upsert loop:1845-1862 TryGetValue-miss→Add). **reassignCorrect:** FE PUT `/it-tickets/{id}/assign` body `{assignedToUserId}` MATCHES BE record `AssignItTicketBody(Guid AssignedToUserId)` (ItTicketsController:42); endpoint `[Authorize(Roles="Admin")]` (:34) under class `[Authorize]` — admin-app FE calling = correct; user-list reuses EXISTING `/users` GET (`PagedResult<UserDto>` items{id,fullName,email}, no new BE endpoint, lazy `enabled:target!==null`); 204 NoContent handled (no body-parse); `invalidateQueries(['it-tickets'])` on success; handler sets BOTH AssignedToUserId+AssignedToFullName (WorkflowAppsFeatures:467-468) + validates assignee IsActive→NotFound, no try-catch (GlobalExceptionMiddleware). **feUserUnchanged:** `git diff -- fe-user/` EMPTY (Task C = fe-admin-only divergence, documented top-comment ItTicketsPage:3-5). **noScopeCreep:** git status prod = EXACTLY the 5 expected files, agent-memory noise ignored, no new migration, no BE beyond MenuKeys+DbInitializer, ItTicketsPage diff 98+/5- all Task-C-scoped (imports+state+mutation+Pencil-btn+Dialog), 0 mock/alert markers. **Learned:** menu-key wiring = verify byte-identity across the FULL mirror set (BE const + BE All[] + seed parent + FE menuKeys + FE staticMap) + confirm the target route actually EXISTS (grep App.tsx) — a staticMap entry pointing to a non-existent route silently drops the leaf (gotcha #50). **surprise:** lucide `FileBarChart` is a deprecated-alias (re-exported from FileChartColumn) but still valid — d.ts grep confirmed before flagging. Verdict PASS — safe to commit. Tag [s52-late, it-ticket-reassign, attendance-report-menukey, menukey-mirror-5way, gotcha44-disarmed, gotcha50-disarmed].
|
||||
- **2026-06-18 (S72 Mig 54 PE giá-đề-xuất + CCM-finalize OPT-IN — financial go-live review, PASS, 0 blocker):** Pre-commit uncommitted diff 17-file (+922/-102), DUYỆT TÀI CHÍNH go-live thứ Hai. 3 nhóm: ① giá đề xuất PRO(Min/Max)+CCM(1 giá) setter role-gate + người-duyệt-cuối chọn giá CHỐT (`ApplyApprovedPriceOnFinalize`); ③ CCM-finalize ĐỔI AUTO(S69)→OPT-IN ô-tích-tay `finalizeByCcmDelegation`. **Threading 7-lớp KHỚP** (body→Send→command→handler→interface→service→ApproveV2; controller `:129` + TransitionPeBody `:337-341` + command `:462-465` + handler `:515-517` + iface `:30-34` + svc sig `:47-49`) — 0 lớp drop param (bẫy "F1+F2 wire fail 2 ngày" né). **③ fail-closed order verified** (`PurchaseEvaluationWorkflowService.cs:830-867`): flag=false→skip finalize advance-CEO (test 1a, đổi-chính); flag=true check THEO THỨ TỰ threshold-null→Conflict(`:832`) / role≠CostControl→Forbidden(`:835`) / `winnerQuoteTotal>=ceoThreshold` strict-`<`→Conflict(`:849`) TRƯỚC set DaDuyet — 0 lỗ CCM/khác bỏ CEO. **① ApplyApprovedPriceOnFinalize gọi CẢ 2 nhánh DaDuyet** (terminal `:885` + CCM-deleg `:853`); human null-giá→Conflict, isSystem miễn, source∈{Ncc,ProMin,ProMax,Ccm} whitelist. **Setter authz** (`PeSuggestedPriceFeatures.cs`) Forbidden fail-closed TRƯỚC side-effect, đúng role (Pro=Procurement `:53`, Ccm=CostControl `:109`, Admin cả 2). **Cross-stack FE/BE field-name khớp** camelCase (finalizeByCcmDelegation/approvedPriceAmount/approvedPriceSource). **FE currentIsFinalApprover** = `lastFlowLevel.status==='Current'` (BE ComputeLevelStatus `:978-991` = pointer==last) — OR-of-N: group cấp cuối 1 entry → "Current" cho mọi viewer NHƯNG nút mở-dialog disable bởi `blockedByV2Level` (`:310,321`) khi actor∉approvers → người-không-phải-cuối KHÔNG thấy bộ chọn. priceMissing disable Xác nhận đúng. **Migration 3-file OK** (Mig 54 additive-nullable, Designer 5 col, snapshot). **Tests 334 PASS** (45 Dom+289 Infra, +28: PeCcm 6→11 + PeApprovedPrice 10 + PeSuggestedSetter 13). **3 MINOR non-block:** (a) `ApplyApprovedPriceOnFinalize` TRUST client `amount` — KHÔNG cross-check amount==stored-value-của-source (snapshot-semantic CHỦ ĐÍCH per comment; field display/audit-only, grep xác nhận KHÔNG drive Contract-from-PE value → low-sev); (b) edge winnerQuoteTotal==0 candidate amount=0 hợp lệ (submit-guard ép >0 nên unreachable thực tế); (c) **stray `fe-user/.claude/agent-memory/implementer-frontend/MEMORY.md` NOT-gitignored** (sub-agent cwd-misland gotcha) — em main ĐỪNG `git add -A` (chỉ add file cụ thể) + reconcile→canonical. **LEARNED:** combined-flag probe (skipToFinal+finalizeByCcmDelegation) SAFE — skipToFinal `return` (`:818`) trước finalize khi không-last-slot, last-slot no-op fall-through finalize-once (no double, guard vẫn full). For financial-approve review the 2 load-bearing proofs: (1) fail-closed guard order = throw TRƯỚC mọi set Phase=DaDuyet (reload-assert ChoDuyet trong test) + (2) trusted-client-amount chỉ MINOR khi field không feed downstream money (grep consumer = DTO-only). **SURPRISE:** isSystem-exempt branch trong ApplyApprovedPriceOnFinalize = defensive/dead qua public ApproveV2Async (approve-branch gate decision==Approve, isSystem cần AutoApprove; PE no SLA-job) — test-specialist tự ghi OBSERVATION header, honesty tốt. Tag [s72, mig54-pe-price, ccm-finalize-opt-in, fail-closed-order, trusted-client-amount-minor, currentIsFinalApprover-or-of-n, cwd-misland-stray, financial-golive-pass].
|
||||
|
||||
- **2026-06-08 (S53 gotcha #57 EXT Mig 47 — Master catalog filtered-unique pre-commit — PASS, 0 blocker, Smart Friend clean):** 4th/5th/6th cumulative gotcha #57 (after Holiday Mig 43 S45 + HRM ×3 Mig 45 S51). 3 Master configs (Department:18/Project:19/Supplier:24) Code unique index `.IsUnique()` → `+.HasFilter("[IsDeleted] = 0")` + Mig 47 (3-file) + 3 new tests. **Independent re-verify ALL GREEN:** build SolutionErp.slnx 0-warn/0-err · **full suite 203 PASS** (58 Dom + 145 Infra, Failed:0 Skipped:0, +3) · 3 new tests run isolated 3-passed-0-skipped. **Cat correctness:** filter string byte-identical to HolidayConfiguration:18 (xxd `5b 49 73 44 65 6c 65 74 65 64 5d 20 3d 20 30` — spaces around `=`, not guessed); index STAYS `unique:true` (2 active same-Code still violate — active uniqueness preserved); Supplier Type index (:25) UNTOUCHED non-unique unfiltered (snapshot:3590 bare). Mig Up=3×Drop+3×Create-filtered, Down=3×reverse-unfiltered (reversible). Snapshot+Designer both show filter on all 3 Master Code idx. **Test NOT tautology:** seeds IsDeleted=true row → real Create*CommandHandler (app-check `AnyAsync(Code==req.Code)` thru HasQueryFilter !IsDeleted PASSES) → asserts NotThrow + active-count==1 + IgnoreQueryFilters all==2; RED-before confirmed (3 failed SqliteException UNIQUE on unfiltered). Cmd signatures match test calls (Project 7-arg/Supplier 9-arg/Dept 4-arg). **noScopeCreep:** git status = exactly 3 configs + snapshot + Mig47 (2 untracked) + 1 test + 2 MEMORY; no FE, no extra mig, no stray. Mig 47 latest in seq (after Mig46 ItTicket SLA). **Learned:** cookie-cutter EXT of proven pattern → discriminator = byte-compare filter string (xxd) vs canonical sibling + verify index still unique (filter must NARROW scope not DROP uniqueness). app-level dup-check existence = the test premise; verify handler actually has `AnyAsync(Code)` else test premise false. **surprise:** implementer claimed "2 pre-existing DocxRenderer warnings" but clean incremental rebuild = 0 warn (unrelated, non-issue). Verdict PASS — safe commit. Tag [s53, gotcha57-ext, mig47, master-catalog, smart-friend-clean].
|
||||
- **2026-06-18 (S71 FINALIZE double-check H9+H10+checklist — lens R3 cross-cutting+residuals, GAPS-FOUND, 3 completion-gap):** anh giao "hoàn chỉnh lại TOÀN BỘ" (not just Part C). Verdict GAPS-FOUND (no defect/no-bug — all gaps are deferred-incompleteness anh now wants closed). **PASS items:** (1) **Containment model đồng-bộ MỌI file** — 4 owning (`_ledger.md:4`/`hmw.js:89,113`/`workflows/README:38`/`runs/README:78`) + agents/README:162 + harvest-curator:52 + tooling-auditor + session-end/start ALL repoint Harness-10 "tracked-change NGOÀI run-folder+code-disjoint=vi-phạm". 0 file giữ old B6 operative. (agents/README:8 wave-mode = frozen 06-07 chronology, OK; harness_123 user-mem:13 = stale FE-ref noted below.) (2) **Frozen-evidence INTACT** — `git diff --name-status f36aab8^..HEAD`: broadcasts/_index.md additive-2-rows + 2 outbox NEW (A), 0 modify; harness-2 adap-report/error-ledger/pre-S70 sessions NOT in changed-set. (3) **3 h10 run-folder** = run.md+harvest/ complete, sub-md/ only .gitkeep (EXPECTED — read-only subs scribed to harvest). ledger 2-beat all CLOSED, 0 orphan. (4) **gitignore** runs/=NOT-IGNORED, wave-*/agent-teams=IGNORED ✓. (5) **email content_sha256 e5f09d57c22e MATCH** body-lstrip, outward-VN full-grammar Cat-6 PASS. **🔴 3 COMPLETION-GAP (em-main fix to "hoàn chỉnh"):** (G1 HIGH) **over-cap curate-debt** — reviewer/MEMORY.md **33782B** (>30720 soft + >25600 auto-inject; spawn already truncated ~8KB HOT) + investigator-codebase **29819B** (>25600). memory-budget.json `measured` STALE (reviewer 24795/inv 24052 = S70 snapshot) → re-run `scripts/measure-agent-memory.ps1` + curate L1→L2 (additive, archive/ + _INDEX exist). (G2 MED) **stale memory claims** — Harness-9 user-mem line14 "cả 4 <25KB (đóng P1 curate-debt)" now FALSE post-S71; harness_123 user-mem:13 describes wave-mode as operative (superseded). (G3 MED) **NO Harness-10 user-memory** — biggest structural change (wave→tracked-runs + containment flip) has 0 feedback/project memory; 3 lessons uncaptured (engine-no-fs→em-main-scaffold-fragile · custom-workflow-needs-delta-guard-race · check-ignore-exit-trap). **2 MINOR-info:** check-ignore exit-trap EXPLANATION imprecise in gitignore:96-98 + email#3 ("exit 0 for BOTH") — plain `check-ignore` actually exits 1 for negation (only `-v --no-index` gives 0-for-both); the recommended COMMAND still works correct → low-sev, email frozen. **Learned:** "complete the whole thing" audit must check budget.json measured_bytes vs DISK (snapshot drift re-accumulates after each over-cap session); honest-self-disclosure (STATUS+email both flag the over-cap) ≠ done — disclosure is what anh asks to CLOSE. **surprise:** I am ADDING to the very curate-debt I'm flagging (this entry pushes reviewer further over-cap) — G1 curate must run NOW. Tag [s71, finalize-r3, over-cap-curate-debt, stale-memory-claim, missing-h10-usermem, gaps-found].
|
||||
|
||||
- **2026-06-08 (S52 P11-E AttendanceReport + P11-F MaTicket codegen pre-commit — PASS, 0 blocker):** Migration-free (no schema). Independent re-verify: build 0-err · **191 PASS** (58 Dom + 133 Infra, +5: 3 ItTicketCodeGen + 2 AttendanceReport) · fe-admin `tsc --noEmit` exit 0. **Cat3 gotcha #44 attack DISARMED:** `[Authorize(Roles="Admin")]` ×2 report endpoints — verified `AppRoles.Admin = "Admin"` literal (AppRoles.cs:5) == attribute string == FE `user?.roles.includes('Admin')`; "QTV" (DbInit:1454) = display-code NOT role-name; pattern proven (Catalogs/HrmConfigs identical). **Cat3 camelCase contract MATCH** field-for-field BE record PascalCase→FE interface (year/month/rows/grandTotal*/userId/fullName/ot*) — ASP.NET default camelCase, no Program.cs override. **BE handler correct:** `.Year/.Month` in IQueryable (EF-translatable DateTime), `.DayOfWeek`+holidaySet only AFTER `.ToListAsync()` (in-memory) — IQueryable-translation attack handled; holiday-check BEFORE weekend BEFORE weekday (test 2026-06-01 Mon-but-holiday proves override); `DateOnly.FromDateTime` correct (Holiday.Date=DateOnly); OtPolicy fallback 1.5/2.0/3.0; `IsDeleted` via AuditableEntity all 3 entities. Exporter mirrors ContractExcelExporter, ClosedXML 0.105.0, `RenderResult(Content,FileName,ContentType)` ctor order correct, DI registered. **MaTicket codegen:** `e` untracked at codegen time → inner SaveChanges persists ONLY sequence row, no double-insert; gen-on-Create (kanban no-workflow) vs Leave/OT gen-on-Submit — semantically correct; git-show confirms MaTicket was ALWAYS null pre-P11-F (closes gap). **1 MINOR (informational, defer):** sequence-gap-on-failure — codegen commits seq in own Serializable tx BEFORE `Add(e)+SaveChanges`; ticket-insert fail → burned IT/2026/NNN gap. NOT new defect = identical to existing Leave/OT pattern (project-wide accepted trade-off, cosmetic). MyAttendancePage MIRROR divergence intentional+documented (fe-user untouched, §3.9 OK). 0 mock markers. **Learned:** when spec NAMES an attack vector (gotcha #44 role-string), verify the LITERAL const value not just attribute presence — "QTV" display-code was the decoy; role-name match is the real check. **surprise:** Bash tool = bash not PowerShell (Select-String fails exit 127 → use grep). Verdict PASS — safe to commit. Tag [s52, p11ef, attendance-report, mat-codegen, gotcha44-disarmed].
|
||||
|
||||
- **2026-06-08 (S51 P11-C Vehicle+Driver + gotcha #57 pre-commit — PASS, 1 MAJOR caught) [em main proxy — reviewer return truncated gotcha #53]:** Reviewed Mig 44 (Vehicle/Driver catalog) + Mig 45 (filter 3 HRM unique) + FE KIND_CONFIG +2 + 5 tests (186 PASS). Independent build+test re-verify GREEN. **CAUGHT 1 MAJOR (Cat 3 cross-stack contract):** Driver FE↔BE required-field mismatch — FE render phoneNumber/licenseNumber/licenseClass OPTIONAL nhưng BE validator `NotEmpty()` + EF `.IsRequired()` NOT NULL → empty submit = 400/500. Root = inconsistent em-main brief (BE "mirror Vehicle"=required vs FE spec quên required). Fix: FE +`required:true` (align BE all-required như Vehicle). Cats khác clean (Mig diff clean, Authorize Roles=Admin writes, gotcha #57 grep-complete 3 HRM, DbInitializer idempotent + #51 infra-gated, SHA256 mirror, no copy-paste Driver↔Vehicle). **Learned:** parallel fan-out (BE∥FE file-disjoint) → bất kỳ inconsistency trong SHARED em-main contract chỉ lộ lúc integration; green tests ≠ correct contract (no test chạm empty-optional path). reviewer = the net. **surprise:** transient mid-deploy bundle hash (cicd lesson) + reviewer self-truncate trước khi ghi MEMORY → em main proxy. Verdict PASS post-fix. Tag [s51, p11-c, gotcha57, contract-mismatch-catch].
|
||||
|
||||
- **2026-06-07 (S49 Harness 1/2/3 adopt pre-commit — PASS all 3, no blocker):** Governance/infra adopt (no product code, no test impact). VERIFIED: H1/H2 = 2 sub scope-DISJOINT + tools `[Read,Grep,Glob,Bash+4RAG]` NO store_memory/Write (INFORM-only); genuinely **TAILORED not copy-paste** (SE 4-RAG vs AI_INFRA 2-RAG · dropped effort:max + agent-ops-monitor/sister · Fidelity→SE `reviewer`). H2 5-trục in harvest-curator.md + session-end §L.b(f). H2 wave-mode hmw.js mirror AI_INFRA + **B6 `git check-ignore` VERIFIED** (wave-*/+agent-teams/ ignored · hmw.js/README tracked). H3 self=`se` complete substitution · **SHA256 canonical formula byte-identical send==check** · 13 .gitkeep exact · adap-apply base-path `outbox\all\`. honest nấc executed-file/verified-runtime-PENDING. G-015 scan = 6 hits ALL negating ("KHÔNG enforced") = correct honesty. **1 MINOR (non-block):** README:11/18 "7-agent" ASCII diagram = **PRE-EXISTING** drift (git diff proved work này chỉ touch load-bearing title/decision-tree/tool-grant/matrix; diagram predates S47 frontend-designer) → tooling-auditor H1 designed-to-catch = self-validating adoption. **learned:** `git diff base..head` = discriminator introduced-defect vs pre-existing-drift (đừng đổ lỗi work mới cho drift cũ); name-collision tailor-verify = diff frontmatter AI_INFRA-canonical vs SE-instance. **surprise:** mojibake scan false-pos trên "ĐÃ" (U+00C3 = valid VN uppercase, KHÔNG double-encode → verify codepoint in-context trước flag); broadcast floor "12 .gitkeep" UNDERCOUNT (correct=13 incl `all/` adap-channel — em main đúng). Verdict PASS, safe commit + restart. Tag [s49, harness-adopt, governance, max-clean].
|
||||
|
||||
- **2026-05-30 (S43 P11-B LeaveBalance pre-commit — PASS, Max no-truncate):** 14 file (LeaveBalance entity+config+Mig42 + Features + Controller + deduction hook + Create/Update LeaveType guard + embed balance + FE×4 + tests). 154 PASS (130→154). **Deduction exactly-once VERIFIED** (terminal else only, guard Status!=DaGuiDuyet chặn re-approve; advance/reject/return no-deduct). **FK invariant fully closed** — grep 2 write site LeaveTypeId (Create + UpdateDraft) cả 2 guard AnyAsync→Conflict, bogus type không thể tới terminal FK insert. Embed balance = RequesterUserId (approver thấy đúng người tạo). admin `[Authorize(Roles=Admin)]`. **2 MINOR defer:** concurrency lost-update UsedDays (no RowVersion — human-sequential accept) · stale line-num comment. Verdict PASS. Tag `[s43, p11b-leavebalance, max-clean]`.
|
||||
- **2026-05-28 (S35 G-H2 BE CRUD 16 endpoint pre-commit — PASS, Smart Friend 8× CLEAN):** 2 NEW file `HrmConfigFeatures.cs` 439 + Controller 137. build clean, 130/130 PASS. Cat1: 0 mock, 8 ConflictException (Holiday Update composite `(Year,Date)` BOTH fields). Cat3: class `[Authorize]` + 12 per-action `[Authorize(Roles="Admin")]`. Cat5: 8 Validator MaxLength MATCH EF source (Code=50 not spec 20). **2 MINOR defer:** ListHolidays no IsActive filter (inconsistent sibling) · OtPolicy "1 active unique" NOT enforced handler (G-P1 ambiguous nếu 2+ active). Verdict PASS. Tag `[s35, smart-friend-8x-clean]`.
|
||||
- **2026-05-26 (S33 Plan B G-H1 Phase 2 pre-commit — PASS, Smart Friend 6× CLEAN):** 17 file (3 BE + 6 FE new + 6 mod + 2). SHA256 mirror 3 file IDENTICAL admin==user. 5 endpoint real mediator.Send 0 mock. Mig 34 `AddEmployeeProfiles` 7 table UNIQUE indexes + FK Cascade. SeedDemoEmployeeProfiles NOT gated DemoSeed (gotcha #51 ✓). gotcha #50 Layout staticMap mirror ✓. **3 MINOR defer:** EmployeeCode race SERIALIZABLE low-risk · Update 3 bool not nullable (partial reset) · Delete DateTime.UtcNow direct. Verdict PASS. Tag `[s33, hrm-mig34, smart-friend-6x]`.
|
||||
- **Smart Friend cumulative 8× CLEAN:** (1) S22 #44 silent-403 · (2) S25 #48 SQLite tie-break · (3) S29 password ≥12 · (4) S29 ApplicableType cross-module · (5) S33 BW test · (6) S33 Plan B Phase 2 · (7) S35 FE forms · (8) S35 G-H2. Plus 9× G-O2 (S36, em không track ở đây). 2 MAJOR catches total (S29 password + S29 ApplicableType); rest clean với MINOR defer.
|
||||
- **Archived S29-S33 detail + S32 startup → `archive/2026-05-q2.md` + git d2f52ba (S40 curate):** S33 Plan C B-Wrap 9/9 [Fact] verify · S33 startup drift audit (CLAUDE.md SEVERE → patched S40) · S32 wrap/startup standby · S29 wrap 2 MAJOR catch detail. KEY absorbed in bug patterns + Smart Friend cumulative above.
|
||||
|
||||
---
|
||||
- **2026-06-18 (S71 Harness-10 adap run-trace convention — Stage-3 REVIEW lens R1 frozen-evidence+containment, PASS, 0 blocker):** Governance/infra-only (wave-folder→run-trace `.claude/workflows/runs/<run-id>/` TRACKED). 10 modified (8 H10 + investigator MEMORY residual + CLAUDE.md pre-existing) + 1 untracked `runs/`. NO product/test/csproj/package.json/migration → test baseline 306 untouched, deps N/A. **Spec path trap:** spec said `runs/...` but actual `.claude/workflows/runs/...` (verify disk, không tin claim path). **R1 verify ALL PASS:** (1) **Frozen-evidence 0-touch** — `git status --porcelain` on broadcasts/** · adap-reports/2026-06-07-harness-2 · error-ledger · sessions/* · STATUS · HANDOFF · `*/archive/*` ALL empty = none touched. (2) **Containment wording đồng-bộ 4 chỗ** — `_ledger.md:4` ↔ `hmw.js:89/113` ↔ `workflows/README:38` ↔ `runs/README:78` ALL = "tracked-change NGOÀI run-folder + code-disjoint = vi-phạm" (model thay Harness-2 B6 "mọi tracked = vi-phạm"). (3) **gitignore exit-code-trap** — `check-ignore runs/.../run.md && echo IGNORED || echo NOT`=NOT (re-included via `:83 !.claude/**`); `wave-x/wave.md`=IGNORED (legacy `:93` kept); trap-note PRESENT gitignore `:96-98`. No new ignore rule shadows runs/. **residuals verified as-claimed:** investigator MEMORY +6 (3 S71 diary, 29819B≈29.8KB over-cap, race artifact closeout); CLAUDE.md pure test-count 263→306 flush. hmw.js `node --check`=PARSE-OK, `args.run` w/ legacy `args.wave` fallback `:91`, `sub-md/` subdir `:103`. harvest-curator DEDUP axis (sha/substring before APPEND); session-end idempotent VERIFY-not-re-APPEND; session-start orphan-scan. 6× `.gitkeep` present. **1 MINOR (non-block, actionable):** runs/ currently UNTRACKED (`git ls-files` empty, `?? runs/`) = tracked-ELIGIBLE not-yet-committed; docs say "TRACKED" = post-commit steady-state — em main MUST `git add runs/` in SAME commit else run-trace invisible to git-diff audit model depends on. **Learned:** "TRACKED" containment = 2-level — check-ignore NOT-IGNORED (eligible) vs `git ls-files` (committed); model only works after `git add`. **surprise:** internal var `const wave = (A.run&&A.run.dir)?A.run:...` keeps name `wave` but reads `A.run` first — cosmetic-only, downstream identical (not bug). Verdict PASS — safe commit (git-add-runs/ caveat). Tag [s71, harness-10-runtrace, frozen-evidence-clean, containment-wording-4file-sync, gitignore-exit-trap, tracked-eligible-vs-committed].
|
||||
- **2026-06-17 (S69 GOLIVE Văn phòng số public-all-roles authz — PASS, 0 blocker, gotcha #44-family CLEAN):** 1-file BE-only DbInitializer.cs (+81, new `SeedAllRolesOfficeModulePermissionsAsync` :2261 + call :2055 AFTER S65 HRM grant → AFTER revoke :2042). NOT deployed (static + Dev-DB review, build PASS). Near-exact mirror of S65 HRM method, ONLY delta = `+CanCreate=true` (HRM was read-only). **8 verify ALL PASS:** (1) **Ordering** — grant call sits after `RevokeTemporarilyHiddenModulesAsync` (:2042) + after S65 (:2048) → grant wins revoke. (2) **Allow-list EXACTLY 16 Off keys** — Off/Dashboard/DanhBa/PhongHop(+View+Book)/DeXuat(+List+Create+Inbox)/DonTu(+Leave+Ot+Travel)/DatXe/ItTicket; const names map correct values per MenuKeys.cs:99-120; NO PhongHopManage/AttendanceReport/ChamCong; array contains ZERO Hrm*/Personal/Pe*/Master key → no leak. (3) **Upgrade-only correct** — row exists→only flips CanRead/CanCreate false→true (`if(!row.CanRead)`+`if(!row.CanCreate)`), NEVER touches CanUpdate/CanDelete, never lowers; new row→read+create=true, update/delete=false (Permission.cs defaults false anyway). (4) **3 excluded keys STAY HIDDEN — decisive cascade check:** `Off` is NOT one of the 4 inherit-roots in GetMyMenuTreeQuery (:56-59,:70-73,:80-83 = Contracts/Workflows/PE/PeWorkflows ONLY) → granting Off does NOT cascade to children; each Off child reads its OWN `resolved` flags (:65, falls to false-tuple if no row); PhongHop_Manage(parent=Off_PhongHop:1830)/AttendanceReport(parent=Off:1845) not-in-list→revoke-false→filtered by HasAccess(:96); ChamCong re-parented to Personal(:1850/:1962) under hidden Personal root, not under Off, not granted→hidden. (5) **Admin unharmed** — MenuPermissionHandler:27 Admin bypass; Dev DB: all 18 Off rows belong to Admin already read+create=true → upgrade branch no-op. (6) **No real write-path opened — KEY for golive:** grep Controllers for Off menu keys = 0 matches; Office controllers gate writes by class-level `[Authorize]` (any-auth, self-service create) + per-action `[Authorize(Roles="Admin")]` for true admin writes (MeetingRoomsController Create/Update/Delete=Manage-rooms :26/34/43, Attendances :37/42, LeaveBalances :23/28) — NOT by Off_*.Create policy. So broad CanCreate grant only drives FE menu+button (usePermission/PermissionGuard); API write-auth untouched, admin CRUD stays Admin-only regardless. (7) **No migration** — seed-logic only; all 16 keys in MenuKeys.All:157-161 (seeded). (8) **Idempotent** — 2nd run: rows already true→0 change; SaveChanges gated `if(added>0||upgraded>0)`. **Dev DB baseline** (307 perms,13 roles): 0 non-admin Off rows exist→method takes add-branch for 12 non-admin roles (creates 16 read+create rows each, 3 excluded never added). build Infrastructure 0err/0warn. 0 rogue write (only cicd-monitor/MEMORY.md noise, read-only respected). **Learned:** for a public-grant golive the load-bearing security proof is TWO-fold — (a) cascade-safety = confirm the granted root is NOT an inherit-root (else siblings leak, gotcha #44-family) AND trace excluded keys' ParentKey to a non-granted/hidden parent; (b) write-path-safety = grep that the broadly-granted menu key is NOT used as a controller `[Authorize(Policy=)]` (here Office uses class `[Authorize]`+per-action Roles=Admin, so CanCreate is FE-only — granting it cannot escalate API writes). **surprise:** the "Manage rooms" admin function is double-protected — excluded from allow-list (menu hidden) AND its API is `[Authorize(Roles=Admin)]`; menu-hide alone would've been insufficient but the controller gate makes the broad grant safe even if a key had slipped. Verdict PASS — safe commit+deploy. Tag [s69, office-golive-authz, public-all-roles, inherit-root-no-cascade, off-not-policy-key-fe-only-grant, gotcha44-family-clean, admin-write-double-protected].
|
||||
- **2026-06-17 (S69 Văn phòng số RE-SKIN static logic-preservation — PASS, 0 blocker):** 10 pages presentation-only re-skin → PURO PageHeader/KpiCard + Hồ sơ-NS idiom (9 fe-user office + 1 fe-admin AttendanceReport). NOT built yet, fe-admin not mirrored (em main next). **Strongest proof = exact API/queryKey diff OLD-vs-NEW byte-identical ALL 8 fe-user pages** (grep `api\.(get|post|put|delete)` + `queryKey:[...]` sorted -u, zero delta): proposals POST /submit + /{kind} · workflow-apps POST /{k}+/submit+PUT /workflow · meeting-bookings POST/DELETE+invalidate · it-tickets PUT /{id}/assign · directory/departments/attendance-report/excel-blob all UNCHANGED. Mutation side-effects (onSuccess/onError/invalidateQueries/setActionDialog/setComment/navigate) 1:1 (line-shift only). ProposalCreate validation `!title.trim()` throw + required + submit-disabled intact. AttendanceReport exportExcel blob (createObjectURL→a.download→click→revoke) intact. **Cat2 orphans CLEAN:** 0 unused import — flagged Users(=UsersIcon alias) + FormEvent/ReactNode (React.* namespace not named-import) + Accent(comment word) all FALSE-alarm verified. **Cat3 shared-comp contract:** PageHeader{eyebrow,title,subtitle,icon,accent,actions} + KpiCard{label,value,icon,accent,active,onClick} props all match real sig; KpiCard onClick wired to REAL filter state (ItTickets `setFilter`/WorkflowAppsList `setStatusFilter`/ProposalsList — driving actual client `.filter()`), InternalDirectory 2 KpiCards INTENTIONALLY inert (no onClick=presentational counts, matches comp design — NOT dummy). **Shared comps + index.css NOT modified** (git status -- ui/ + *.css EMPTY; sha256 identical fe-user==fe-admin per ls). **Cat4 color-trap CLEAN:** grep added lines for `(teal|violet|amberx|greenx)-(200|300|400|800|900)` = ZERO; index.css confirms accents ship only 50/100/500/600/700 (brand has full 50-900 so brand-800 valid); gotcha #66 — 0 gradient/dark-bg headings added (all headers on light surface use accent-ink text-brand-800/{accent}-700 via PageHeader). **Cat1 mock-markers:** 0 //Mock/alert/TODO-wire. **Client-side filter additions** (ItTickets filter/breached, WorkflowAppsList statusFilter useMemo) = presentation views over fetched items, NO new query/endpoint. **2 MINOR (non-block):** (a) ProposalDetail status badge now renders TWICE — PageHeader actions slot + existing status-row (cosmetic dup, both presentation); (b) it-tickets/workflow-apps client-filter is view-only over a `pageSize:100/50` first-page fetch (pre-existing pagination limit, re-skin doesn't worsen). **Learned:** for pure re-skin, the decisive logic-preservation proof is `grep api-call + queryKey sorted -u` OLD-vs-NEW byte-equality across every page — faster + more rigorous than reading each hunk; orphan-import heuristic (body-occ<=1) flags `X as Y` aliases + `React.X` namespace + comment-words as false-positives, always grep the actual usage line before flagging build-break. **surprise:** custom accent palettes (amberx/greenx/teal/violet) deliberately ship NO -800 stop so headings MUST use -700 (brand is the only -800-bearing accent) — a -800 on a non-brand accent = silent no-class Tailwind v4, the re-skin respected this everywhere. Verdict PASS — safe for em main to build+mirror. Tag [s69, office-reskin, presentation-only, api-querykey-byte-equal, color-trap-clean, kpicard-inert-vs-filter, gotcha66-clean].
|
||||
- **2026-06-16 (S65 PE mục E HoSoLink review — em-main PROXY, PE-Workflow reviewer-stage died-empty):** Review mục-E hyperlink render + HoSoLink BE wiring (`5a0aaa4`). Reviewer-stage trong Workflow `pe-hoso-link-rename-pro` return RỖNG → em main self-gate evidence: Detail DTO `hoSoLink` present + `null` backward-compat phiếu thật (Run #293 GET 200); Create/Update +trailing-optional `HoSoLink=null` KHÔNG vỡ call-site (grep 0 manual ctor — KHÁC CreateDepartmentCommand #291 CS7036 vì positional-required vs trailing-optional); mirror fe-user==fe-admin SHA256 IDENTICAL (PeDetailTabs+PeWorkspaceCreateView); hyperlink `<a target=_blank rel=noopener noreferrer>` no reverse-tabnabbing; rename "Dự trù PRO"→"Ngân sách PRO" CHỈ display (giữ "Ghi chú từ PRO" + field-code). LEARNED: hyperlink free-text = no server-side XSS (render-as-href client-only); absolute-set Update (null=clear) chủ đích. SURPRISE: reviewer-stage chết-rỗng trong fan-out = lý do verify-heavy task vẫn cần em-main self-gate dù có Workflow (verdict `feedback_workflow_fanout_reliability`). Tag `[s65, pe-section-e-review, em-main-proxy-self-gate, hosolink-backward-compat, workflow-fanout]`.
|
||||
- **2026-06-16 (S65 public Hồ sơ NS read for all roles — static pre-commit, PASS, 0 blocker, gotcha #44 family CLEAN):** 1-file change DbInitializer.cs (+66, call-site :2046 SAU revoke :2040 + new `SeedAllRolesHrmProfileReadPermissionsAsync` :2203). Prod NOT deployed (static review, build PASS đã claim). **7 verify ALL PASS:** (1) **Ordering** — grant gọi SAU `RevokeTemporarilyHiddenModulesAsync` trong SeedAsync → grant thắng (git diff confirms call sits immediately after revoke). (2) **Upgrade path prod-critical** — method MUTATES existing row `if(!row.CanRead){row.CanRead=true;upgraded++}` (EF change-tracked → SaveChanges persists); NOT skip-existing-noop. Correctly fixes S58-class bug (revoke set CanRead=false on prod rows → upgrade flips true). (3) **Scope precise** — `hrmKeys = new[]{MenuKeys.Hrm, MenuKeys.HrmHoSo}` EXACTLY 2; NO Hrm_Dashboard/Hrm_Config*/Off*/Personal. `Hrm` is NOT one of 4 inherit-roots (Contracts/Workflows/PE/PeWorkflows in GetMyMenuTree:56-59) so granting Hrm root does NOT cascade to Dashboard/Config children → they keep own false flags → filtered out by `HasAccess(n)=n.CanRead||Children.Any(HasAccess)`. Menu shows Hrm root → Hồ sơ NS leaf ONLY (HrmHoSo ParentKey=Hrm:1806, Dashboard sibling ParentKey=Hrm:1850 stays hidden). (4) **Read-only** — add-path CanCreate/Update/Delete=false; upgrade-path touches ONLY CanRead. (5) **No regression** — Admin bypass at MenuPermissionHandler:27 untouched; revoke unchanged; Off/Personal/Dashboard/Config stay hidden after full seed. (6) **Idempotent** — 2nd run: row.CanRead already true → `if(!row.CanRead)` false → 0 change. (7) **No non-Admin write path** — `MenuPermissionHandler` Read→AnyAsync(CanRead) is what GET checks; all 19 EmployeesController write actions (main+5 satellite) require Hrm_HoSo.Create/Update/Delete which grant leaves false → 403. **surprise/monitor-note (NOT a defect, NOT introduced by this change):** HrDashboardController/HrmConfigsController/Attendances/LeaveBalances carry ONLY class-level `[Authorize]` (any-auth, NO per-action Hrm_*.Read policy) — so their data was already reachable by direct URL pre+post S65 (menu-hide ≠ API-lock; S58 revoke comment DbInit:2153-2155 explicitly acknowledged this). S65 does NOT widen it (only touches perm matrix rows Hrm+Hrm_HoSo + menu filter). cicd-monitor must NOT assume "Dashboard hidden in menu"=="dashboard data unreachable". Spec comment said "6 catalog Hrm_Config*" but there are 6 config leaves + Hrm_Config subgroup = 7 keys — cosmetic count, all stay hidden, not a code bug. **Learned:** for menu-key read-grant, verify the granted root is NOT an inherit-root (else cascade leaks siblings) + trace HasAccess filter + confirm leaf ParentKey chains to the visible root; upgrade-path correctness = grep that method MUTATES row (not skip-existing) when a prior revoke pre-set the flag false on prod. Verdict PASS — safe commit. Tag [s65, public-hrm-hoso, upgrade-path-correct, inherit-root-no-cascade, gotcha44-family-clean, menu-only-not-api-lock-monitor-note].
|
||||
|
||||
## 🔄 Curate trigger
|
||||
- >~30KB → archive recent → L2 `archive/<period>.md`. Stale >3mo → remove.
|
||||
- **Last curate: 2026-05-29 S40 em main proxy** (28.4→~18KB): archived S33 Plan C + S33 startup + S32×2 + S29 wrap detail → q2 + git d2f52ba; refreshed stale (81/111→130 test, 47→55 gotcha, 31→40 mig, ~146→211 endpoints). Foundation (bug patterns + 5-category + Smart Friend guard + cross-module security) preserved. Prev: S34 q2 · S22 q1.
|
||||
- **Last curate: 2026-06-18 S71 (Harness-9 L1→L2, same-role race append over auto-inject cap)** (36.7→24.2KB): moved 10 entries (byte-exact) → `archive/2026-06.md` — oldest FIFO tail S33/S35/S43/S49 + Smart-Friend-cumulative + archive-pointer + die-meta S57bis/S60 + redundant bottom Harness-10 R2/R3 (dup of S71 H10 entries kept). KEPT foundation + newest cluster (S71×2/S69×3/S65×2). Verified 10/10 moved lines `grep -Fxf` present-once in archive; numstat archive +N -0. `_INDEX.md` +10 pointer lines (substring sha-keyed). gist NOT updated (skip — em-main distill later). Prev: S70 (42.5→24.8KB) · S40 (28.4→18KB).
|
||||
- **Prev curate: 2026-06-17 S70 (Harness-9, em-main + Stage-B workflow)** (42.5→24.8KB): moved 9 entries S51→S57 (byte-exact) → `archive/2026-06.md`; KEPT foundation + 6 newest (S69×2/S65×2/S60/S57bis) + S49/S43/S35/S33 tail + Smart-Friend-cumulative + archive-pointers. Built `archive/_INDEX.md` (substring sha-keyed) + `.gist.md` (4-field distill-gen:1). Also Stage-C audit actor (`wf_9520d8cd-4fe` — verify 0-byte-loss/pointer/coverage). No re-ground (additive-only). Prev: S40 (28.4→18KB) · S34 q2 · S22 q1.
|
||||
- **Prev curate: 2026-05-29 S40 em main proxy** (28.4→~18KB): archived S33 Plan C + S33 startup + S32×2 + S29 wrap detail → q2 + git d2f52ba; refreshed stale (81/111→130 test, 47→55 gotcha, 31→40 mig, ~146→211 endpoints). Foundation (bug patterns + 5-category + Smart Friend guard + cross-module security) preserved. Prev: S34 q2 · S22 q1.
|
||||
|
||||
32
.claude/agent-memory/reviewer/archive/2026-05.gist.md
Normal file
32
.claude/agent-memory/reviewer/archive/2026-05.gist.md
Normal file
@ -0,0 +1,32 @@
|
||||
# Reviewer Agent — Gist 2026-05 (q1 S21-S24 + q2 S25/S26/S28)
|
||||
|
||||
> **distill-gen: 1** (đã nén — KHÔNG nén lại bản này).
|
||||
> 4-field/record: **VIỆC · KẾT-LUẬN(+file:line/commit) · BÀI-HỌC · BẤT-NGỜ**. Mỗi dòng kết bằng back-resolve `substring:"..."` về verbatim. Nhãn cao/vừa/thấp = giá-trị-tái-dùng.
|
||||
> Verbatim: `2026-05-q1.md` (### headings) + `2026-05-q2.md` (## headings). Mục lục: `_INDEX.md`. **Archives FROZEN.**
|
||||
> **Foundation note (gotcha #17):** q2 header ghi foundation gotchas #17-#48 + Smart Friend guard + 5-category + Cat6 + cross-module-security mirror preserved untouched in MEMORY.md — gotcha #17 = q2-foundation-note anchor.
|
||||
|
||||
---
|
||||
|
||||
## q1 — S21→S24
|
||||
|
||||
- [cao] VIỆC: S21 t3-t5 em-main solo self-review (no spawn), 12-commit cumulative push, CICD-Monitor thay vai Reviewer post-deploy. KẾT-LUẬN: PASS — build+npm×2 mỗi chunk; **gotcha #45 fix = self-test 3 regression test (test-before §7)**; cumulative 84 test/29 mig/45 gotcha. BÀI-HỌC: future-focus = per-NV permission audit (Level-table vs User-table flag) + EF backfill SQL order giữa ADD-DROP. BẤT-NGỜ: heavy push không cần Reviewer spawn khi UAT-mode + Monitor cover. substring:"S21 t3-t5, no spawn" → 2026-05-q1.md
|
||||
|
||||
- [cao] VIỆC: S22 em-main solo self-review (no spawn) Plan E strict-V2. KẾT-LUẬN: PASS — actor.UserId scope List+Detail+Inbox (loose clause `|| ApprovalWorkflowId!=null` removed); **guard EnsureCanRejectV2Async** chặn forge non-approver PATCH Reject (defense-in-depth FE+BE); S22+4→S22+5 AdjustBudget refactor dùng `level.AllowApproverEditBudget` opt-in; **Identity password ≥12 chars reject 11-char `User@123456`**. BÀI-HỌC: **anti-pattern default-scope-expansion S22+4→S22+5 (per-NV opt-in, KHÔNG default-expand không admin tick)**; verify type-fields trước render (BudgetAdjustSection TS2339 assume changelogs). BẤT-NGỜ: **gotcha #30 PS5.1 Vietnamese diacritics ASCII-only** (seed-test-users-prod.ps1 parser-fail) + **NEW gotcha #47 paths-ignore agent-memory gap (PENDING bro decide)**. substring:"S22 18:00" → 2026-05-q1.md
|
||||
|
||||
- [cao] VIỆC: S23 t1 Plan K1+K2 adversarial **spawn** review — Mig 31 schema swap + Service Approver F2 branch (11 BE files +4093/-83 LOC). KẾT-LUẬN: **VERDICT PASS với 2 Major + 2 Minor**. Major: (1) **Orphan UsersController zombie endpoint PATCH `/api/users/{id}/allow-skip-final` silent NoOp** (Task.CompletedTask, admin UI tick→BE swallow); (2) **stale Mig 28 comment `ApprovalWorkflow.cs:78`**. Wire: Approver F2 branch line 477 AFTER UPSERT opinion 441-468 + BEFORE advance 502 (audit context đủ). BÀI-HỌC: **anti-pattern "Transient sentinel" — đặt sentinel + comment "chunk khác cleanup" nhưng chunk đó scope SHIFT → zombie state**; recommend explicit K5 cleanup trước K7 test. BẤT-NGỜ: Mig Designer 3938 dòng dominate diff. substring:"S23 t1 Plan K1+K2 cumulative review, spawn" → 2026-05-q1.md
|
||||
|
||||
- [cao] VIỆC: S23 t3 Plan M adversarial **spawn** review — F1 edge-case Bước 1 + Phase=TraLai display rename. KẾT-LUẬN: **VERDICT PASS** — **OneLevel/OneStep edge case keeps ChoDuyet** (hot-path **`ApplyReturnModeAsync` Service.cs:287-333**): replace `Phase=TraLai+clear+return` bằng set-pointer(0,1)+summary "không lùi được"→fallthrough→Phase=ChoDuyet preserved; Drafter mode GIỮ Phase=TraLai. 106/106 test (+2 Fact). BÀI-HỌC: backward-compat phiếu UAT đang TraLai vẫn resume (entry fromPhase==TraLai→ChoDuyet). BẤT-NGỜ: 8 user-facing "Trả lại" literal chưa rename = action-verb vs phase-label phân biệt (spec narrow OK). substring:"S23 t3 Plan M cumulative review, spawn" → 2026-05-q1.md
|
||||
|
||||
- [cao] VIỆC: S23 t4-t11 em-main self-review (no spawn) Plan N+O+P+Q+R+S+T+U. KẾT-LUẬN: PASS cumulative — **Plan N GetPe `PurchaseEvaluationFeatures.cs:765` per-NV ApproverUserId discriminator**; **Plan O 4-lookup-site cascade fix `EnsureCanRejectV2Async:201` + ApplyReturnModeAsync:248 + EnsureEditableForDetailsAsync:72 + AdjustBudgetCommandHandler:311** (+3 regression test); **Plan P Controller `TransitionPeBody:267` record +3 fields mirror Command — root-cause 2-ngày prod bug F1+F2 wire fail**; Plan T DbInitializer DemoSeed:Disabled flag. BÀI-HỌC: **grep ENUMERATE TẤT CẢ lookup sites cùng pattern (Plan N point-9 chỉ catch 1/5 → Plan O cascade); Controller body record MUST mirror Command fields**. BẤT-NGỜ: DbInitializer auto re-seed loop (IIS recycle) → Plan T flag root-cause. substring:"Plan N+O+P+Q+R+S+T+U, no Reviewer spawn" → 2026-05-q1.md
|
||||
|
||||
- [vừa] VIỆC: S24 Plan AA adversarial **spawn** review — AwAdminOverview wire (BE+Layout+FE WorkflowMatrixViewPage). KẾT-LUẬN: **VERDICT PASS 0 critical/0 major/0 minor** — Controller→GetAwAdminOverviewQuery→Handler `Where(IsUserSelectable==ius)` real call no mock; **class-level `[Authorize]` bare PRESERVED (gotcha #44 protect)**; DbInitializer ToDictionary PK-unique safe. Low-note: non-admin pass isUserSelectable=false leak workflow chưa ghim (non-sensitive, NOTE only). BÀI-HỌC: enforce admin/non-admin filter = audit follow-up; MenuKeys.All[] không cover dynamic Pe_*_WfView. BẤT-NGỜ: TS `FlagCell` indexed-access từ Pick 7-keys union compile clean. substring:"S24 Plan AA cumulative pre-commit verify, spawn" → 2026-05-q1.md
|
||||
|
||||
- [cao] VIỆC: S24 Plan AA post-wrap finalize (no re-spawn) — 4 polish chunks back-to-back (px-2 hotfix + redesign v1/v2 + wrap fix). KẾT-LUẬN: PASS — **ROI pattern: BUNDLE cumulative verify cho heavy chunk (wire BE / migration / cross-stack); SKIP spawn cho <30min polish (CSS/layout/color)** — multi-spawn 4× ~100K = ROI thấp vs 1 spawn heavy ~25K. BÀI-HỌC: UI/UX iteration thuần CSS chỉ cần self-verify build + bro visual confirm. BẤT-NGỜ: Run #210 mixed BE+FE+docs trigger CICD normal — KHÔNG reinforce docs-only anomaly hypothesis. substring:"S24 Plan AA post-wrap cumulative finalize" → 2026-05-q1.md
|
||||
|
||||
## q2 — S25 / S26 / S28
|
||||
|
||||
- [cao] VIỆC: S25 Plan AB + wrap em-main self-review — ApplyReturnModeAsync refactor cdfd542 (PE Budget Adjust + Trả lại Người-chỉ-định log). KẾT-LUẬN: PASS — **gotcha #48 SQLite frozen-clock tie-break** (Multi-Changelog.Add cùng SaveChangesAsync transaction → **`OrderByDescending(CreatedAt).FirstAsync()` non-deterministic**). BÀI-HỌC: **Cat5 ADD = test filter discriminator beyond timestamp = EntityType + Summary keyword**; UAT skip `dotnet test` recurring risk khi BE refactor >100 LOC. BẤT-NGỜ: frozen-clock = same-tick CreatedAt → order ambiguous in SQLite test. substring:"S25 Plan AB + wrap" → 2026-05-q2.md
|
||||
|
||||
- [vừa] VIỆC: S26 Plan AG adversarial **spawn** review (~25K) + AG2-AG6 em-main solo. KẾT-LUẬN: PASS 12-check 0-issue — **commit `0bf6c7e` 2-file +346/-116 mirror IDENTICAL `21001E90...`**; useMemo nested + details/summary 2-level + localStorage Set persist; 0 mig; 111/111 test. BÀI-HỌC: Reviewer spawn cho heavy cross-stack (A+B+C ~370 LOC + 4 sub-agent), em-main solo cho polish iteration (SHA256 IDENTICAL + npm×2). BẤT-NGỜ: AG2-AG6 polish 50-100 LOC ROI thấp → no re-spawn. (**Smart-Friend guard active all spawns**). substring:"S26 Plan AG pre-commit + AG2-AG6" → 2026-05-q2.md
|
||||
|
||||
- [cao] VIỆC: S28 wrap Layer A governance Reviewer-perspective (em-main solo, KHÔNG actual product review). KẾT-LUẬN: **Cat6 ADDED = Authority-boundary check** (distinguish "bro suggested X" advisory vs "bro mandated X" directive; **flag em-main self-authorized "MANDATORY ... cross-project" rule**). **ABANDONED-rule: "RAG ghi mọi tương tác mandatory" S28-t2 over-reach** — bro caught t4 scope-down về SOLUTION_ERP self-discipline. BÀI-HỌC: implicit consent ("chú ý"/"có thể") ≠ explicit mandate ("BẮT BUỘC"); tag schema `[lesson, phase-<N>, <bc>]` forward S28+. BẤT-NGỜ: em-main t2 implicit-interpret "chú ý X" AS "MANDATORY policy" = authority-boundary violation caught retroactive. substring:"S28 wrap Layer A governance Reviewer perspective" → 2026-05-q2.md
|
||||
34
.claude/agent-memory/reviewer/archive/2026-06.gist.md
Normal file
34
.claude/agent-memory/reviewer/archive/2026-06.gist.md
Normal file
@ -0,0 +1,34 @@
|
||||
# Reviewer Agent — Gist 2026-06 (S51→S57, moved from L1 @S69 curate)
|
||||
|
||||
> **distill-gen: 1** (đã nén — KHÔNG nén lại bản này).
|
||||
> 4-field/record: **VIỆC · KẾT-LUẬN(+file:line/commit) · BÀI-HỌC · BẤT-NGỜ**. Mỗi dòng kết bằng back-resolve `substring:"..."` về verbatim. Nhãn cao/vừa/thấp.
|
||||
> Verbatim: `2026-06.md` (entry-bullets, chronological earliest-first). Mục lục: `_INDEX.md`. **Archive FROZEN.**
|
||||
> 9 record = các entry vừa dời từ MEMORY.md L1 lúc S69 curate (giữ verdict + file:line + bài-học).
|
||||
|
||||
---
|
||||
|
||||
- [cao] VIỆC: S51 P11-C Vehicle+Driver catalog (Mig 44/45 + FE KIND_CONFIG +2 + 5 test) — **spawn, em-main proxy (reviewer return truncated gotcha #53)**. KẾT-LUẬN: **PASS post-fix, 1 MAJOR caught** = Driver FE↔BE required-field mismatch (FE render phoneNumber/licenseNumber/licenseClass OPTIONAL nhưng BE validator `NotEmpty()`+EF `.IsRequired()` → empty submit 400/500); fix FE +`required:true` align Vehicle. Mig 45 = filter 3 HRM unique (gotcha #57). BÀI-HỌC: **parallel fan-out BE∥FE file-disjoint → inconsistency trong SHARED em-main contract chỉ lộ lúc integration; green tests ≠ correct contract (no test chạm empty-optional path); reviewer = the net**. BẤT-NGỜ: transient mid-deploy bundle hash + reviewer self-truncate trước ghi MEMORY → em-main proxy. substring:"S51 P11-C Vehicle+Driver + gotcha #57" → 2026-06.md
|
||||
|
||||
- [cao] VIỆC: S52 P11-E AttendanceReport + P11-F MaTicket codegen pre-commit (migration-free) — spawn. KẾT-LUẬN: PASS 0-blocker, 191 PASS — **gotcha #44 attack DISARMED: `[Authorize(Roles="Admin")]` ×2 report endpoints, verify `AppRoles.Admin="Admin"` LITERAL (AppRoles.cs:5) == attribute == FE includes('Admin'); "QTV" (DbInit:1454) = display-code DECOY**; camelCase contract field-for-field; handler `.Year/.Month` IQueryable-translatable, holiday-check in-memory AFTER ToListAsync. MaTicket codegen-on-Create (kanban no-workflow). BÀI-HỌC: **khi spec NAMES attack vector (gotcha #44 role-string), verify LITERAL const value không chỉ attribute-presence**. BẤT-NGỜ: Bash tool = bash không phải PowerShell (Select-String exit 127 → dùng grep). substring:"S52 P11-E AttendanceReport + P11-F MaTicket" → 2026-06.md
|
||||
|
||||
- [cao] VIỆC: S52-late Task C ItTicket admin-reassign + Task D AttendanceReport menu-key pre-commit (migration-free, 5 prod file) — spawn. KẾT-LUẬN: PASS 0-blocker — **menu-key `"Off_AttendanceReport"` byte-identical 4 chỗ (MenuKeys.cs:125 == menuKeys.ts:68 == seed parent == Layout staticMap:87)**; FE PUT `/it-tickets/{id}/assign` body MATCH BE record; admin auto-perm via SeedAdminPermissions iterate MenuKeys.All. BÀI-HỌC: **menu-key wiring = verify byte-identity FULL mirror set (BE const + All[] + seed parent + FE menuKeys + staticMap) + confirm target route THỰC SỰ tồn tại (grep App.tsx) — staticMap→non-existent route silently drops leaf (gotcha #50)**. BẤT-NGỜ: lucide `FileBarChart` = deprecated-alias re-export FileChartColumn nhưng vẫn valid (d.ts grep confirm trước flag). substring:"S52-late Task C ItTicket admin reassign" → 2026-06.md
|
||||
|
||||
- [cao] VIỆC: S53 Mig 47 Master catalog (Department/Project/Supplier) Code filtered-unique `+.HasFilter("[IsDeleted] = 0")` (4th/5th/6th cumulative gotcha #57 EXT) + 3 test — spawn. KẾT-LUẬN: PASS 0-blocker, **203 PASS** — **filter string byte-identical HolidayConfiguration:18 (xxd `5b 49 73 ... 30`, spaces quanh `=`); index STAYS unique:true (filter NARROW scope KHÔNG drop uniqueness)**; test seeds IsDeleted=true row → real handler `AnyAsync(Code)` thru HasQueryFilter → NotThrow (RED-before SqliteException confirmed). BÀI-HỌC: **cookie-cutter EXT = byte-compare filter string (xxd) vs canonical sibling + verify index still unique; app-level dup-check = test premise, verify handler có `AnyAsync(Code)` else premise false**. BẤT-NGỜ: implementer claim "2 pre-existing DocxRenderer warn" nhưng clean incremental rebuild = 0 warn. substring:"S53 gotcha #57 EXT Mig 47" → 2026-06.md
|
||||
|
||||
- [cao] VIỆC: S54 ItTicket reassign authz Admin-OR-dept-IT cross-stack — controller `[Authorize(Roles="Admin")]`→`[Authorize]` any-auth, authz moved INTO `AssignItTicketHandler` + new capability query — spawn. KẾT-LUẬN: PASS 0-blocker, 216 PASS — **role-string "Admin" chain traced FULL: `AppRoles.Admin="Admin"`(AppRoles.cs:5)→SeedRoles `Name=roleName`(DbInit:1485)→GetRolesAsync returns NAMES→JwtTokenService:32 `Claim(ClaimTypes.Role)`→CurrentUserService:30-31 `Roles.Contains("Admin")`; "QTV"(DbInit:1458 RoleLabels)=ShortName DECOY; Program.cs no RoleClaimType override**; guard fail-CLOSED khi itDeptId null; capability 0-leak `{canReassign:false,staff:[]}`. BÀI-HỌC: **authz role-string review = trace const→seed Name→GetRolesAsync(names not codes)→Claim→reader AND grep JWT cfg `RoleClaimType` override (none=symmetric); display-code (QTV) = classic decoy**. BẤT-NGỜ: moving authz controller→handler = CORRECT gotcha #44 fix (không phải smell) khi paired BE-computed capability flag. substring:"S54 ItTicket reassign authz Admin-OR-dept-IT" → 2026-06.md
|
||||
|
||||
- [vừa] VIỆC: S55 master-data import — Mig 48 `AddProjectMasterFields` (Project +4 nullable Year/Investor/Location/Package) + `SeedRealMasterDataAsync` (62 Project+71 WorkItem+3 Supplier per-code idempotent ungated) + FE ×2 — **spawn, em-main proxy (reviewer truncated gotcha #53 trước verdict)**. KẾT-LUẬN: PASS — em-main completed pending-check `dotnet test` = clean rebuild **216 PASS** (giải tỏa cached-binary 2.76s concern); Mig Up=4 AddColumn/Down=4 DropColumn reversible; per-code idempotent ungated line 118 (reaches prod); FLOCK01 collision skip-demo-wins; runtime Dev proof Investor populates. BÀI-HỌC: **long adversarial review return truncates (gotcha #53) → reviewer emit PASS/FAIL verdict SỚM trước deep re-verify để sống sót; em-main complete được pending-check deterministic (clean dotnet test = fresh build)**. BẤT-NGỜ: cached-binary fast-build nghi → clean test = fresh build giải tỏa. substring:"S55 master-data import pre-commit" → 2026-06.md
|
||||
|
||||
- [cao] VIỆC: S55 Phase-1 FE visual redesign pre-commit — 14 fe-admin file VISUAL/CSS-only (NAMGROUP density + SOLUTION brand) — spawn. KẾT-LUẬN: PASS 0-blocker, **verdict-first survived** — npm build fe-admin ✓ 0 TS err; **Button cva variant keys + size STABLE chỉ class VALUES swap (51 call-site safe); Input/Select/Dialog forwardRef+passthrough unchanged; DataTable `Column<T>` type UNCHANGED**; **Be-Vietnam-Pro KEPT (grep @import:3 + --font-sans:22 + font-family:34 unchanged — initial font-drop blocker RETRACTED sau grep)**; Tailwind v4 `shadow-xs`/slash-opacity valid. 2 MINOR: text-slate-400 hint ≈3.5-4:1 borderline-AA (defer). BÀI-HỌC: **font-drop scare = grep 3 load-bearing lines (@import/--font-sans token/font-family) TRƯỚC khi flag — diff hunk lower in file ≠ font removed; emit PASS/FAIL line-1 FIRST (gotcha #53 survival)**. BẤT-NGỜ: Tailwind v4 `shadow-xs` real (v3 shadow-sm renamed) — đừng flag typo. substring:"S55 Phase-1 FE visual redesign pre-commit" → 2026-06.md
|
||||
|
||||
- [cao] VIỆC: S56 pre-golive authz **live prod curl** 8 new endpoints — spawn. KẾT-LUẬN: PASS 0-blocker — **8/8 return 401 unauth; admin-authed hrm-configs/vehicles/drivers/leave-balances/attendances all 200 (xlsx 6797B); non-admin Drafter correctly 403 on 2 Admin-only**; **gotcha #44 silent-403 sweep CLEAN: GET /it-tickets/assignable-staff returns HTTP 200 `{canReassign:false,staff:[]}` for non-IT (NOT swallowed 403), handler returns flag không throw (`WorkflowAppsFeatures.cs:466`)**. 1 MINOR: PUT /it-tickets/{id}/assign checks NotFound BEFORE Forbidden (`:496-508`) = existence-oracle leak (mutation fail-closed, post-golive hardening). BÀI-HỌC: capability endpoint = flag-return không throw = đúng gotcha #44 fix. BẤT-NGỜ: NotFound-before-Forbidden ordering = minor info-leak defense-in-depth defer. substring:"S56 pre-golive authz live-curl" → 2026-06.md
|
||||
|
||||
- [cao] VIỆC: S57-resume Harness-4 two-tier adopt gate (governance pre-send + pre-commit, no product code) — spawn (self-report `claude-fable-5[1m]` = promote-list direct evidence). KẾT-LUẬN: **PASS-with-fixes 0-blocker** — re-verify ALL GREEN: frontmatter **7 pin `claude-opus-4-8` + 4 `inherit` + 0 `[1m]`-in-frontmatter** + 0 project-pin settings; **evidence track-record 8/8 REAL** vs HANDOFF/STATUS; **nấc G-011 đúng mọi chỗ load-bearing (demote=executed-file·pending-restart, 0 overclaim runtime)**. Fixes: hash PLACEHOLDER trước send + "SENT ✓" premature status-verb + count "(13)"vs"11" + invalid-role typo→rơi 'opus'. BÀI-HỌC: **gate adopt-governance = re-run MỌI grep claim + cross-check evidence vs HANDOFF nguyên văn; n=2 demoted spawn-test double-duty làm inherit-chain proof HỢP LỆ (registry cached=chạy config cũ) nhưng cần phrase rõ**. BẤT-NGỜ: CCD harness cache agent frontmatter → đổi agent .md phải restart CLI mới ăn. substring:"S57-resume Harness-4 two-tier adopt gate" → 2026-06.md
|
||||
|
||||
---
|
||||
|
||||
> **distill-gen: 2** (S71 curate — KHÔNG nén lại bản này). 10 record dời từ L1 @S71 (over-cap do same-role race). Phần lớn routine pre-commit gate (low-unique-marker) → nén gộp; deep-detail đọc verbatim `2026-06.md` qua `_INDEX.md`.
|
||||
|
||||
- [thấp] VIỆC: 6 routine pre-commit gate cũ S33-S49 (S33 Plan B G-H1 Mig 34 SF-6× · S35 G-H2 BE CRUD 16-endpoint SF-8× · Smart-Friend-cumulative-8×-CLEAN · S40 archive-pointer S29-S33 · S43 P11-B LeaveBalance Max-no-truncate · S49 Harness-1/2/3 governance Max-clean). KẾT-LUẬN: tất cả PASS/CLEAN, 0 unique gotcha#/root-cause mới (gate áp-dụng-lặp). BÀI-HỌC: foundation 5-category + Smart-Friend đã ở L1 — các gate này không sinh marker mới. BẤT-NGỜ: N/A (routine). substring:"S33 Plan B G-H1" / "S35 G-H2 BE CRUD" / "S43 P11-B LeaveBalance" / "S49 Harness 1/2/3" → 2026-06.md
|
||||
- [vừa] VIỆC: 2 die-meta non-deliver (S57bis product-gate die-0-byte ×2 · S60 đợt1 PE submit-guard die mid-run 3rd). KẾT-LUẬN: reviewer chết giữa task → em-main on-behalf gate. BÀI-HỌC: recovery-path = [[feedback_agent_kill_recovery]] (canonical user-memory — marker survive ở đó). BẤT-NGỜ: die-0-byte ×2 cùng điểm. substring:"S57bis product gate" / "S60 đợt1 PE submit-guard" → 2026-06.md
|
||||
- [vừa] VIỆC: 2 Harness-10 adap-review R2(hmw.js engine)/R3(floor C1-C8) — REDUNDANT (dupe S71 entry giữ L1-top). KẾT-LUẬN: cùng catch C5 L1 over-claim (doc nói hmw.js prompt-builder emit L1, engine no-fs → grep=0 → fixed em-main convention). BÀI-HỌC: marker survive ở L1 S71 + [[feedback_harness10_run_trace]]. BẤT-NGỜ: R2+R3 độc-lập cùng kết luận = high-conf. substring:"Harness-10 adap R2-lens hmw.js" / "Harness-10 adap run-trace folder R3-floor" → 2026-06.md
|
||||
51
.claude/agent-memory/reviewer/archive/2026-06.md
Normal file
51
.claude/agent-memory/reviewer/archive/2026-06.md
Normal file
@ -0,0 +1,51 @@
|
||||
# Reviewer Agent - Archive 2026-06 (S69 curate)
|
||||
|
||||
> Verbose Recent-activity entries moved from MEMORY.md during S69 Harness-9 curate (L1 over-cap ~43.5KB -> under 28KB).
|
||||
> **FROZEN / additive-only.** Entries are BYTE-EXACT copies from MEMORY.md L1 (newest-first there); ordered here chronological earliest-first.
|
||||
> Foundation (role + bug patterns #42/#43/#44/#47 + 6-category checklist + Smart Friend guard + review essentials) + 6 newest activity entries + S49/S43/S35/S33 tail + Smart-Friend-cumulative + archive-pointers KEPT in MEMORY.md.
|
||||
> Span: S51 -> S57 (9 entries). See `_INDEX.md` for the cross-archive table.
|
||||
|
||||
---
|
||||
|
||||
## Archive entries (chronological - earliest session first)
|
||||
|
||||
- **2026-06-08 (S51 P11-C Vehicle+Driver + gotcha #57 pre-commit — PASS, 1 MAJOR caught) [em main proxy — reviewer return truncated gotcha #53]:** Reviewed Mig 44 (Vehicle/Driver catalog) + Mig 45 (filter 3 HRM unique) + FE KIND_CONFIG +2 + 5 tests (186 PASS). Independent build+test re-verify GREEN. **CAUGHT 1 MAJOR (Cat 3 cross-stack contract):** Driver FE↔BE required-field mismatch — FE render phoneNumber/licenseNumber/licenseClass OPTIONAL nhưng BE validator `NotEmpty()` + EF `.IsRequired()` NOT NULL → empty submit = 400/500. Root = inconsistent em-main brief (BE "mirror Vehicle"=required vs FE spec quên required). Fix: FE +`required:true` (align BE all-required như Vehicle). Cats khác clean (Mig diff clean, Authorize Roles=Admin writes, gotcha #57 grep-complete 3 HRM, DbInitializer idempotent + #51 infra-gated, SHA256 mirror, no copy-paste Driver↔Vehicle). **Learned:** parallel fan-out (BE∥FE file-disjoint) → bất kỳ inconsistency trong SHARED em-main contract chỉ lộ lúc integration; green tests ≠ correct contract (no test chạm empty-optional path). reviewer = the net. **surprise:** transient mid-deploy bundle hash (cicd lesson) + reviewer self-truncate trước khi ghi MEMORY → em main proxy. Verdict PASS post-fix. Tag [s51, p11-c, gotcha57, contract-mismatch-catch].
|
||||
|
||||
- **2026-06-08 (S52 P11-E AttendanceReport + P11-F MaTicket codegen pre-commit — PASS, 0 blocker):** Migration-free (no schema). Independent re-verify: build 0-err · **191 PASS** (58 Dom + 133 Infra, +5: 3 ItTicketCodeGen + 2 AttendanceReport) · fe-admin `tsc --noEmit` exit 0. **Cat3 gotcha #44 attack DISARMED:** `[Authorize(Roles="Admin")]` ×2 report endpoints — verified `AppRoles.Admin = "Admin"` literal (AppRoles.cs:5) == attribute string == FE `user?.roles.includes('Admin')`; "QTV" (DbInit:1454) = display-code NOT role-name; pattern proven (Catalogs/HrmConfigs identical). **Cat3 camelCase contract MATCH** field-for-field BE record PascalCase→FE interface (year/month/rows/grandTotal*/userId/fullName/ot*) — ASP.NET default camelCase, no Program.cs override. **BE handler correct:** `.Year/.Month` in IQueryable (EF-translatable DateTime), `.DayOfWeek`+holidaySet only AFTER `.ToListAsync()` (in-memory) — IQueryable-translation attack handled; holiday-check BEFORE weekend BEFORE weekday (test 2026-06-01 Mon-but-holiday proves override); `DateOnly.FromDateTime` correct (Holiday.Date=DateOnly); OtPolicy fallback 1.5/2.0/3.0; `IsDeleted` via AuditableEntity all 3 entities. Exporter mirrors ContractExcelExporter, ClosedXML 0.105.0, `RenderResult(Content,FileName,ContentType)` ctor order correct, DI registered. **MaTicket codegen:** `e` untracked at codegen time → inner SaveChanges persists ONLY sequence row, no double-insert; gen-on-Create (kanban no-workflow) vs Leave/OT gen-on-Submit — semantically correct; git-show confirms MaTicket was ALWAYS null pre-P11-F (closes gap). **1 MINOR (informational, defer):** sequence-gap-on-failure — codegen commits seq in own Serializable tx BEFORE `Add(e)+SaveChanges`; ticket-insert fail → burned IT/2026/NNN gap. NOT new defect = identical to existing Leave/OT pattern (project-wide accepted trade-off, cosmetic). MyAttendancePage MIRROR divergence intentional+documented (fe-user untouched, §3.9 OK). 0 mock markers. **Learned:** when spec NAMES an attack vector (gotcha #44 role-string), verify the LITERAL const value not just attribute presence — "QTV" display-code was the decoy; role-name match is the real check. **surprise:** Bash tool = bash not PowerShell (Select-String fails exit 127 → use grep). Verdict PASS — safe to commit. Tag [s52, p11ef, attendance-report, mat-codegen, gotcha44-disarmed].
|
||||
|
||||
- **2026-06-08 (S52-late Task C ItTicket admin reassign + Task D AttendanceReport menu-key pre-commit — PASS, 0 blocker):** Migration-FREE (menu = idempotent DbInitializer seed). 5 prod files: MenuKeys.cs (const+All[]), DbInitializer.cs (1 seed tuple), fe-admin {menuKeys.ts, Layout.tsx staticMap, ItTicketsPage.tsx}. **Independent re-verify GREEN:** `dotnet build SolutionErp.slnx` 0-warn/0-err · `npm run build` fe-admin tsc-b+vite OK (1945 modules, only pre-existing CSS @import + >500KB + ineffective-dynamic-import warns). **menuKeyMatchOk:** `"Off_AttendanceReport"` byte-identical 4 places (MenuKeys.cs:125 const == menuKeys.ts:68 == seed parent key == Layout staticMap:87); seed leaf parent=`MenuKeys.Off` order=8 icon=`FileBarChart` (verified valid lucide alias `FileChartColumn as FileBarChart` — getIcon resolves via Icons[name]); App.tsx route `/attendance/report → AttendanceReportPage` PRE-EXISTING committed S52 (6a66429, no diff, 170-LOC real page not stub) — Layout maps to REAL route. `types/menu.ts` correctly NOT mirrored (`key:string` not typed union). admin auto-perm via `SeedAdminPermissionsAsync` iterating `MenuKeys.All` (DbInit:1917, idempotent Contains:1919); new-leaf-on-existing-DB confirmed (upsert loop:1845-1862 TryGetValue-miss→Add). **reassignCorrect:** FE PUT `/it-tickets/{id}/assign` body `{assignedToUserId}` MATCHES BE record `AssignItTicketBody(Guid AssignedToUserId)` (ItTicketsController:42); endpoint `[Authorize(Roles="Admin")]` (:34) under class `[Authorize]` — admin-app FE calling = correct; user-list reuses EXISTING `/users` GET (`PagedResult<UserDto>` items{id,fullName,email}, no new BE endpoint, lazy `enabled:target!==null`); 204 NoContent handled (no body-parse); `invalidateQueries(['it-tickets'])` on success; handler sets BOTH AssignedToUserId+AssignedToFullName (WorkflowAppsFeatures:467-468) + validates assignee IsActive→NotFound, no try-catch (GlobalExceptionMiddleware). **feUserUnchanged:** `git diff -- fe-user/` EMPTY (Task C = fe-admin-only divergence, documented top-comment ItTicketsPage:3-5). **noScopeCreep:** git status prod = EXACTLY the 5 expected files, agent-memory noise ignored, no new migration, no BE beyond MenuKeys+DbInitializer, ItTicketsPage diff 98+/5- all Task-C-scoped (imports+state+mutation+Pencil-btn+Dialog), 0 mock/alert markers. **Learned:** menu-key wiring = verify byte-identity across the FULL mirror set (BE const + BE All[] + seed parent + FE menuKeys + FE staticMap) + confirm the target route actually EXISTS (grep App.tsx) — a staticMap entry pointing to a non-existent route silently drops the leaf (gotcha #50). **surprise:** lucide `FileBarChart` is a deprecated-alias (re-exported from FileChartColumn) but still valid — d.ts grep confirmed before flagging. Verdict PASS — safe to commit. Tag [s52-late, it-ticket-reassign, attendance-report-menukey, menukey-mirror-5way, gotcha44-disarmed, gotcha50-disarmed].
|
||||
|
||||
- **2026-06-08 (S53 gotcha #57 EXT Mig 47 — Master catalog filtered-unique pre-commit — PASS, 0 blocker, Smart Friend clean):** 4th/5th/6th cumulative gotcha #57 (after Holiday Mig 43 S45 + HRM ×3 Mig 45 S51). 3 Master configs (Department:18/Project:19/Supplier:24) Code unique index `.IsUnique()` → `+.HasFilter("[IsDeleted] = 0")` + Mig 47 (3-file) + 3 new tests. **Independent re-verify ALL GREEN:** build SolutionErp.slnx 0-warn/0-err · **full suite 203 PASS** (58 Dom + 145 Infra, Failed:0 Skipped:0, +3) · 3 new tests run isolated 3-passed-0-skipped. **Cat correctness:** filter string byte-identical to HolidayConfiguration:18 (xxd `5b 49 73 44 65 6c 65 74 65 64 5d 20 3d 20 30` — spaces around `=`, not guessed); index STAYS `unique:true` (2 active same-Code still violate — active uniqueness preserved); Supplier Type index (:25) UNTOUCHED non-unique unfiltered (snapshot:3590 bare). Mig Up=3×Drop+3×Create-filtered, Down=3×reverse-unfiltered (reversible). Snapshot+Designer both show filter on all 3 Master Code idx. **Test NOT tautology:** seeds IsDeleted=true row → real Create*CommandHandler (app-check `AnyAsync(Code==req.Code)` thru HasQueryFilter !IsDeleted PASSES) → asserts NotThrow + active-count==1 + IgnoreQueryFilters all==2; RED-before confirmed (3 failed SqliteException UNIQUE on unfiltered). Cmd signatures match test calls (Project 7-arg/Supplier 9-arg/Dept 4-arg). **noScopeCreep:** git status = exactly 3 configs + snapshot + Mig47 (2 untracked) + 1 test + 2 MEMORY; no FE, no extra mig, no stray. Mig 47 latest in seq (after Mig46 ItTicket SLA). **Learned:** cookie-cutter EXT of proven pattern → discriminator = byte-compare filter string (xxd) vs canonical sibling + verify index still unique (filter must NARROW scope not DROP uniqueness). app-level dup-check existence = the test premise; verify handler actually has `AnyAsync(Code)` else test premise false. **surprise:** implementer claimed "2 pre-existing DocxRenderer warnings" but clean incremental rebuild = 0 warn (unrelated, non-issue). Verdict PASS — safe commit. Tag [s53, gotcha57-ext, mig47, master-catalog, smart-friend-clean].
|
||||
|
||||
- **2026-06-08 (S54 ItTicket reassign authz Admin-OR-dept-IT cross-stack pre-commit — PASS, 0 blocker, gotcha #44 disarmed correctly):** Controller `/assign` hạ `[Authorize(Roles="Admin")]`→`[Authorize]` any-auth; authz moved INTO `AssignItTicketHandler` (Admin-OR-IT Forbidden + assignee-must-IT Conflict) + new `GetAssignableItStaffQuery` capability endpoint. **Independent re-verify GREEN:** `dotnet test SolutionErp.slnx` = **216 PASS** (58 Dom + 158 Infra, Failed:0 Skipped:0, +13 matches 203→216 claim exactly) · fe-admin + fe-user `tsc -p tsconfig.app.json --noEmit` BOTH exit 0 clean. **#2 CHÍ MẠNG role-string "Admin" CONFIRMED REAL (full chain traced):** `AppRoles.Admin="Admin"` literal (AppRoles.cs:5) → SeedRolesAsync `Name=roleName` (DbInit:1485) so DB Role.Name=="Admin" → Identity GetRolesAsync returns NAMES → JwtTokenService:32 `new Claim(ClaimTypes.Role, r)` → CurrentUserService:30-31 `FindAll(ClaimTypes.Role)` → `cu.Roles.Contains("Admin")` CORRECT. "QTV" (DbInit:1458 RoleLabels) = ShortName DISPLAY label only = decoy. Program.cs JWT sets NO `RoleClaimType` override (ClaimTypes.Role symmetric write/read). Same proven pattern as every existing Roles="Admin" endpoint. **Bypass airtight:** controller any-auth → handler sole gate; guard `if(!isAdmin && !(itDeptId is Guid mine && myDeptId==mine)) Forbidden` fail-CLOSED when itDeptId null (non-admin blocked; admin still passes role-branch). `Department.Code=="IT"` IS seeded (DbInit:2082 "Phòng CNTT") so live non-null. **Capability 0-leak:** non-auth → `{canReassign:false, staff:[]}` (no name leak), `[Authorize]` any-auth → 0 silent-403. **Defense-in-depth intact:** FE nút `{canReassign&&}` but PUT still hits handler guard → 403/409 → `onError: toast.error(getErrorMessage(err))` surfaces (NOT swallowed). **FE SHA256 page 4bcaf2f IDENTICAL both apps** (types.ts correctly NOT identical — admin AttendanceReportDto vs user HrDashboardDto diverge below; added AssignableStaff block byte-identical); old fe-user page was read-only kanban (NO app-specific logic lost — diff purely additive); fe-user has all imports (apiError.getErrorMessage, Dialog size/footer/onClose props match, Select passthrough, sonner). **Test 13-fact NOT happy-path:** Case5 Forbidden side-effect assert `AssignedToUserId.Should().BeNull()` (red-able by contrast vs Case6/7 same-handler success), Case3/3b empty-staff 0-leak, Case8 Conflict msg-exact. prove-by-contrast ĐỦ CHẶT (partition non-IT-throws vs IT/admin-succeed identical handler). **1 MINOR defer:** assignee-must-IT NEW vs old handler (git HEAD: old allowed admin→ANY active user); itDeptId-null → even admin Conflict — fail-closed acceptable+spec-requested, cosmetic (prod IT-dept seeded). **Learned:** authz role-string review = trace FULL chain const→seed Name→GetRolesAsync(names not codes)→Claim(ClaimTypes.Role)→reader AND grep JWT cfg for `RoleClaimType` override (none=symmetric) — display-code (QTV) in RoleLabels/ShortName dict = classic decoy. **surprise:** moving authz controller→handler is the CORRECT gotcha #44 fix (not a smell) when paired with BE-computed capability flag for FE gating + handler as sole gate. Verdict PASS — safe commit. Tag [s54, it-ticket-reassign-authz, gotcha44-disarmed, role-string-chain-verified, cross-stack-clean].
|
||||
|
||||
- **2026-06-09 (S55 master-data import pre-commit — PASS [em main proxy — reviewer return truncated gotcha #53 before verdict, mirror S51]):** Reviewed Mig 48 `AddProjectMasterFields` (Project +4 nullable col Year/Investor/Location/Package) + `SeedRealMasterDataAsync` (62 Project+71 WorkItem+3 Supplier per-code idempotent ungated) + FE ProjectsPage form +4 ×2 app. Reviewer ran 293s/31-tools nhưng truncated mid-thought (nghi cached-binary 2.76s build → muốn forced clean rebuild + Project tests). **Em main COMPLETED đúng việc nó định làm:** `dotnet test SolutionErp.slnx` = clean rebuild + **216 PASS** (58+158, 0 fail/skip) → giải tỏa cached-binary concern (test = fresh build). 10 dims GREEN: Mig Up=4 AddColumn/Down=4 DropColumn reversible + 3-file; seed 62/71/3 0-dup; per-code idempotent ungated line 118 (reaches prod); FLOCK01 collision skip-demo-wins; FE↔BE 4 nullable both sides (tránh S51 mismatch); test-file compile-fix +4 null legit; gotcha #57 index untouched; **runtime Dev proof** (data landed, Investor col populates). 0 rogue write (read-only respected, git clean of code). **Learned:** long adversarial review return truncates (gotcha #53) → reviewer nên emit PASS/FAIL verdict SỚM (trước deep re-verify) để sống sót truncation; em main complete được đúng pending-check (clean dotnet test) deterministic. Verdict PASS — safe commit. Tag [s55, master-import, em-main-proxy-truncate, runtime-dev-proof].
|
||||
|
||||
- **2026-06-09 (S55 Phase-1 FE visual redesign pre-commit — PASS, 0 blocker, verdict-first survived):** 14 fe-admin files VISUAL/CSS-only (NAMGROUP density + SOLUTION brand). **Independent re-verify GREEN:** `npm run build` fe-admin = ✓ 607ms, 1945 modules, 0 TS err (only PRE-EXISTING warns: CSS @import-order + >500KB chunk + INEFFECTIVE_DYNAMIC_IMPORT realtime.ts — git-confirmed none introduced, @import lines untouched in diff). **Regression Cat1 ALL preserved:** Button cva variant keys (primary/secondary/outline/ghost/danger) + size (sm/md/lg) STABLE — only Tailwind class VALUES swapped, defaultVariants intact (51 call-sites safe); Input/Select/Textarea/Label = `forwardRef`+`...props`+`className` passthrough unchanged, only `cn()` literal; Dialog `{open,onClose,title,children,footer,size}` destructure + sm/md/lg→max-w map intact (+aria-label="Đóng" = a11y GAIN); DataTable `Column<T>` type UNCHANGED (diff starts after type def) — render/sortable/align/width + sort + Pagination props intact, RowActions/RowActionButton purely ADDITIVE; Layout MenuLeaf className-only (brand left-rail via before:), nav/resolver/permission-filter/routing untouched; PhaseBadge phase→ContractPhaseColor/Label map intact; PageHeader/EmptyState/TopBar pure class. DashboardPage data-flow (useQuery/navigate/fmtMoney/BarChart/PhaseBadge) preserved, STAT_TONE+SectionLabel additive, +`cn` import only. **Brand Cat3:** Be Vietnam Pro KEPT (grep: @import:3 + --font-sans:22 + font-family:34 all unchanged — initial blocker RETRACTED after grep); only brand-/slate/semantic colors, 0 off-brand hex/indigo. **a11y:** focus-visible rings present everywhere (brand-500); Label self-documents slate-500 (~4.6:1 AA-pass) chosen over NAMGROUP zinc-400. **Tailwind v4 (^4.2.3)** — `ring-current/15`, `shadow-xs`, slash-opacity all valid v4. **noScopeCreep:** exactly 14 fe-admin, 0 fe-user, 0 BE/src (only noise = frontend-designer/MEMORY.md agent file). **2 MINOR (non-block, a11y-floor):** `text-slate-400` on white for small hint/empty text (DashboardPage hints ~line 50/64, DataTable empty-cell, EmptyState was-400-stays-400) ≈3.5-4:1 — borderline-fail WCAG-AA for <18px, but these are de-emphasized hints not primary content + PRE-EXISTING tone (redesign mostly UPGRADED slate-400→500 on EmptyState desc + Pagination); accept for hint role, revisit if audit. **Learned:** font-drop scare = grep the 3 load-bearing lines (@import/--font-sans token/font-family) BEFORE flagging — diff hunk lower in file ≠ font removed; emit PASS/FAIL line-1 FIRST (gotcha #53 truncation survival, mirror S51/S55). **surprise:** Tailwind v4 `shadow-xs` is real (v3's shadow-sm renamed) — don't flag as typo; v4 slash-opacity on currentColor (`ring-current/15`) is valid. Verdict PASS — safe commit+deploy. Tag [s55-fe, visual-redesign, namgroup-density, verdict-first, regression-clean, slate400-minor].
|
||||
|
||||
- **2026-06-09 (S56 pre-golive authz live-curl — PASS, 0 blocker):** Live prod curl 8 new endpoints. **8/8 return 401 unauth**; admin-authed: hrm-configs/vehicles(2)+drivers(2), leave-balances/my(5 lazy), attendances/report+excel(200, 6797B xlsx) all 200; non-admin Drafter correctly 403 on the 2 Admin-only attendance endpoints. **gotcha #44 silent-403 sweep CLEAN:** capability GET /it-tickets/assignable-staff returns HTTP 200 `{canReassign:false,staff:[]}` for non-IT Drafter (NOT swallowed 403) + `{true,[]}` admin — handler returns flag, doesn't throw (`WorkflowAppsFeatures.cs:466`). assign-mutation guard fail-closed (:504). E2E: GET /projects payload has all +4 fields (70/70), CAL01 Investor live. Off_AttendanceReport menu key in admin /menus/me. **1 MINOR (non-block, defense-in-depth):** PUT /it-tickets/{id}/assign checks NotFound BEFORE Admin-OR-IT Forbidden (`WorkflowAppsFeatures.cs:496-508`) → existence-oracle leak; mutation itself fail-closed → post-golive hardening only. Tag [s56, pre-golive-verify, authz-clean, gotcha44-clean, notfound-before-forbidden-minor].
|
||||
|
||||
- **2026-06-10 (S57-resume Harness-4 two-tier adopt gate — PASS-with-fixes, 0 blocker):** Gate trước send-email + commit (governance, không product code). Self-report spawn: `claude-fable-5[1m]` (reviewer = promote-list inherit → direct promote-tier evidence, em main cite được). Independent re-verify ALL GREEN: grep frontmatter = đúng 7 pin `claude-opus-4-8` + 4 `inherit` + 0 `[1m]`-in-frontmatter (2 body-text hits hợp lệ: database-agent.md:46 + README.md:9 MỚI — adap-report "match duy nhất" stale-by-own-edit) + 0 project-pin settings. Evidence track-record **8/8 REAL** vs HANDOFF/STATUS/own-memory (S51 MAJOR · S54 QTV-decoy · S53 Mig46 · S56 H2-4.5/5 + dept-IT-0-user · S57 ×3 controller +5/+5/+5 `[Authorize(Roles="Admin,CatalogManager")]` working-tree). Nấc G-011 đúng mọi chỗ load-bearing (demote = executed-file·pending-restart, 0 overclaim runtime). Fixes: hash PLACEHOLDER trước send (`nac: sent` + "SENT ✓" premature = đúng status-verb class broadcast cảnh báo) · STATUS "(runtime resolve 1M)" thiếu attribution AI_INFRA-s20 · hmw.js:91 log "same-model inherit" stale + :9 "8-agent" vs 9 roles · adap-report "(13)" vs "11" count · invalid-role typo → rơi 'opus' (fail-direction xuống vs H4.5 nghiêng-quality). **Learned:** gate adopt-governance = re-run MỌI grep claim + cross-check evidence vs HANDOFF nguyên văn; n=2 demoted spawn-test double-duty làm inherit-chain proof là HỢP LỆ (registry cached = chạy config cũ) nhưng cần phrase rõ kẻo đọc nhầm thành promote-list spawn-test. Tag [s57, harness-4, two-tier-gate, pre-send-gate, g011].
|
||||
|
||||
|
||||
--- (S71 curate 2026-06-18 — moved from L1 MEMORY.md: oldest FIFO tail S33→S49 + die-meta S57bis/S60 + redundant bottom Harness-10 R2/R3. Byte-exact, additive-only.) ---
|
||||
|
||||
- **2026-05-26 (S33 Plan B G-H1 Phase 2 pre-commit — PASS, Smart Friend 6× CLEAN):** 17 file (3 BE + 6 FE new + 6 mod + 2). SHA256 mirror 3 file IDENTICAL admin==user. 5 endpoint real mediator.Send 0 mock. Mig 34 `AddEmployeeProfiles` 7 table UNIQUE indexes + FK Cascade. SeedDemoEmployeeProfiles NOT gated DemoSeed (gotcha #51 ✓). gotcha #50 Layout staticMap mirror ✓. **3 MINOR defer:** EmployeeCode race SERIALIZABLE low-risk · Update 3 bool not nullable (partial reset) · Delete DateTime.UtcNow direct. Verdict PASS. Tag `[s33, hrm-mig34, smart-friend-6x]`.
|
||||
|
||||
- **2026-05-28 (S35 G-H2 BE CRUD 16 endpoint pre-commit — PASS, Smart Friend 8× CLEAN):** 2 NEW file `HrmConfigFeatures.cs` 439 + Controller 137. build clean, 130/130 PASS. Cat1: 0 mock, 8 ConflictException (Holiday Update composite `(Year,Date)` BOTH fields). Cat3: class `[Authorize]` + 12 per-action `[Authorize(Roles="Admin")]`. Cat5: 8 Validator MaxLength MATCH EF source (Code=50 not spec 20). **2 MINOR defer:** ListHolidays no IsActive filter (inconsistent sibling) · OtPolicy "1 active unique" NOT enforced handler (G-P1 ambiguous nếu 2+ active). Verdict PASS. Tag `[s35, smart-friend-8x-clean]`.
|
||||
|
||||
- **2026-05-30 (S43 P11-B LeaveBalance pre-commit — PASS, Max no-truncate):** 14 file (LeaveBalance entity+config+Mig42 + Features + Controller + deduction hook + Create/Update LeaveType guard + embed balance + FE×4 + tests). 154 PASS (130→154). **Deduction exactly-once VERIFIED** (terminal else only, guard Status!=DaGuiDuyet chặn re-approve; advance/reject/return no-deduct). **FK invariant fully closed** — grep 2 write site LeaveTypeId (Create + UpdateDraft) cả 2 guard AnyAsync→Conflict, bogus type không thể tới terminal FK insert. Embed balance = RequesterUserId (approver thấy đúng người tạo). admin `[Authorize(Roles=Admin)]`. **2 MINOR defer:** concurrency lost-update UsedDays (no RowVersion — human-sequential accept) · stale line-num comment. Verdict PASS. Tag `[s43, p11b-leavebalance, max-clean]`.
|
||||
|
||||
- **2026-06-07 (S49 Harness 1/2/3 adopt pre-commit — PASS all 3, no blocker):** Governance/infra adopt (no product code, no test impact). VERIFIED: H1/H2 = 2 sub scope-DISJOINT + tools `[Read,Grep,Glob,Bash+4RAG]` NO store_memory/Write (INFORM-only); genuinely **TAILORED not copy-paste** (SE 4-RAG vs AI_INFRA 2-RAG · dropped effort:max + agent-ops-monitor/sister · Fidelity→SE `reviewer`). H2 5-trục in harvest-curator.md + session-end §L.b(f). H2 wave-mode hmw.js mirror AI_INFRA + **B6 `git check-ignore` VERIFIED** (wave-*/+agent-teams/ ignored · hmw.js/README tracked). H3 self=`se` complete substitution · **SHA256 canonical formula byte-identical send==check** · 13 .gitkeep exact · adap-apply base-path `outbox\all\`. honest nấc executed-file/verified-runtime-PENDING. G-015 scan = 6 hits ALL negating ("KHÔNG enforced") = correct honesty. **1 MINOR (non-block):** README:11/18 "7-agent" ASCII diagram = **PRE-EXISTING** drift (git diff proved work này chỉ touch load-bearing title/decision-tree/tool-grant/matrix; diagram predates S47 frontend-designer) → tooling-auditor H1 designed-to-catch = self-validating adoption. **learned:** `git diff base..head` = discriminator introduced-defect vs pre-existing-drift (đừng đổ lỗi work mới cho drift cũ); name-collision tailor-verify = diff frontmatter AI_INFRA-canonical vs SE-instance. **surprise:** mojibake scan false-pos trên "ĐÃ" (U+00C3 = valid VN uppercase, KHÔNG double-encode → verify codepoint in-context trước flag); broadcast floor "12 .gitkeep" UNDERCOUNT (correct=13 incl `all/` adap-channel — em main đúng). Verdict PASS, safe commit + restart. Tag [s49, harness-adopt, governance, max-clean].
|
||||
|
||||
- **Smart Friend cumulative 8× CLEAN:** (1) S22 #44 silent-403 · (2) S25 #48 SQLite tie-break · (3) S29 password ≥12 · (4) S29 ApplicableType cross-module · (5) S33 BW test · (6) S33 Plan B Phase 2 · (7) S35 FE forms · (8) S35 G-H2. Plus 9× G-O2 (S36, em không track ở đây). 2 MAJOR catches total (S29 password + S29 ApplicableType); rest clean với MINOR defer.
|
||||
|
||||
- **Archived S29-S33 detail + S32 startup → `archive/2026-05-q2.md` + git d2f52ba (S40 curate):** S33 Plan C B-Wrap 9/9 [Fact] verify · S33 startup drift audit (CLAUDE.md SEVERE → patched S40) · S32 wrap/startup standby · S29 wrap 2 MAJOR catch detail. KEY absorbed in bug patterns + Smart Friend cumulative above.
|
||||
|
||||
- **2026-06-11 (S57bis product gate — KHÔNG DELIVER, die-0-byte ×2, on-behalf em main ghi hộ, H2-proposed):** Cả 2 spawn (email-gate đầu + final gate) chết 0-byte output 0 return (resume-kill class #3, ref `feedback_agent_kill_recovery`) → em main SELF-GATE evidence-checklist: grep authz key-set + role-string vs AppRoles + Mig 49 Up/Down reversible + 240 test + Run #381 + prod smoke 401/404-control. LEARNED: output-file size=0 + im >5 phút = chết, KHÔNG đợi thêm; KHÔNG re-spawn >2 lần trong session có `--resume`. SURPRISE: khác S52 killed-with-partial — lần này 0-byte tuyệt đối (không gì recover được từ return). Tag `[s57bis, die-0-byte-x2, self-gate, on-behalf]`.
|
||||
|
||||
- **2026-06-12 (S60 đợt1 PE submit-guard + drafter-bypass gate — KHÔNG DELIVER, die mid-run, on-behalf em main ghi hộ, H2-proposed):** Task: review `37122f0` cross-stack (BE TransitionAsync submit-guard đủ-4-thông-tin mục 3 + bypass người-soạn-trong-chuỗi V2 BƯỚC-ĐẦU-only + FE PeDetailTabs ×2 + 14 PeSubmitGuardAndBypassTests 240→254). Die mid-run #53-class (commit body tự khai "Reviewer die mid-run → em main self-gate evidence-checklist PASS 0 blocker") → ship Run #283 PASS prod-verified, bundle rotate both. LEARNED: self-gate em main đứng vững lần 2 (sau S57bis) — checklist deterministic (test gate + diff scope + prod smoke 401/404-control) đủ cho PE refinement cross-stack. SURPRISE: die lần 3 trong 2 ngày (S57bis die-0-byte ×2 + S60 mid-run) DÙ promote-tier inherit Fable 5 → model-tier KHÔNG phải nguyên nhân die (nghi resume-kill/harness class) — trend data cho Harness-4. Tag `[s60, die-mid-run-3rd, self-gate, on-behalf]`.
|
||||
|
||||
- **2026-06-18 (Harness-10 adap R2-lens hmw.js ENGINE integrity — CONCERN, confirms sibling L1 over-claim still live, pre-commit):** Lens = hmw.js engine integrity (em-main rename wave→run-trace). **Engine itself CLEAN — all 4 R2 checks PASS:** (1) structure valid — `const wave=(A.run&&A.run.dir)?A.run:((A.wave&&A.wave.dir)?A.wave:null)` :91 nested-ternary paren-balanced 3/3, accepts args.run primary + args.wave alias (additive, old callers OK), var `wave` internal-name kept consistent :91/:92/:95/:103/:107/:132; subMd path :103 `${wave.dir}/sub-md/${role||'task'}-${i}.md` matches spec; template-literals balanced (backtick 54 EVEN all-escaped, brace 56/56, paren 140/140, bracket 14/14). (2) zero operative WAVE-MODE — grep `WAVE-MODE`=0; all 6 wave refs contextualized (legacy-alias :19/:90/:91, "supersedes Harness 2 wave" :87/:109); :113 ISOLATION contains "tracked-change NGOÀI run-folder (runs/<run-id>/)+code-disjoint=vi-phạm" ✓. (3) fan-out logic UNCHANGED — `git diff -U0` hunks = ONLY :91 behavioral (alias-accept); resolveModel/SCHEMA/checkpointApproved-guard/parallel/results.filter untouched. (4) valid JS (balance + structural, NO node --check per top-level-await). **THE CATCH (CONCERN, intersects R2):** runs/README.md:51 documents L1 in-run-reminder as firing in "`hmw.js` prompt-builder" w/ exact text 'run đang OPEN—nhớ scaffold@P1'+'run trước OPEN-beat đã harvest chưa' → grep that in hmw.js = **0**. hmw.js writeGuard :114 emits ONLY C4 return-instruction ("Harvest per-turn primary (C4)..."), NO scaffold/OPEN orchestrator-reminder; :92 is a log() at mode-detect not prompt-injection + still lacks the promised text. **Plan-vs-applied gap proven:** invest-synthesis:17 PLANNED "C5 Layer1: thêm reminder vào prompt-builder"; implement-synthesis NEVER lists applying L1 to hmw.js (only L2 :71 + L3 :51 applied); yet README:51+C7:72 present L1 as live. Doc asserts engine-behavior grep proves absent = over-claim. **Sibling reviewer (same adap, prior run today) already CONCERN on this exact gap — I independently re-confirm UNFIXED.** **Cross-file PASS:** gitignore runs/ TRACKED via `!.claude/**`:83 (check-ignore -v confirms negation) + wave-*/ kept IGNORED; containment wording synced 4 files (_ledger:4↔hmw:89/113↔runs/README:78); frozen evidence (broadcasts/adap-harness-2/error-ledger/STATUS/HANDOFF/archive_INDEX/sessions) ALL empty-diff; 0 mojibake. **Residual (non-block, self-flagged):** investigator-codebase/MEMORY.md +6 (29819B ~just-under-cap) = 4 same-role INVEST agents race (concurrency risk #7 invest-synthesis flagged) → em-main reconcile @closeout; new :113 guard forbids sub agent-memory writes = prevents recurrence. **Learned:** narrow lens (hmw.js JS structure) ≠ excuse to wave a doc-asserts-engine over-claim — when README says a layer "fires in <engine-file>", grep the engine for the CLAIMED text not a sibling instruction; INVEST-plan ≠ IMPLEMENT-applied. **Surprise:** engine rename genuinely flawless (dual-alias/balance/logic-frozen) — ONLY defect is adjacent doc over-stating what the clean engine does; engine-perfect + doc-overclaim coexist in one adap. Smart-Friend held: did NOT downgrade to PASS despite narrow lens + clean engine + sibling already-flagged. Tag [harness10, r2-hmwjs-engine, engine-clean-doc-overclaim, c5-L1-overclaim-reconfirm, plan-vs-applied-gap, dual-alias-additive].
|
||||
|
||||
- **2026-06-18 (Harness-10 adap run-trace folder R3-floor review — CONCERN, 1 over-claim, pre-commit):** Reviewed adap thay wave-mode → `runs/<run-id>/` 3-part (run.md+sub-md/+harvest/) git-TRACKED. Floor C1-C8 disk-verified. **C1/C2 PASS** — all 3 runs (invest/implement/review) scaffolded full 3-part (`ls` confirm + .gitkeep placeholders). **C3 PASS correct-nấc (NO over-claim)** — `git check-ignore runs/`=NOT-IGNORED (tracked-eligible via `!.claude/**` :83) AND `git ls-files runs/`=EMPTY=NOT-committed-yet; _ledger:4 + runs/README:80 + gitignore:89-99 document "tracked" correctly, NEVER falsely claim "committed". Nấc THẬT = tracked-ELIGIBLE pre-commit (must commit to realize — expected, not defect). **C4 PASS** — invest+implement synthesis present per-turn; review harvest empty=correct (in-progress). **C5 CONCERN (the catch)** — L2 (session-start:71 orphan scan `closed=⏳`+harvest-rỗng) + L3 (session-end:51 idempotent VERIFY-not-re-APPEND) genuinely wired. BUT **L1 OVER-CLAIM**: runs/README:51 documents L1 in-run reminder firing in "hmw.js prompt-builder" w/ exact text 'run đang OPEN—nhớ scaffold@P1'+'run trước...harvest chưa' → `grep -c` that text in hmw.js = **0**. hmw.js writeGuard only emits C4 return-instruction ("Harvest per-turn primary (C4)"), NO scaffold/OPEN reminder. INVEST planned it ("C5 Layer1: thêm reminder vào prompt-builder"), IMPLEMENT synthesis never mentions applying it, yet runs/README:51+C7:72 present L1 as live. Doc-vs-reality gap = over-claim. **C6 PASS** — _ledger OPEN+CLOSE beats (invest/implement CLOSED, review ⏳) + orphan def:3. **C7 PASS** — caveat genuinely honest (engine no-fs · C2 fragile · 3-layer=lưới-không-khóa · G-015 TRACKED≠read-only-enforced); strong. **C8 PASS** — wave→runs migration done (0 wave-*/ remain), wave-*/ kept IGNORED (verified). **Frozen evidence 0-byte-loss CONFIRMED** (broadcasts/·adap-harness-2·error-ledger·STATUS·HANDOFF all empty-diff vs HEAD). hmw.js `node --check`=OK, dual-alias A.run/A.wave intact. Containment wording synced 4 files (_ledger:4↔hmw:113↔workflows/README:38↔runs/README:78). **Learned:** for a multi-layer "anti-miss net" adap, the catch is grepping each layer's CLAIMED trigger-site against the actual engine file — a layer documented as "fires in hmw.js prompt-builder" must have backing text there, not just a sibling instruction; INVEST-plan ≠ IMPLEMENT-applied (cross-check synthesis-plan vs disk). **Surprise:** README's own C1-C7 section-numbering ≠ task's C1-C8 reviewer-axes (two schemes, NOT a defect — README documents convention, task axes evaluate it); don't conflate. Over-claim=CONCERN per task rule (would be PASS if README:51 softened L1 to "C4 return-instruction" matching reality, OR hmw.js actually added the scaffold reminder). Tag [harness10, run-trace-folder, c5-L1-overclaim, tracked-not-committed-correct-nac, frozen-evidence-clean, plan-vs-applied-gap].
|
||||
41
.claude/agent-memory/reviewer/archive/_INDEX.md
Normal file
41
.claude/agent-memory/reviewer/archive/_INDEX.md
Normal file
@ -0,0 +1,41 @@
|
||||
# Reviewer Agent — Archive Index (L2 dark-matter map)
|
||||
|
||||
> **Purpose:** mục lục cho L2 archive (verbatim entries KHÔNG vào RAG). 1 dòng / 1 bản-ghi.
|
||||
> **Pointer-style:** `substring:"..."` = primary (Ctrl-F / grep literal trong file đích, mỗi chuỗi đã verify resolve **count=1 unique**). Fallback: nếu file reflow, grep Session+Plan discriminator phrase (vd `"S23 t1 Plan K1+K2 cumulative review, spawn"`). KHÔNG dùng line-hint (archive FROZEN nhưng line-number không bền).
|
||||
> **Heading style khác nhau:** q1 dùng `###` (multiple records share date 2026-05-15 → date-only collides, dùng substring); q2 dùng `##`; 2026-06 dùng entry-bullet `- **`. Count headings: `^#{2,3}` cho q1/q2.
|
||||
> **Archives FROZEN / additive-only.** Sorted theo DATE (ascending). 4-field distill xem `*.gist.md` cùng thư mục.
|
||||
> **Built:** 2026-06-17 S69 Harness-9.
|
||||
|
||||
---
|
||||
|
||||
## Records (sorted by date — earliest first)
|
||||
|
||||
2026-05-13 · em-main self-review (no spawn) · cumulative 12-commit push, CICD-Monitor thay vai; gotcha #45 self-test 3 regression · substring:"S21 t3-t5, no spawn" → `2026-05-q1.md`
|
||||
2026-05-13 · em-main self-review (no spawn) · Plan E strict V2 actor-scope + EnsureCanRejectV2 + password ≥12 + #30 PS-diacritics + #47-gap-found · substring:"S22 18:00" → `2026-05-q1.md`
|
||||
2026-05-14 · adversarial spawn review · PASS w/ 2 Major + 2 Minor — Mig 31 schema swap + Approver F2 branch; caught zombie endpoint + stale comment · substring:"S23 t1 Plan K1+K2 cumulative review, spawn" → `2026-05-q1.md`
|
||||
2026-05-15 · adversarial spawn review · PASS — Plan M OneLevel/OneStep edge keeps ChoDuyet (hot-path Service.cs:287-333); 2 Fact tests · substring:"S23 t3 Plan M cumulative review, spawn" → `2026-05-q1.md`
|
||||
2026-05-15 · em-main self-review (no spawn) · Plan N→U; Plan O 4-lookup-site cascade + Plan P Controller-body-mirror 2-day prod bug · substring:"Plan N+O+P+Q+R+S+T+U, no Reviewer spawn" → `2026-05-q1.md`
|
||||
2026-05-15 · adversarial spawn review · PASS 0/0/0 — Plan AA AwAdminOverview wire + IsUserSelectable filter; class-[Authorize] preserved · substring:"S24 Plan AA cumulative pre-commit verify, spawn" → `2026-05-q1.md`
|
||||
2026-05-15 · em-main finalize (no re-spawn) · ROI lesson: BUNDLE heavy verify, SKIP spawn <30min CSS-polish (4 polish chunks) · substring:"S24 Plan AA post-wrap cumulative finalize" → `2026-05-q1.md`
|
||||
2026-05-19 · em-main self-review · gotcha #48 SQLite frozen-clock tie-break; Cat5 ADD test-filter discriminator EntityType+Summary · substring:"S25 Plan AB + wrap" → `2026-05-q2.md`
|
||||
2026-05-21 · adversarial spawn review · PASS 12-check — Plan AG nested useMemo + details/summary; commit 0bf6c7e mirror IDENTICAL 21001E90 · substring:"S26 Plan AG pre-commit + AG2-AG6" → `2026-05-q2.md`
|
||||
2026-05-22 · governance perspective (no product review) · Cat6 ADD Authority-boundary; ABANDONED "RAG-ghi-mọi-tương-tác" over-reach · substring:"S28 wrap Layer A governance Reviewer perspective" → `2026-05-q2.md`
|
||||
2026-05-26 · adversarial spawn review (Smart Friend 6×) · PASS — Mig 34 AddEmployeeProfiles 7-table + SHA256 mirror 3-file identical; SeedDemoEmployeeProfiles ungated (gotcha #51) · substring:"S33 Plan B G-H1 Phase 2 pre-commit" → `2026-06.md` [moved S71]
|
||||
2026-05-28 · adversarial spawn review (Smart Friend 8×) · PASS — HrmConfig 16 endpoint 8 ConflictException + class-[Authorize]+12 Roles=Admin; Validator MaxLength match EF · substring:"S35 G-H2 BE CRUD 16 endpoint pre-commit" → `2026-06.md` [moved S71]
|
||||
2026-05-28 · cumulative summary (no single review) · Smart Friend 8× CLEAN roster + 2 MAJOR catches total (S29 password + S29 ApplicableType) · substring:"Smart Friend cumulative 8× CLEAN" → `2026-06.md` [moved S71]
|
||||
2026-05-29 · archive-pointer (S40 curate) · S29-S33 detail + S32 startup → `2026-05-q2.md`+git d2f52ba; key absorbed in bug-patterns · substring:"Archived S29-S33 detail + S32 startup" → `2026-06.md` [moved S71]
|
||||
2026-05-30 · adversarial spawn review (Max no-truncate) · PASS — Mig 42 LeaveBalance deduction exactly-once + FK invariant 2-write-site guard; 130→154 test · substring:"S43 P11-B LeaveBalance pre-commit" → `2026-06.md` [moved S71]
|
||||
2026-06-07 · governance/infra adopt (Max clean) · PASS all 3 — Harness 1/2/3 tailored-not-copy; H2 wave-mode check-ignore verified; SHA256 send==check; pre-existing diagram-drift minor · substring:"S49 Harness 1/2/3 adopt pre-commit" → `2026-06.md` [moved S71]
|
||||
2026-06-08 · spawn review (em-main proxy, truncated) · PASS, 1 MAJOR caught — Driver FE-optional vs BE-required contract mismatch; gotcha #57 · substring:"S51 P11-C Vehicle+Driver + gotcha #57" → `2026-06.md`
|
||||
2026-06-08 · spawn review · PASS — AttendanceReport + MaTicket codegen; gotcha #44 role-string "Admin" literal disarmed · substring:"S52 P11-E AttendanceReport + P11-F MaTicket" → `2026-06.md`
|
||||
2026-06-08 · spawn review · PASS — ItTicket admin-reassign + AttendanceReport menu-key 5-way mirror; gotcha #50 route-exists · substring:"S52-late Task C ItTicket admin reassign" → `2026-06.md`
|
||||
2026-06-08 · spawn review · PASS — Mig 47 Master catalog filtered-unique (4th/5th/6th gotcha #57 EXT); filter byte-compare · substring:"S53 gotcha #57 EXT Mig 47" → `2026-06.md`
|
||||
2026-06-08 · spawn review · PASS — ItTicket reassign authz controller→handler; role-string chain const→seed→Claim verified · substring:"S54 ItTicket reassign authz Admin-OR-dept-IT" → `2026-06.md`
|
||||
2026-06-09 · spawn review (em-main proxy, truncated) · PASS — Mig 48 Project +4 fields + SeedRealMasterData 62/71/3; clean dotnet test 216 · substring:"S55 master-data import pre-commit" → `2026-06.md`
|
||||
2026-06-09 · spawn review · PASS — 14-file FE visual redesign; Be-Vietnam-Pro KEPT (font-scare retracted); verdict-first survival · substring:"S55 Phase-1 FE visual redesign pre-commit" → `2026-06.md`
|
||||
2026-06-09 · live-curl prod review · PASS — 8 endpoints 401/200/403; gotcha #44 sweep clean; NotFound-before-Forbidden minor · substring:"S56 pre-golive authz live-curl" → `2026-06.md`
|
||||
2026-06-10 · governance gate · PASS-with-fixes — Harness-4 two-tier frontmatter (7 pin Opus / 4 inherit); evidence 8/8 real; nấc G-011 · substring:"S57-resume Harness-4 two-tier adopt gate" → `2026-06.md`
|
||||
2026-06-11 · spawn DID-NOT-DELIVER (die-0-byte ×2, on-behalf) · em-main self-gate evidence-checklist; Mig 49 reversible + 240 test + Run #381 + prod 401/404; resume-kill class #3 · substring:"S57bis product gate" → `2026-06.md` [moved S71]
|
||||
2026-06-12 · spawn DID-NOT-DELIVER (die mid-run 3rd, on-behalf) · em-main self-gate; PE submit-guard 4-info + drafter-bypass first-step-only; 240→254 test, Run #283 · substring:"S60 đợt1 PE submit-guard" → `2026-06.md` [moved S71]
|
||||
2026-06-18 · adap review (Harness-10, R2-lens engine) · CONCERN — hmw.js engine CLEAN (dual-alias additive) but runs/README:51 over-claims L1 reminder in engine (grep=0); plan≠applied · substring:"Harness-10 adap R2-lens hmw.js ENGINE integrity" → `2026-06.md` [moved S71]
|
||||
2026-06-18 · adap review (Harness-10, R3-floor) · CONCERN — run-trace 3-part scaffolded; C3 tracked-not-committed correct-nấc; same L1 over-claim re-caught; frozen-evidence 0-loss · substring:"Harness-10 adap run-trace folder R3-floor review" → `2026-06.md` [moved S71]
|
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,15 @@
|
||||
---
|
||||
name: activity-s51-s52
|
||||
description: L2 archive — Recent-activity FIFO entries S51-S52 (filtered-unique gotcha #57 RED, round-robin/SLA, codegen day-type) aged out of MEMORY.md HOT
|
||||
metadata:
|
||||
type: project
|
||||
---
|
||||
|
||||
# L2 archive — activity S51-S52 (aged out of HOT FIFO 2026-06-18 S74)
|
||||
|
||||
Verbatim entries moved from `MEMORY.md` "Recent activity" to keep HOT index under cap. Recall via RAG `search_memory` or read here directly.
|
||||
|
||||
- **2026-06-08 (S52 P11-D Master gotcha #57 EXT) [test-before · 3 RED LIVE]:** +3 test `tests/.../Application/MasterCatalogFilteredUniqueTests.cs` (run `--filter MasterCatalogFilteredUnique` → Failed 3/Passed 0). Department+Project+Supplier `.IsUnique()` BARE (Dept cfg:18 / Proj:19 / Supp:24) chưa `[IsDeleted]=0` — cùng class gotcha #57. Mirror EXACT GROUP B HrmConfigFilteredUniqueTests: seed row `IsDeleted=true` slot Code="DUP1" → `Create{Dept|Project|Supplier}CommandHandler(db)` cùng Code → assert `NotThrowAsync` + active==1 + `IgnoreQueryFilters` all==2. **3 RED** = `DbUpdateException → SQLite Error 19 UNIQUE constraint failed: {Departments|Projects|Suppliers}.Code` (app-check `AnyAsync(Code==X)` chạy QUA HasQueryFilter → loại soft-deleted → PASS → Add+SaveChanges → DB UNIQUE bare đếm cả row xoá → throw). NOT test lỗi — REPORTED em main fix migration `.HasFilter` 3 config → flip GREEN. **⚠️ all-count PHẢI `IgnoreQueryFilters()`** (khác HRM ref dùng raw `Count(Code==X)` trên DbSet đã có HasQueryFilter → trả 1 not 2 = sai; tôi sửa = active-count plain DbSet, all-count IgnoreQueryFilters). 3 handler clean `(IApplicationDbContext db)` 1-dep. KHÔNG đụng Configuration/Domain/migration. Tag [s52, p11-d, gotcha-57, master-catalog, filtered-unique, test-before, RED].
|
||||
- **2026-06-08 (S52 P11-D Wave2 round-robin + SLA-due) [proxy by em main: agent killed session-limit trước MEMORY step]:** +9 test `ItTicketAssignSlaTests.cs` → **200 PASS** (Infra 133→142). **Round-robin:** seed Department Code="IT" + 2 user A/B `IsActive` trong IT + A có 1 ticket Open → Create → assign **B** (load 0<1); tie A=B → `ThenBy(Id)`; edge no-dept-IT / no-user-IT → unassigned; user ngoài IT hoặc `IsActive=false` KHÔNG assign. **SLA-due:** Priority Urgent→+4h / High→+8h / Medium→+24h / Low→+72h (assert `e.SlaDueAt==CreatedAt+SlaWindow[priority]`). **Regression P11-F:** create vẫn gen `^IT/\d{4}/\d{3}$`. `ItTicketSlaJob` BackgroundService SKIP unit-test (breach-query inline, khó test trực tiếp — REPORTED). Baseline 191→**200** (58 Domain + 142 Infra). Tag [s52, p11-d, round-robin, sla-due, regression].
|
||||
- **2026-06-08 (S52 P11-E + P11-F WorkflowApps/Attendance test-after):** +5 test → **191 PASS** (Infra 128→133). 2 file `tests/.../Application/`: **ItTicketCodeGenTests** (3 — MaTicket regex `^IT/\d{4}/\d{3}$` + sequential 001→002 cùng prefix `IT/{year}` LastSeq++ + per-year-prefix 2027 reset 001) + **AttendanceReportTests** (2 — full aggregate day-type/weighted + DepartmentId filter). **⭐ Serializable-on-SQLite GOTCHA = NON-ISSUE (confirmed):** `WorkflowAppCodeGen.GenerateMaDonTuAsync` dùng `BeginTransactionAsync(IsolationLevel.Serializable)` chạy SẠCH trên SQLite — provider map isolation level gracefully (no throw), format+seq+per-year đều hold KHÔNG cần try/skip. Đã proven sẵn bởi WorkflowAppApproveV2Tests (DT/LR path). Handler `CreateItTicketHandler(db, cu, clock)` = 3 dep MediatR. **Day-type test pattern (P11-E core):** holiday check chạy TRƯỚC weekend/weekday → seed 2026-06-01 (thứ Hai) vào holidaySet → assert phân **Holiday** dù là weekday (override day-of-week). Holiday.Date=DateOnly → `BuildHoliday` dùng `DateOnly.FromDateTime`. OtWeighted = 2×1.5+3×2.0+1×3.0=12.0m. DepartmentId filter: seed 2 Department row + 2 user khác dept → query deptA chỉ trả 1 row (handler join Users `u.DepartmentId==deptId`, userMeta dùng `DefaultIfEmpty` nên dept row optional nhưng seed cho DepartmentName assert). No prod bug. **⚠️ MSBuild OOM** chạy full parallel → dùng `-maxcpucount:1 -p:BuildInParallel=false` (env resource, KHÔNG test fail). Tag [s52, p11-e, p11-f, codegen, day-type, serializable-sqlite-ok, test-after].
|
||||
- **2026-06-08 (S51 P11-C HMW Wave2 filtered-unique gotcha #57):** +4 test `tests/.../Application/HrmConfigFilteredUniqueTests.cs` → **185 total = 183 PASS + 2 RED** (Infra 123→127). Mirror HolidayTests Case 7 (seed soft-deleted Code-slot → Create same Code → assert success + active==1 + all==2). **2 GREEN** Vehicle+Driver (Mig 44 config ĐÃ filtered → 2 catalog mới đúng). **2 RED INTENTIONAL = gotcha #57 REPRODUCED** (test-before): `CreateLeaveType_OnSoftDeletedCodeSlot...` → `SQLite Error 19 UNIQUE constraint failed: LeaveTypes.Code` + `CreateShift_OnSoftDeletedCodeSlot...` → `ShiftPatterns.Code` (bare `.IsUnique()` đếm cả row soft-deleted; handler app-check `!IsDeleted` PASS → Add+SaveChanges → DbUpdateException). NOT test lỗi — REPORTED em main fix Mig 45 `.HasFilter("[IsDeleted]=0")` cho 2 config → flip GREEN. **⚠️ Soft-delete trong test (giống Holiday):** AuditingInterceptor (prod soft-delete Deleted→Modified+IsDeleted=true) KHÔNG wire trong SqliteDbFixture → `Remove+SaveChanges` = HARD delete (không test được). PHẢI seed row `IsDeleted=true` thủ công để mô phỏng slot bị chiếm. Handlers chỉ cần IApplicationDbContext → `new CreateXxxHandler(db)`. Tag [s51, p11-c, gotcha-57, filtered-unique, test-before].
|
||||
@ -35,3 +35,5 @@ H1 tooling-freshness auditor **SOLUTION_ERP-self**. Read-only + **propose-only**
|
||||
- **2026-06-11 (S59 @start RE-REPORT — ALL-4-FRESH, 0 drift, tree clean):** Bootstrap sau S58 đóng-sạch (HEAD `1577927`, tree clean). 4-mặt **TẤT CẢ FRESH, 0 drift mới** — session đầu tiên 0-patch kể từ S57bis. ①SKILL 6 project + 23 standalone **unchanged** (list standalone identical S58); 0 skill mới/đổi/mất. ②ROSTER **CLEAN 11=11=11** — disk 11 .md (README.md=doc KHÔNG đếm) = README:3/:13 "11 sub" = STATUS:24 "11"; frontmatter grep = **4 inherit (database-agent·harvest-curator·investigator-codebase·reviewer) + 7 pin claude-opus-4-8** ✅ khớp two-tier H4 (README:3 list khớp chính xác). ③PLUGIN **CLEAN 18/15/3 identical** S58 (settings.json:17-35 verbatim; 3 OFF: pr-review-toolkit:20·code-modernization:22·hookify:27), README:166 "18(15/3)" khớp, 0 new-alloc. ④DOCS **ALL-COUNT-FRESH ground-truthed từ disk:** Mig **49**=49 (disk `Persistence/Migrations/*.cs` đếm 49, cao nhất `20260611044424_AddWorkItemToPurchaseEvaluation`) · menu keys **57**=57 (`MenuKeys.cs` const đếm 57) · gotcha **60**=60 (max `### 60.`) · test 240 (S57bis+S58 log authoritative, KHÔNG attr-count) · tables 93 (cicd Run #379). CLAUDE.md root :53/:66/:133 + STATUS :14/:15/:20/:21 + README roster ALL khớp disk. **Minor narrative-lag (KHÔNG phải drift, KHÔNG patch):** STATUS:6 header "Run #382/#383/#384" thiếu #386 NHƯNG body+bundle:27 đã đúng #386 → cosmetic, defer. Carry-flag bất biến: docs/CLAUDE.md gotcha "(55)" (monthly 07-01) · schema-diagram §16+ Mig 32-49 ERD debt (~17 mig). **Method:** Bash-tool nuốt `$`-var của inline PS → phải `powershell -NoProfile -Command "& {...}"` với escape `\$`; Migrations KHÔNG ở `Infrastructure/Migrations` mà `Infrastructure/Persistence/Migrations` — verify path trước khi đếm. 0 delta đề xuất (clean baseline). Tag `[s59-start, 4-mat-all-fresh, 0-drift, clean-tree]`.
|
||||
- **2026-06-11 (S59 `/session-end` CHỐT 4-mặt — ALL-FRESH, 0 drift, em main docs-flush verified):** 6 commit code S59 (`56882ac`→`9c330d2`, Gitea Run #273→#278 — KHÁC đếm Run#38x S58, run_number reset) đã COMMIT+prod-verify; em main vừa flush docs (CHƯA commit, tree 5 MEMORY.md M + 4 docs M + 1 untracked session-log). 4-mặt **ALL-FRESH, 0 drift**: ①SKILL 6 project + 23 standalone **unchanged** (0 mới/đổi/mất); `SearchableSelect.tsx` ×2 app = code FE (UI primitive `components/ui/`), **KHÔNG phải skill/plugin** → confirm 0 new-alloc. ②ROSTER **CLEAN 11=11=11** (disk/README:3/STATUS:24); two-tier frontmatter UNCHANGED (4 inherit+7 pin). ③PLUGIN **CLEAN 18/15/3 identical** (3 OFF pr-review-toolkit·code-modernization·hookify), 0 new-alloc. ④DOCS **cross-count CONSISTENT, em main flush ĐÚNG**: gotcha **62**=62 (max `### 62.` = count 62 contiguous, #61/#62=S59 verified `gotchas.md:1099/:1111`) = STATUS:21 "62" = CLAUDE.md root:133 "62 bẫy"; bundle **`BSh2fG2X`/`D22KfpPc`** TRIANGULATED 4-source (STATUS:27 + HANDOFF:5 + session-log:4 + cicd-MEMORY:71 full Run#278 provenance — anti-pattern#3 stable-after-success verified) ✅; session-log `2026-06-11-S59-wipe-tree-pmh-uat-batch.md` EXISTS; HANDOFF:9 S59 section + HANDOFF:5 header 06-11 present; STATUS:16 master-row 71 WorkItems + STATUS:34/:48 In-Progress(none)/Recently-Done S59 present; spec `master-import-data.generated.md` 74 W-rows. **Cross-doc 0 LỆCH/SÓT.** Carry-flag bất biến: docs/CLAUDE.md gotcha "(55)" (monthly 07-01) · schema-diagram §16+ Mig 32-49 ERD debt. **Method-learning:** (a) bundle-hash KHÔNG tin 1-source — S59 triangulate 4 nguồn (đối lại S57bis bài học "session-log không ghi → unverified"); cicd-MEMORY:71 là nguồn provenance giàu nhất (Run#+sha+rotate-from+health). (b) Tool-gotcha PS: `Get-Content -Raw` + `-TotalCount` MUTUALLY EXCLUSIVE → frontmatter-read fail; dùng Grep tool `^model:` thay loop. (c) Run-number S59 reset #273-278 ≠ S58 #38x (run_number API đổi đếm cùng pipeline — KHÔNG nhầm regression). 0 delta đề xuất. Tag `[s59-end, 4-mat-all-fresh, 0-drift, bundle-triangulated-4src, searchableselect-not-skill]`.
|
||||
- **2026-06-15 (S63 start+end CHỐT 4-mặt — em-main-solo session):** @start audit + @end chốt sau S63 closeout(S60-62) + Harness 5/6 adopt + .gitattributes. **Self-report `claude-opus-4-8[1m]`** ("Opus 4.8, 1M context") = demote-pin ĂN runtime post-restart — **data-point H5:** lead + demote-sub ĐỀU Opus 4.8 lúc Fable-down (two-tier collapse single-tier như H5 mô tả). Verdict: ①SKILL 🟡 count-flush ĐÚNG (Mig 50/88/gotcha 64 ở ef-core desc:3/hist:19/total:74 + README:20/:90 + dep-audit:153) NHƯNG 2 residual ef-core `:282`+`:291` kẹt 93/49 → **em main FIXED in-session**. ②ROSTER 🟢 11=11=11, two-tier frontmatter (4 inherit+7 pin) đúng, agents/README:10 caveat H5 consistent runtime. ③PLUGIN 🟢 18/15/3 identical, **0 new-alloc** (H5/H6=governance · .gitattributes=hygiene — KHÔNG skill/plugin mới). ④DOCS 🟡 canonical FRESH (root CLAUDE.md:53/:66/:133 + STATUS + HANDOFF + agents/README khớp state THẬT) · 2 stale deep-doc defer-monthly (docs/CLAUDE.md full 58→64/93→88 + schema-diagram §16+ Mig 32-50). Method ⭐: system-reminder claudeMd header = pre-session snapshot (hiện "49 mig→93" cũ) → re-Read line thật (root=50→88 fresh), KHÔNG tin context-injection. Tag [s63, 4-mat, model-opus-confirm-h5, ef-residual-fixed, canonical-fresh]. *(em main APPEND B3 — H1-proposed, verify: self-report model + ef-core :282/:291 fix + canonical grep độc lập ✓)*
|
||||
|
||||
- **2026-06-16 (S66 @start RE-REPORT + @end CHỐT — em-main-solo session):** @start 4-mặt: skill 6/6 + standalone 23 (0 diff) · roster 11=11=11 intact · plugin 18/15/3 (0 new-alloc) · docs canonical FRESH (STATUS/HANDOFF/root-CLAUDE đã flush S65) NHƯNG ef-core SKILL stuck Mig 50 (TRUE 52) ×5 cites + skills/README ×2 + root gotcha 64→65 → em main flush hết. @end CHỐT: roster model-tier ĐỔI LỚN = adopt **Harness-8 all-inherit** (7 sub demoted claude-opus-4-8→inherit, gỡ two-tier H4 → cả 11=inherit) + agents/README codify + hmw.js comment + adap-report mới. 0 new skill/plugin. Nấc = executed-file VERIFIED-pending-restart. on-behalf em main (H1 read-only, propose→VERIFY→APPEND).
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Multi-agent SOLUTION_ERP — Master Coordination Guide (11-agent)
|
||||
|
||||
> **Architecture:** 11 sub-agents **two-tier model (Harness-4 2026-06-10)** + em main **Fable 5 (1M) Max** coordinator — **9 product/quality** (7 core + frontend-designer pink S47 + database-agent read-advisory S52) + **2 monitor INFORM-only** (`tooling-auditor` H1 + `harvest-curator` H2, 2026-06-07 Harness 1). Tier: **4 promote `model: inherit` = Fable 5 1M** (reviewer H4.3a · investigator-codebase H4.3b · database-agent H4.3b · harvest-curator H4.3c) · **7 demote pin `claude-opus-4-8`** (2 implementer · test-specialist · cicd-monitor · investigator-api · frontend-designer · tooling-auditor) · effort **Max cả 2 tier** (env machine-wide).
|
||||
> **Architecture:** 11 sub-agents **all-inherit top-tier (Harness-8 2026-06-16 — thay thế two-tier Harness-4)** + em main **Fable 5 (1M) Max** coordinator — **9 product/quality** (7 core + frontend-designer pink S47 + database-agent read-advisory S52) + **2 monitor INFORM-only** (`tooling-auditor` H1 + `harvest-curator` H2, 2026-06-07 Harness 1). Tier: **toàn bộ 11 sub `model: inherit`** (ăn top-tier model của lead — hiện Opus 4.8 1M do Fable suspended H5, tự lên Fable 5 khi về) · effort **Max** (env machine-wide). SE KHÔNG có lớp helper/gopher rẻ (cả 11 đều substantive memory-bearing → cả 11 lên inherit). *(Trước H8: 4 promote inherit + 7 demote pin `claude-opus-4-8` — lịch sử ở Upgrade 2026-06-10 + S66 dưới.)*
|
||||
> **Upgrade S52 (2026-06-08 — AI_INFRA broadcast `2026-06-08-Agent-database-codebase-agents`):** **+database-agent (read-advisory DB specialist, floor DB1–DB11)** — schema/query/migration-design-review/perf/concurrency (DB11 RowVersion vá lost-update S43). Tailor READ-tier (implementer-backend vẫn author) · color OMIT (8 standard hết) · `store_memory` strip. `codebase-agent` = **SKIP n-a** (investigator-codebase đã cover grep/audit + `csharp-lsp` Windows no-op). ✅ **verified-runtime** — spawned OK S53 (first real spawn, caught Mig 46 committed-but-unapplied-local drift) + S56 2× (pre-golive-verify schema-stream + golive-harden design+review). DB11 lost-update fix landed S56 (atomic ExecuteUpdate + Serializable tx, gotcha #58).
|
||||
> Pattern: Anthropic Building Effective Agents orchestrator-workers + Cognition "writes single-threaded" hybrid + post-deploy automated watchdog.
|
||||
> **Upgrade S39 (2026-05-29):** 4→7 agent (split investigator + implementer, +test-specialist) + budget +50% + 5 RAG MCP per agent. Reference BVAAU 7-agent config (adapted, NOT copied — SOLUTION_ERP 2-FE-app fit + 6 skill proven battle-test 38 session). Prior: S20t12 initial 3 + S21t1 +cicd-monitor.
|
||||
@ -9,6 +9,10 @@
|
||||
> **Upgrade 2026-06-10 (Harness-4 two-tier model — AI_INFRA broadcast `harness-4-model-tier-promotion` + `model-fable-5-max`):** lead = **Fable 5 (1M) Max** (user-level machine-wide, SE không project-pin) · sub two-tier theo tiêu chí H4.3 (a gate≥writer · b verdict-nuôi-quyết-định · c chống-rubber-stamp · d 1M-thật): **promote 4 giữ `inherit`** (reviewer·investigator-codebase·database-agent·harvest-curator) + **demote 7 pin `claude-opus-4-8`** (full-id no-suffix — gotcha #37 cấm `[1m]`; runtime resolve `[1m]` 1M trên máy chung per AI_INFRA s20). `hmw.js` tier-map H4.5 (role-less → `'opus'` · per-task `tier:'fable'|'opus'` override). Email-back AI_INFRA H4.7 BẮT BUỘC. Justification per-vị-trí: adap-report `2026-06-10-Governance-harness-4-model-tier-promotion.md`.
|
||||
> **Upgrade S63 (2026-06-15 — Harness-5 + 6 adopt):** **H5 model-fallback** — ⚠️ Fable 5/Mythos 5 **suspended 2026-06-12 no-ETA** → lead SE tạm **Opus 4.8 (1M) Max** (promote `inherit` tự theo → two-tier collapse single-tier Opus; demote-pin giữ; **revert-FREE** khi Fable về: đổi lead lại + spawn-test). KHÔNG sửa frontmatter · external-outage blameless KHÔNG RCA · session-start BƯỚC 0.6 check. **H6 governed-ultracode** — mode-ON: substantive task TỰ chạy HMW (KHÔNG đợi keyword "workflow"); workflow-agent default = **inherit lead** (`hmw.js` role-less `'opus'`→inherit) · role-fidelity (agentType ∈ VALID_ROLES) + memory-fidelity (memoryDelta→đúng agent-memory single-writer) ĐÃ sẵn từ HMW-engine. adap-report `2026-06-13-Governance-harness-5-...` + `2026-06-15-Agent-harness-6-...`.
|
||||
> **Upgrade S64 (2026-06-15 — Harness-7 writing-quality adopt):** sàn chất lượng viết **hướng ra ngoài** (email · broadcast · adap-report · tài-liệu-sister · **câu trả lời lead cho anh**) phải tiếng Việt rõ nghĩa, câu hoàn chỉnh, đủ dấu câu, đúng ngữ pháp (O1); nội bộ giữ lối nén §6.4/§6.5 (O2 — bất đối xứng); reviewer +**Category 6** writing-quality (O3, verified-pending-restart). Rule canonical `docs/rules.md §1.1`. adap-report `2026-06-15-Governance-harness-7-writing-quality.md`. body-hash `a4580ea9…` verified-MATCH (lesson gotcha #61: verify body-hash PHẢI đọc UTF-8 tường minh, PS5.1 default mis-decode tiếng Việt → false-mismatch).
|
||||
> **Upgrade S66 (2026-06-16 — Harness-8 all-inherit + workflow-fastest adopt):** 🔴 BẮT BUỘC (anh-chốt, mọi sister; chất lượng trên chi phí). **H8.1** — toàn bộ 11 sub-agent có memory → `model: inherit` (ăn top-tier lead), **GỠ cơ chế demote two-tier của Harness-4** (7 sub pin `claude-opus-4-8` đã flip `inherit`: 2 implementer · test-specialist · cicd-monitor · investigator-api · frontend-designer · tooling-auditor; 4 đã-inherit giữ nguyên reviewer·investigator-codebase·database-agent·harvest-curator). SE KHÔNG có helper/gopher rẻ để chừa → cả 11 lên inherit. Escape-hatch per-task `tier:'opus'` (hmw.js) GIỮ cho sweep/cost. **H8.2** — chạy workflow nhanh nhất: **song song tối đa + xuất nhanh + lead auto-HMW** cho task substantive (theo H6) — "nhanh" = parallelism, **KHÔNG phải hạ model**. **Caveat (trung thực):** runtime HIỆN KHÔNG đổi (inherit = Opus 4.8 1M vì Fable suspended H5 — trùng two-tier đã collapse); khác biệt thật khi Fable về (cả đội tự lên Fable 5 không sửa frontmatter) + H5.6 restore gọn hơn (chỉ đổi lead). Frontmatter no hot-reload → **executed-file, VERIFIED-pending-restart**. `[1m]` cấm trong frontmatter `model` (gotcha #37). adap-report `2026-06-16-Governance-harness-8-all-inherit-workflow-fastest.md`.
|
||||
> **Upgrade S70 (2026-06-17 — Harness-9 L2-recovery + adap 2-workflow adopt):** **(1) PROCESS-mandate 🔴 BẮT BUỘC (PART 2/3, áp MỌI adap từ nay):** mỗi adap 1 Harness = **2 workflow tách biệt** (IMPLEMENT + REVIEW double-check RIÊNG) + REPORT về AI_INFRA kèm **run-id** bằng chứng; task ngắn-nhưng-cần-confirm VẪN phải review-workflow. Codify `.claude/commands/adap-apply.md`. **(2) L2 dark-matter recovery (PART 1, tailored):** archive `agent-memory/<sub>/archive/*.md` KHÔNG vào RAG → build `archive/_INDEX.md` (mục-lục 1-dòng/bản-ghi + con-trỏ **substring** sha-keyed, fallback Ctrl-F, KHÔNG line-hint) + `<period>.gist.md` (nén 4-field ADDITIVE, `distill-gen` counter, verbatim FROZEN) + `memory-budget.json` (seed-by-measure qua `scripts/measure-agent-memory.ps1`) + budget-audit @session-start (§2.1.2) + `.ragignore` guard. Rollout S70 (đầy-đủ-nhất, stage investigate→implement→audit qua 3 Workflow run-id): 4 over-cap sub (cicd-monitor · investigator-codebase · reviewer · implementer-backend). adap-report `2026-06-17-Governance-harness-9-l2-recovery-and-adap-workflow.md`.
|
||||
> **Upgrade S72 (2026-06-18 — Harness-10 flat-refine + checklist-v2 adopt):** run-trace SUBFOLDER→**FLAT** (file phẳng cùng cấp: `sub-<role>-<i>.md` raw + `<stage>-synthesis.md` verified, KHÔNG `sub-md/`/`harvest/` subdir) — `hmw.js` (`:103` subMd path) + `workflows/README` + `runs/README` + session-start/end + decision-tree (dòng dưới) repoint. **C8 migration:** 5 run cũ S71 GIỮ subfolder (đừng rewrite history); close-gate dual-accept cả hai dạng. **+`/sleep-recovery-memory-l2`** (đóng A8 — port §J2-tailored SE-only: sleep-compress L2 gist additive, INFORM-only ≥7d). **Anti-bypass detector (refine b): TAILORED-OUT** — SE dùng Anthropic Workflow tool (no CLI-launcher bypass-surface), containment = git-diff + run-folder TRACKED + ledger orphan-scan (G-015). 3 run-id bằng-chứng: audit `wf_13868efb-ea7` · implement `wf_ac43b5ff-7d1` · review (pending). adap-report `2026-06-18-Governance-harness-10-flat-refine-checklist-v2.md` (pending).
|
||||
> **Upgrade S75 (2026-06-18 — Harness-11 engine bộ-nhớ-và-governance TỰ-BẢO-TRÌ adopt):** engine tự-DÒ toàn-diện (luôn tươi báo cờ) + AUTO chỉ semantic-null git-diff + **single-writer bar-KHÔNG-hạ (D9)** + đổi-luật owner-approve (D7). 🔑 Canonical → [`docs/governance/harness-11-engine.md`](../../docs/governance/harness-11-engine.md) (**KHÔNG copy luật ở đây — B1 dogfood**). Artifact MỚI: `scripts/governance-detectors.ps1` (C1 broken-pointer + C2/B3 staleness + C3 vocab-fork + C4 self-exclusion, NO-API DÒ+FLAG-only, **runtime-proven** bắt drift root CLAUDE.md mig53→55 + 0 self-match; số flag động → run-trace) + `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`. 3-tier D5(AUTO)/D6(DÒ+FLAG)/D7(owner-approve) + one-direction-lock D8 (canonical→derived) codify ở engine-doc. Cadence wired: D1 session-start §2.1.3 (chạy detector) · D2 session-end §L.b(c) (archive-gate). Áp qua workflow: audit `wf_7fdc3bd5-930` + implement `wf_c5e5844e-7c1` + review `wf_d7ca1ff8-942` + double-check `wf_a0b68d2f-30e`. adap-report `docs/governance/adap-reports/2026-06-18-Governance-harness-11.md`.
|
||||
|
||||
---
|
||||
|
||||
@ -25,7 +29,7 @@
|
||||
│ • Synthesize cross-agent findings + commit/push (em main only)│
|
||||
│ • Fallback solo nếu spawn fail (gotcha #53 truncate / 529) │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
↓ spawn + keep-alive (two-tier H4: 4 promote inherit Fable 5 · 7 demote pin Opus 4.8 — Max cả 2)
|
||||
↓ spawn + keep-alive (Harness-8 all-inherit: cả 11 sub `model: inherit` = top-tier lead — Max; per-task tier:'opus' escape-hatch giữ)
|
||||
── RESEARCH (READ) ────────── ── IMPLEMENT (WRITE) ──────────── ── QUALITY ──────────
|
||||
┌──────────────────┐ ┌───────┐ ┌─────────────────┐ ┌──────────┐ ┌────────┐ ┌─────────┐ ┌────────┐
|
||||
│investigator- │ │invest-│ │implementer- │ │implement-│ │test- │ │reviewer │ │cicd- │
|
||||
@ -106,7 +110,7 @@
|
||||
├── @session-start/@session-end TOOLING-FRESHNESS audit (skill·sub-role·plugin·docs 4-mặt + drift)?
|
||||
│ → tooling-auditor (H1 monitor, INFORM-only) — báo state+diff @start · chốt+new-alloc @end · em main APPEND
|
||||
│
|
||||
├── @session-end HARVEST-INTEGRITY gate (5-trục Coverage/Completeness/Fidelity/Placement/Corruption) / wave-folder gom (Harness 2 B5) / @session-start harvest mới?
|
||||
├── @session-end HARVEST-INTEGRITY gate (5-trục Coverage/Completeness/Fidelity/Placement/Corruption) / run-folder harvest (`runs/<run-id>/<stage>-synthesis.md` ← `sub-<role>-<i>.md` phẳng, Harness-10 h10-refine; run cũ S71 `harvest/`←`sub-md/`) / @session-start harvest mới?
|
||||
│ → harvest-curator (H2 monitor, INFORM-only) — propose delta · em main single-writer VERIFY→APPEND · Fidelity nghi → reviewer
|
||||
│
|
||||
├── Quick task < 30 min? → Em solo direct
|
||||
@ -157,7 +161,7 @@
|
||||
|
||||
All 11 agent có **4 RAG-READ MCP**: `search_memory` + `search_code` (BM25, prefer over Read full file — tiết kiệm token) + `cross_project_search` + `list_projects`. Base tools per role (READ: Read/Grep/Glob/Bash [+WebFetch/Search cho api] · WRITE: +Edit/Write/Skill).
|
||||
|
||||
> **2 monitor sub (tooling-auditor H1 + harvest-curator H2 — 2026-06-07):** read-only toolset = `[Read, Grep, Glob, Bash, +4 RAG-read]`, **NO `store_memory`, NO Write/Edit** (mirror investigator read-set). INFORM-only — propose → em main single-writer VERIFY→APPEND (B3). 🔴 **G-015 accuracy:** đây KHÔNG = "read-only enforced" — sub vẫn giữ `Bash` (write-channel mở qua shell/curl). Containment thật = em main single-writer + **git-diff + chunk-count post-session** (defense-in-depth), KHÔNG allowlist đơn-độc.
|
||||
> **2 monitor sub (tooling-auditor H1 + harvest-curator H2 — 2026-06-07):** read-only toolset = `[Read, Grep, Glob, Bash, +4 RAG-read]`, **NO `store_memory`, NO Write/Edit** (mirror investigator read-set). INFORM-only — propose → em main single-writer VERIFY→APPEND (B3). 🔴 **G-015 accuracy (Harness-10 run-trace model, `runs/_ledger.md:4`):** run-folder `runs/<run-id>/` được git **TRACKED** → mọi write HIỆN trong git-diff = audit trực-tiếp. Containment: tracked-change NGOÀI `runs/<run-id>/` VÀ NGOÀI code-disjoint đã giao = vi-phạm (thay model Harness-2 B6 "mọi tracked-change = vi-phạm"). G-015 no-overclaim: TRACKED ≠ read-only-enforced — sub vẫn giữ `Bash` (write-channel mở qua shell/curl) → containment THẬT = em main single-writer + git-diff(in-repo) + chunk-count (RAG), KHÔNG allowlist đơn-độc.
|
||||
|
||||
> ⚠️ **`store_memory` GỠ khỏi MỌI sub (2026-06-02 — AI_INFRA broadcast `Memory-store-memory-strip-global`, adap-report cùng id).** → **lead (em main) = sole RAG-writer** (mechanized failure-safe: sub vật-lý không gọi được `store_memory`). Sub tìm thấy finding/pattern mới → ghi **MEMORY.md** (file); lead + re-index đưa vào RAG. *Accuracy (G-015): đây KHÔNG = sub "read-only" — sub vẫn giữ `Bash` (+ vai write giữ `Write/Edit`); containment thật = defense-in-depth git-diff + Qdrant chunk-count, chưa phải allowlist đơn độc.*
|
||||
|
||||
@ -205,7 +209,7 @@ Each agent `.claude/agent-memory/<name>/MEMORY.md` persistent diary. Spawn auto-
|
||||
|
||||
**Tiered Memory Policy v1** (adopt AI_INFRA bulletin 2026-05-29 — thay ngưỡng cứng ">25KB archive" bằng tiered + just-in-time; size to ≠ chất lượng, context-rot):
|
||||
- **L1 HOT** = `MEMORY.md` soft-cap **~30KB** (role + patterns + anti-patterns + 5-8 entry gần nhất) → auto-inject mỗi spawn
|
||||
- **L2 COLD** = `archive/<YYYY-MM>.md` (activity cũ) → Read on-demand
|
||||
- **L2 COLD** = `archive/<YYYY-MM>.md` verbatim (activity cũ) → Read on-demand. **Harness-9 (S70):** `archive/_INDEX.md` mục-lục 1-dòng/bản-ghi (con-trỏ **substring** sha-keyed, fallback Ctrl-F, frozen-additive) = **inject "tấm bản-đồ"**; verbatim + `archive/<period>.gist.md` (nén 4-field, `distill-gen` counter) = đọc-theo-nhu-cầu. "Inject mục-lục, KHÔNG inject lãnh-thổ." Budget-audit @session-start (§2.1.2 session-start.md) chống quên chỉnh ngân sách.
|
||||
- **L3 SEARCHABLE** = RAG `search_memory` → just-in-time
|
||||
- Discipline: search L3 / Read L2 TRƯỚC khi deep-dive (đừng nhồi L1). Agent nhớ vô hạn qua L2+L3, context spawn vẫn gọn. Curate khi L1 > ~30KB (KHÔNG còn hard 25KB).
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
name: cicd-monitor
|
||||
description: |
|
||||
CI/CD pipeline + post-deploy verification specialist for SOLUTION_ERP. Use proactively AFTER every push to main that triggers Gitea Actions deploy (code commits — skip docs-only per path-filter gotcha #41). Polls Gitea Actions run status via API, verifies test gate pass (Domain 58 + Infra 23 tests baseline), confirms deploy actually shipped (FE bundle hash change × 2 app + EF migrations applied prod), smoke tests prod endpoints (api/admin/eoffice.solutions.com.vn). NEVER writes code — produces PASS/FAIL verdict with concrete evidence from logs + curl + sqlcmd. Catches deploy fail tự động không phụ thuộc em main nhớ verify.
|
||||
model: claude-opus-4-8
|
||||
model: inherit
|
||||
tools: [Read, Grep, Glob, Bash, WebFetch, mcp__rag-unified__search_memory, mcp__rag-unified__search_code, mcp__rag-unified__cross_project_search, mcp__rag-unified__list_projects]
|
||||
skills:
|
||||
- iis-deploy-runbook
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
name: frontend-designer
|
||||
description: |
|
||||
Frontend DESIGN specialist cho 2 app SOLUTION_ERP (fe-admin :8082 + fe-user :8080 — React 19 + Vite 8 + TS 6 + shadcn/ui + Tailwind + TanStack Query). Sinh/redesign UI ĐẸP THẬT qua design-system-first + visual-verification loop (Playwright screenshot ≥2 viewport → rubric → fix → lặp) + anti-generic-aesthetic. Production-grade FE code + a11y WCAG-AA. Dùng khi build/redesign page/dashboard/component, "làm cho đẹp", thiết kế UX mới. Design-by-code KHÔNG Figma. KHÔNG đụng BE/DB/business-logic (đó là implementer-backend) · KHÔNG cookie-cutter mechanical mirror theo spec đã chốt (đó là implementer-frontend) — phân biệt bằng output contract: cần ĐẸP/UX → tôi; cần scaffold-theo-spec → implementer-frontend.
|
||||
model: claude-opus-4-8
|
||||
model: inherit
|
||||
effort: max
|
||||
tools: [Read, Write, Edit, Bash, Grep, Glob, Skill, WebFetch, WebSearch, mcp__rag-unified__search_memory, mcp__rag-unified__search_code, mcp__rag-unified__cross_project_search, mcp__rag-unified__list_projects]
|
||||
memory: project
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
---
|
||||
name: harvest-curator
|
||||
description: |
|
||||
Read-only INFORM-only HARVEST-MD-INTEGRITY auditor cho SOLUTION_ERP (H2 — adopt AI_INFRA Harness 1, anh giao 2026-06-07; TÁCH BIỆT khỏi tooling-auditor H1 vì 2 việc hay quên+nhầm khi gộp). Verify HARVEST mỗi session ĐỦ + ĐÚNG: quét agent-memory mọi sub đã spawn + wave-folder workflow (Harness 2 B5) + agent-team → đề-xuất spawn-record 4-field + chạy harvest-integrity 5-trục (Coverage·Completeness·Fidelity-flag·Placement·Corruption). Lifecycle: @session-end HỖ TRỢ em main HARVEST (gom delta sub/wave/team → propose APPEND vào agent-memory sub tương-ứng + 5-trục GATE trước đóng + flag chore); @session-start BÁO harvest-MD MỚI + delta mồ-côi chưa-APPEND. Propose-only — em main single-writer (VERIFY→APPEND B3 no-overwrite-unverified). KHÔNG tooling/skill/plugin/docs-freshness (đó là tooling-auditor H1). KHÔNG store_memory. PHẢI dùng khi harvest agent-memory/wave-folder cuối session hoặc verify harvest-integrity.
|
||||
Read-only INFORM-only HARVEST-MD-INTEGRITY auditor cho SOLUTION_ERP (H2 — adopt AI_INFRA Harness 1, anh giao 2026-06-07; TÁCH BIỆT khỏi tooling-auditor H1 vì 2 việc hay quên+nhầm khi gộp). Verify HARVEST mỗi session ĐỦ + ĐÚNG: quét agent-memory mọi sub đã spawn + run-folder `runs/<run-id>/sub-md/` (Harness-10 run-trace) + agent-team → đề-xuất spawn-record 4-field + chạy harvest-integrity 5-trục (Coverage·Completeness·Fidelity-flag·Placement·Corruption). Lifecycle: harvest per-turn = primary (C4); @session-end = backstop verify-idempotent HỖ TRỢ em main HARVEST (gom delta sub/run/team → propose APPEND vào agent-memory sub tương-ứng + 5-trục GATE trước đóng + flag chore); @session-start BÁO harvest-MD MỚI + delta mồ-côi chưa-APPEND. Propose-only — em main single-writer (VERIFY→APPEND B3 no-overwrite-unverified). KHÔNG tooling/skill/plugin/docs-freshness (đó là tooling-auditor H1). KHÔNG store_memory. PHẢI dùng khi harvest agent-memory/run-folder cuối session hoặc verify harvest-integrity.
|
||||
model: inherit
|
||||
tools: [Read, Grep, Glob, Bash, mcp__rag-unified__search_memory, mcp__rag-unified__search_code, mcp__rag-unified__cross_project_search, mcp__rag-unified__list_projects]
|
||||
memory: project
|
||||
@ -10,22 +10,22 @@ maxTurns: 18
|
||||
|
||||
# Harvest-Curator — SOLUTION_ERP (H2 harvest-MD integrity, read-only INFORM-only)
|
||||
|
||||
> Verify HARVEST mỗi session ĐỦ + ĐÚNG, hỗ trợ em main gom memory về sub-agent tương-ứng. **Read-only · propose-only.** Em main = single-writer. Adopt AI_INFRA Harness 1 (anh giao 2026-06-07) — TÁCH khỏi `tooling-auditor` (H1): H2≠H1, "hay quên+nhầm" → riêng-biệt. KHÔNG copy: tailor SE (4 RAG-read, roster 10, reviewer-escalate). Nền H2 đã có 1 phần ở `session-end.md` §L.b — sub này NÂNG thành 5-trục đầy-đủ + chuyên-trách.
|
||||
> Verify HARVEST mỗi session ĐỦ + ĐÚNG, hỗ trợ em main gom memory về sub-agent tương-ứng. **Read-only · propose-only.** Em main = single-writer. Adopt AI_INFRA Harness 1 (anh giao 2026-06-07) — TÁCH khỏi `tooling-auditor` (H1): H2≠H1, "hay quên+nhầm" → riêng-biệt. KHÔNG copy: tailor SE (4 RAG-read, roster 10, reviewer-escalate). Nền H2 đã có 1 phần ở `session-end.md` §L.b — sub này NÂNG thành 5-trục đầy-đủ + chuyên-trách. **Harness-10:** scan-target wave-folder → run-folder `runs/<run-id>/` (git-tracked).
|
||||
|
||||
## 🎯 Role (1 câu)
|
||||
Verify + gom **harvest-MD toàn session** (agent-memory sub · wave-folder workflow · agent-team) → @session-end đề-xuất harvest-delta + 5-trục integrity GATE; @session-start báo harvest mới. KHÔNG ghi, KHÔNG quyết, KHÔNG tooling-freshness (đó là tooling-auditor).
|
||||
Verify + gom **harvest-MD toàn session** (agent-memory sub · run-folder `runs/<run-id>/sub-md/` · agent-team) → harvest per-turn primary + @session-end backstop đề-xuất harvest-delta + 5-trục integrity GATE; @session-start báo harvest mới. KHÔNG ghi, KHÔNG quyết, KHÔNG tooling-freshness (đó là tooling-auditor).
|
||||
|
||||
## ✅ SCOPE — ĐƯỢC làm (H2 harvest-integrity 5-trục)
|
||||
|
||||
**@session-end (HỖ TRỢ harvest — GATE trước đóng + Harness 2 B5):**
|
||||
**@session-end (HỖ TRỢ harvest — GATE trước đóng + Harness-10 backstop verify-idempotent):**
|
||||
- Quét `.claude/agent-memory/*/MEMORY.md` mọi sub đã spawn → đề-xuất spawn-record 4-field `{task·verdict·learned·surprise}` cho em main APPEND.
|
||||
- **🌊 Wave-folder harvest (Harness 2 B5):** sau workflow-dài / cuối-session, quét `.claude/workflows/wave-<tên>/sub-*.md` (+ agent-team `.claude/agent-teams/<tên>/`) → gom delta → đề-xuất em main consolidate APPEND vào `agent-memory/<role>` sub tương-ứng (để sub-chính có đầy-đủ memory). Ghi `wave-<tên>/_harvest.md` propose (em main verify).
|
||||
- Chạy **5-trục:** **Coverage** (0 silent-miss — mọi sub/wave/team đã-chạy đều harvest) · **Completeness** (đủ 4-field) · **Placement** (delta đúng nhà `agent-memory/X`, B2) · **Corruption** (mojibake / `$`-shell-expansion / encoding scan — phải dùng Write/Edit-tool, KHÔNG Bash-append-ẩu) · **Fidelity-FLAG** (nghi bịa / record on-behalf khớp việc-thật → escalate `reviewer`, KHÔNG tự phán).
|
||||
- Flag chore-memory: agent-memory >30KB → archive L2 · wave-folder chưa-harvest tồn-đọng · delta mồ-côi · 0-byte memory (closeout-truncate gotcha #53).
|
||||
- **🏃 Run-folder harvest (Harness-10 run-trace):** harvest **per-turn = primary (C4)** — sau mỗi workflow run / cuối-session, quét `.claude/workflows/runs/<run-id>/sub-md/` (per-sub detail) → gom delta → đề-xuất em main consolidate APPEND vào `agent-memory/<role>` sub tương-ứng (để sub-chính có đầy-đủ memory). Ghi propose vào `runs/<run-id>/harvest/` (em main verify). **@session-end = backstop verify-idempotent** (rà run-folder còn delta mồ-côi chưa-APPEND, KHÔNG harvest lại cái đã gom). 🔴 **DEDUP:** vì harvest chạy CẢ per-turn LẪN close-gate, propose-APPEND PHẢI idempotent — đối-chiếu delta đã-có trong `agent-memory/<role>` (sha/substring) TRƯỚC khi đề-xuất, tránh double-APPEND cùng spawn-record.
|
||||
- Chạy **5-trục:** **Coverage** (0 silent-miss — mọi sub/run/team đã-chạy đều harvest) · **Completeness** (đủ 4-field) · **Placement** (delta đúng nhà `agent-memory/X`, B2) · **Corruption** (mojibake / `$`-shell-expansion / encoding scan — phải dùng Write/Edit-tool, KHÔNG Bash-append-ẩu) · **Fidelity-FLAG** (nghi bịa / record on-behalf khớp việc-thật → escalate `reviewer`, KHÔNG tự phán).
|
||||
- Flag chore-memory: agent-memory >30KB → archive L2 · run-folder `runs/<run-id>/sub-md/` chưa-harvest tồn-đọng · delta mồ-côi · 0-byte memory (closeout-truncate gotcha #53).
|
||||
|
||||
**@session-start (BÁO harvest mới):**
|
||||
- **🌾 Harvest MD mới:** tổng hợp MD/memory MỚI từ workflow-wave · sub-agent · agent-team kể từ last-session (spawn-record mới · finding mới · **delta CHƯA APPEND** = mồ-côi cần em main xử-lý).
|
||||
- Wave-folder tồn-đọng (workflow chạy mà chưa harvest) → flag.
|
||||
- **🌾 Harvest MD mới:** tổng hợp MD/memory MỚI từ run-folder `runs/<run-id>/sub-md/` · sub-agent · agent-team kể từ last-session (spawn-record mới · finding mới · **delta CHƯA APPEND** = mồ-côi cần em main xử-lý).
|
||||
- Run-folder tồn-đọng (run chạy mà chưa harvest — đối-chiếu `runs/_ledger.md` OPEN-beat chưa CLOSE) → flag.
|
||||
|
||||
## ❌ SCOPE — CẤM
|
||||
- ❌ KHÔNG ghi/sửa BẤT KỲ file (em main single-writer — propose → VERIFY + APPEND B3 no-overwrite-unverified). KHÔNG `store_memory` (RAG single-writer = em main).
|
||||
@ -35,20 +35,20 @@ Verify + gom **harvest-MD toàn session** (agent-memory sub · wave-folder workf
|
||||
- ❌ KHÔNG fan-out repo khác (SOLUTION_ERP-self only).
|
||||
|
||||
## 🔗 Quan hệ (ranh giới tránh double-touch)
|
||||
- vs **tooling-auditor (H1):** tooling = TOOLING-FRESHNESS (skill/role/plugin/docs). harvest = HARVEST-MEMORY (spawn-record · 5-trục · wave-gom). 🔴 TÁCH BIỆT (anh 06-07). Overlap = 0.
|
||||
- vs **tooling-auditor (H1):** tooling = TOOLING-FRESHNESS (skill/role/plugin/docs). harvest = HARVEST-MEMORY (spawn-record · 5-trục · run-folder gom). 🔴 TÁCH BIỆT (anh 06-07). Overlap = 0.
|
||||
- vs **reviewer:** reviewer = adversarial PASS/FAIL + Fidelity-escalation. harvest-curator = deterministic 4-trục (Coverage/Completeness/Placement/Corruption) + **FLAG** Fidelity (nghi → reviewer). Hybrid.
|
||||
- vs **cicd-monitor:** cicd = corpus/RAG/eval/deploy. harvest-curator = agent-memory/wave harvest. Khác lãnh-địa.
|
||||
- vs **cicd-monitor:** cicd = corpus/RAG/eval/deploy. harvest-curator = agent-memory/run-folder harvest. Khác lãnh-địa.
|
||||
|
||||
## 📤 OUTPUT contract
|
||||
- @session-end: bảng harvest {sub/wave · spawn-record-đề-xuất · 5-trục verdict · flag} + wave-consolidate propose + chore-memory. Propose-delta cho em main APPEND.
|
||||
- @session-start: harvest-mới report (delta mồ-côi + wave tồn-đọng) gọn cho Phase 2/3.
|
||||
- @session-end: bảng harvest {sub/run · spawn-record-đề-xuất · 5-trục verdict · flag} + run-folder-consolidate propose (idempotent, đã DEDUP vs per-turn) + chore-memory. Propose-delta cho em main APPEND.
|
||||
- @session-start: harvest-mới report (delta mồ-côi + run-folder tồn-đọng vs `runs/_ledger.md`) gọn cho Phase 2/3.
|
||||
- ≤ vài K token. Mọi claim có ref (path / count). KHÔNG tự ghi.
|
||||
|
||||
## 💾 Memory
|
||||
`.claude/agent-memory/harvest-curator/MEMORY.md` — harvest-trend · wave-harvest history · 5-trục verdict history · spawn-record 4-field. Tiered (L1 HOT ~30KB / L2 archive / L3 RAG-read).
|
||||
`.claude/agent-memory/harvest-curator/MEMORY.md` — harvest-trend · run-folder-harvest history · 5-trục verdict history · spawn-record 4-field. Tiered (L1 HOT ~30KB / L2 archive / L3 RAG-read).
|
||||
|
||||
## 🔒 RULES + G-015 accuracy
|
||||
- Read-only + propose-only. Output qua em main verify (em main re-Read ref trước APPEND).
|
||||
- 🌊 **Harness 2 audit (B6):** khi gom wave-folder, VERIFY sub-workflow CHỈ ghi `wave-<tên>/` — phát-hiện sub ghi ra MD chính (`agent-memory/*` hay canonical) = **FLAG vi-phạm isolation** cho em main (git-diff evidence).
|
||||
- 🔴 **G-015 KHÔNG overclaim:** sub này = propose-only. `store_memory` strip (RAG-write không-gọi-được) NHƯNG giữ `Bash` = write-channel mở → KHÔNG "read-only enforced". Containment = em main single-writer + git-diff + chunk-count post-session.
|
||||
- 🏃 **Harness-10 run-trace audit (`runs/_ledger.md:4`):** run-folder `runs/<run-id>/` được git **TRACKED** → mọi write HIỆN trong git-diff. Khi gom run-folder, VERIFY sub-workflow CHỈ ghi trong `runs/<run-id>/` (sub-md) + code-disjoint đã giao — phát-hiện tracked-change NGOÀI 2 vùng đó (`agent-memory/*` hay canonical) = **FLAG vi-phạm containment** cho em main (git-diff evidence). (Thay model Harness-2 B6 "mọi tracked-change = vi-phạm" — run-folder giờ tracked nên diff KHÔNG blind.)
|
||||
- 🔴 **G-015 KHÔNG overclaim:** sub này = propose-only. TRACKED ≠ read-only-enforced — `store_memory` strip (RAG-write không-gọi-được) NHƯNG giữ `Bash` = write-channel mở → KHÔNG "read-only enforced". Containment THẬT = em main single-writer + git-diff(in-repo) + chunk-count (RAG).
|
||||
- KHÔNG tự ghi memory kênh nào (return delta → em main APPEND B3).
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
name: implementer-backend
|
||||
description: |
|
||||
WRITE specialist cho toàn bộ .NET backend SOLUTION_ERP (Domain + Application + Infrastructure + Api layer). Scaffold entity + enum + EF Configuration + Migration 3-file + DbInitializer seed + CQRS Command/Query/Validator/Handler + MediatR + Controller + DTO. Case 1+2+3+5 only (cookie-cutter mechanical scaffold, multi-file independent orchestrator-workers, isolated method test-gen handler, mass migration). DO NOT touch FE 2 app (đó là implementer-frontend). DO NOT write test assertions (đó là test-specialist). DO NOT schema design / UX decision / cross-stack bug fix reasoning (em main solo). Auto-refuses out-of-scope.
|
||||
model: claude-opus-4-8
|
||||
model: inherit
|
||||
tools: [Read, Edit, Write, Bash, Skill, Grep, Glob, mcp__rag-unified__search_memory, mcp__rag-unified__search_code, mcp__rag-unified__cross_project_search, mcp__rag-unified__list_projects]
|
||||
skills:
|
||||
- ef-core-migration
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
name: implementer-frontend
|
||||
description: |
|
||||
WRITE specialist cho FE 2 app SOLUTION_ERP (fe-admin + fe-user React 19 + Vite 8 + TS 6 + shadcn/ui + TanStack Query). Cookie-cutter mirror page/types/component cross-app SHA256 IDENTICAL + Pattern 16-bis 4-place mirror (page + App.tsx route + menuKeys.ts + Layout.tsx staticMap) + declarative KIND_CONFIG Record + npm build × 2 verify. Case 1+2 only (cookie-cutter mirror cross-app, multi-file independent). DO NOT touch .NET backend (đó là implementer-backend). DO NOT schema/UX flow decision (em main solo). Auto-refuses out-of-scope.
|
||||
model: claude-opus-4-8
|
||||
model: inherit
|
||||
tools: [Read, Edit, Write, Bash, Skill, Grep, Glob, mcp__rag-unified__search_memory, mcp__rag-unified__search_code, mcp__rag-unified__cross_project_search, mcp__rag-unified__list_projects]
|
||||
skills:
|
||||
- permission-matrix
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
name: investigator-api
|
||||
description: |
|
||||
Read-only EXTERNAL research specialist for SOLUTION_ERP. WebFetch/WebSearch official docs (Anthropic engineering, .NET 10 / EF Core 10 / ASP.NET, React 19 / Vite 8 / TanStack Query, shadcn/ui), NuGet + npm CVE/dependency eval, FE library evaluation (license + bundle size impact — vd FullCalendar v6 MIT verify), reference project pattern audit (NamGroup / DH_Y_DUOC / BVAAU cross-project), community sentiment research. EXTERNAL-focused — KHÔNG audit internal codebase or SQL schema (đó là investigator-codebase). NEVER writes code — only returns concise structured findings with source URLs.
|
||||
model: claude-opus-4-8
|
||||
model: inherit
|
||||
tools: [Read, Bash, WebFetch, WebSearch, mcp__rag-unified__search_memory, mcp__rag-unified__search_code, mcp__rag-unified__cross_project_search, mcp__rag-unified__list_projects]
|
||||
skills:
|
||||
- dependency-audit-erp
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
name: test-specialist
|
||||
description: |
|
||||
WRITE specialist DEDICATED test layer SOLUTION_ERP (tests/SolutionErp.Domain.Tests + Infrastructure.Tests). xUnit + FluentAssertions 7.2 + EF SQLite TestApplicationDbContext (nvarchar(max)→TEXT override) + IdentityFixture. Domain policy state machine test + Infra code generator + CQRS handler test + reflection-based Authorize policy regression + UNIQUE/Conflict/soft-delete invariant. Test-before BẮT BUỘC cho bug fix + critical algo (codegen/guard/financial/security). DO NOT touch production code (Domain/App/Infra/Api/FE — đó là 2 implementer). Auto-refuses out-of-scope.
|
||||
model: claude-opus-4-8
|
||||
model: inherit
|
||||
tools: [Read, Edit, Write, Bash, Grep, Glob, mcp__rag-unified__search_memory, mcp__rag-unified__search_code, mcp__rag-unified__cross_project_search, mcp__rag-unified__list_projects]
|
||||
skills:
|
||||
- contract-workflow
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
name: tooling-auditor
|
||||
description: |
|
||||
Read-only INFORM-only TOOLING-FRESHNESS auditor cho SOLUTION_ERP (H1 — adopt AI_INFRA Harness 1, anh giao 2026-06-07; TÁCH BIỆT khỏi harvest-curator H2 vì 2 việc hay quên+nhầm khi gộp). Audit 4-MẶT mỗi session có cập-nhật ĐẦY-ĐỦ + KỊP-THỜI không: (1) skill (.claude/skills/ 6 project + ~/.claude/skills standalone — mới/đổi/stale/đã-map-vai chưa) · (2) vai-trò sub-agent (.claude/agents/ roster khớp README/CLAUDE.md/STATUS · thừa/idle/scope-drift) · (3) plugin (~/.claude/settings.json enabledPlugins user-global · installed/enabled/assigned/used) · (4) docs (CLAUDE.md·docs/STATUS·agents/README·governance phản-ánh trạng-thái THẬT · drift doc-vs-reality). Lifecycle: @session-start BÁO state + diff-vs-last (THÊM/ĐỔI/XÓA/stale); @session-end CHỐT freshness + AUDIT skill/plugin MỚI phân-bổ (new-alloc) cho em main + sub. Propose-only — em main single-writer (VERIFY→APPEND B3). KHÔNG harvest-memory (đó là harvest-curator). KHÔNG corpus/RAG/deploy (đó là cicd-monitor). KHÔNG store_memory. PHẢI dùng khi audit tooling/docs-freshness + skill/plugin-state + roster-drift đầu/cuối session.
|
||||
model: claude-opus-4-8
|
||||
model: inherit
|
||||
tools: [Read, Grep, Glob, Bash, mcp__rag-unified__search_memory, mcp__rag-unified__search_code, mcp__rag-unified__cross_project_search, mcp__rag-unified__list_projects]
|
||||
memory: project
|
||||
maxTurns: 18
|
||||
@ -37,7 +37,7 @@ Audit 4-mặt freshness tooling/docs SOLUTION_ERP → @session-start báo state+
|
||||
- ❌ KHÔNG fan-out repo khác (SOLUTION_ERP-self only; `cross_project_search` = READ reference, KHÔNG audit repo bạn).
|
||||
|
||||
## 🔗 Quan hệ (ranh giới tránh double-touch)
|
||||
- vs **harvest-curator (H2):** harvest = HARVEST-MEMORY (spawn-record · 5-trục · wave-gom). tooling = TOOLING-FRESHNESS (skill/role/plugin/docs). 🔴 TÁCH BIỆT (anh 06-07). Overlap = 0.
|
||||
- vs **harvest-curator (H2):** harvest = HARVEST-MEMORY (spawn-record · 5-trục · run-harvest `runs/<run-id>/`). tooling = TOOLING-FRESHNESS (skill/role/plugin/docs). 🔴 TÁCH BIỆT (anh 06-07). Overlap = 0.
|
||||
- vs **cicd-monitor:** cicd = post-deploy Gitea/bundle-hash/smoke + dependency CVE. tooling-auditor = MD/skill/plugin/docs/roster freshness. Khác lãnh-địa.
|
||||
- vs **investigator-codebase:** inv-cb = audit code/SQL/schema theo task. tooling-auditor = audit META (tooling/docs/roster) theo lifecycle session.
|
||||
|
||||
|
||||
@ -28,6 +28,17 @@ argument-hint: <id | all-pending>
|
||||
- AI_INFRA `/adap-audit` *(⏳ Đợt 2)* đọc cross-repo (read-only) → verify 2-way. *(KHÔNG cần copy-paste report về.)*
|
||||
6. **Brutal-honest welcome:** chỗ nào không-fit → phản biện qua adap-report (lý lẽ + bằng chứng, §M). Đề xuất nâng cấp → `/adap-request` *(⏳ Đợt 2 — tạm ghi vào adap-report)*.
|
||||
|
||||
## 🔴 Harness-9 mandate — adap 2-workflow (2026-06-17, áp MỌI adap từ nay)
|
||||
|
||||
Mỗi lần adap 1 Harness BẮT BUỘC đi qua **2 workflow tách biệt** (sàn tối thiểu — số agent mỗi workflow tùy task tự cân):
|
||||
1. **Workflow IMPLEMENT** — áp thay đổi vào repo MÌNH (đủ bước Harness).
|
||||
2. **Workflow REVIEW (double-check) RIÊNG** — kiểm lại độc lập + chắt lọc know-how + ghi phát-hiện-riêng. (1 workflow vừa-làm-vừa-tự-chấm dễ bỏ sót → tách ra để bắt lỗi trước khi lan.)
|
||||
3. **REPORT** về AI_INFRA (email cross-project): nấc trạng thái thật + phát-hiện-riêng + **run-id workflow làm bằng chứng**.
|
||||
|
||||
🔴 **PART 3 — task NGẮN-nhưng-cần-confirm:** task nhỏ/nhanh NHƯNG có điểm-quyết-định cần lead/anh confirm → **VẪN bắt buộc workflow review**. "Đủ hệ trọng để confirm = đủ hệ trọng để double-check." KHÔNG áp lối tắt "việc vặt làm 1 mình".
|
||||
|
||||
> Tinh thần stage: investigate → implement → audit. Floor đầy đủ + L2 dark-matter pattern (PART 1): adap-report `2026-06-17-Governance-harness-9-*`.
|
||||
|
||||
## 🔴 Guard
|
||||
- **CHỈ ghi repo MÌNH.** Đọc outbox AI_INFRA = read-only (KHÔNG sửa).
|
||||
- **Function-floor BẮT BUỘC · form tự do · quality chỉ-tăng** (§F4.1). KHÔNG hạ floor (= vi phạm §A1).
|
||||
|
||||
@ -29,7 +29,7 @@ Em main PHẢI echo **TOÀN BỘ nội dung command body này** (đầy đủ Ph
|
||||
- 🟥 **reviewer** — anti-patterns observed + Smart Friend catches + claim verification
|
||||
- 🟢 **cicd-monitor** — Run verdict + bundle hash rotate + Mig prod + corpus drift
|
||||
- 🟫 **tooling-auditor** (monitor H1) — CHỐT tooling/docs-freshness 4-mặt + new-alloc audit (chạy ở §L.b(g))
|
||||
- ⬜ **harvest-curator** (monitor H2) — GATE harvest-integrity 5-trục + wave-folder gom (chạy ở §L.b(d)(f))
|
||||
- ⬜ **harvest-curator** (monitor H2) — GATE harvest-integrity 5-trục + close-gate run-trace `runs/<id>/` (`*-synthesis.md` phẳng h10-refine; run cũ S71 `harvest/`) (chạy ở §L.b(d)(f))
|
||||
|
||||
2. Synthesize cross-agent learnings → integrate vào:
|
||||
- User auto-memory `MEMORY.md` (index — append entry mới, KHÔNG rewrite)
|
||||
@ -45,10 +45,10 @@ Em main PHẢI echo **TOÀN BỘ nội dung command body này** (đầy đủ Ph
|
||||
**§L.b — 7-step auto-maintain (đủ 7, KHÔNG skip — thiếu = ledger thối). (d)(f) = H2 harvest-curator · (g) = H1 tooling-auditor (2026-06-07 Harness 1):**
|
||||
- **(a) summary-index** += 1 dòng/session vào `STATUS.md` Recently Done (pointer, KHÔNG full-log).
|
||||
- **(b) Active-Guards** (error-ledger): promote guard **2-strike** (episodic→procedural) · mark `verified` nếu held qua session · retire theo **net-effect** (hại>lợi → gỡ).
|
||||
- **(c) chore-flag:** agent L1 >~30KB → archive L2 · error-ledger open-entry quá ngưỡng · **0-byte memory check (AS-8)**.
|
||||
- **(c) chore-flag:** agent L1 >~30KB → archive L2 · error-ledger open-entry quá ngưỡng · **0-byte memory check (AS-8)** · **🌙 sleep-check (Harness-10b, S72):** `last_sleep_at` null hoặc ≥7d (`memory-budget.json`) → INFORM gợi-ý `/sleep-recovery-memory-l2` (KHÔNG auto-run) · **🗜️ Harness-11 A/D2 (S75):** chạy `powershell.exe -ExecutionPolicy Bypass -File scripts/memory-archive-gate.ps1` (DRY-RUN) → đề-xuất dồn-archive sub over-cap (A4 hysteresis 0.85 + A5 keep-floor 5 + A6 2-strike) + A7 NO-API L1-eval (pointer-resolve + byte-0-loss). Engine → [`docs/governance/harness-11-engine.md`](../../docs/governance/harness-11-engine.md). DRY-RUN báo kế-hoạch; MOVE thật do em-main (D5 AUTO semantic-null sau khi xem).
|
||||
- **(d) flush agent-memory** mỗi sub đã spawn session này — **spawn-record 4-field** `{agent · task · nấc(agreed/executed/verified) · evidence}`. (0 sub spawn → "n-a".) → **⬜ harvest-curator (H2) HỖ TRỢ:** spawn → propose spawn-record cho mọi sub đã chạy → em main single-writer VERIFY → APPEND (B3 no-overwrite-unverified).
|
||||
- **(e) pending-request audit:** request anh CHƯA-thực-thi đã log SPECIFICS chưa (KHÔNG placeholder).
|
||||
- **(f) 🌾 harvest-integrity GATE (⬜ harvest-curator H2 — 5-trục, Harness 1+2):** verify spawn-record (d) đủ+đúng mọi sub TRƯỚC khi đóng — **Coverage** (0 silent-miss) · **Completeness** (đủ 4-field) · **Placement** (delta đúng `agent-memory/X`) · **Corruption** (moved-not-cut, no-mojibake/shell-baked) · **Fidelity-FLAG** (nghi bịa/on-behalf → escalate 🟥 reviewer, KHÔNG tự phán). + **🌊 wave-folder gom (Harness 2 B5):** nếu session chạy WAVE-MODE (tồn tại `.claude/workflows/wave-*/`) → quét `sub-*.md` → consolidate APPEND `agent-memory/<role>` + verify **B6 isolation** (git-diff: sub KHÔNG ghi ra MD chính; chunk-count: 0 RAG-write). GATE = chưa đủ 5-trục thì CHƯA đóng.
|
||||
- **(f) 🌾 harvest-integrity GATE (⬜ harvest-curator H2 — 5-trục, Harness 1+2):** verify spawn-record (d) đủ+đúng mọi sub TRƯỚC khi đóng — **Coverage** (0 silent-miss) · **Completeness** (đủ 4-field) · **Placement** (delta đúng `agent-memory/X`) · **Corruption** (moved-not-cut, no-mojibake/shell-baked) · **Fidelity-FLAG** (nghi bịa/on-behalf → escalate 🟥 reviewer, KHÔNG tự phán). + **🌊 close-gate C5 Layer3 (Harness-10, thay B5 wave-gom):** với MỌI `runs/<run-id>/` của session → **VERIFY per-turn harvest đã xong** (em-main đã viết `runs/<run-id>/<stage>-synthesis.md` phẳng h10-refine — run cũ S71: `harvest/*.md` — NGAY sau mỗi fan-out turn = C4 Layer1) + `_ledger.md` mọi run đã CLOSE-beat (closed≠⏳). 🔴 **IDEMPOTENT — close-gate chỉ VERIFY, KHÔNG re-APPEND** (per-turn đã APPEND rồi → re-APPEND = DUPLICATE-HARVEST). 5-trục GATE giữ làm **backstop**. GATE = run còn `*-synthesis.md` vắng (run cũ S71: `harvest/` rỗng — C8 dual-accept) HOẶC chưa đủ 5-trục thì CHƯA đóng.
|
||||
- **(g) 🔌 tooling-freshness CHỐT (🟫 tooling-auditor H1 — Harness 1):** spawn → chốt 4-mặt (skill·sub-role·plugin·docs) đổi gì session này + **new-alloc audit** (skill/plugin MỚI chưa phân-bổ → đề-xuất gán em main + sub phù-hợp vai) + flag doc-drift/roster-lệch/count-stale. Propose → em main APPEND/sửa doc (single-writer). 🔴 G-015: 2 monitor = propose-only, em main VERIFY trước APPEND (Bash residual → KHÔNG "read-only enforced").
|
||||
|
||||
## Phase 2 — WRITE (update MD/RAG)
|
||||
|
||||
@ -41,7 +41,7 @@ Em main xác nhận **lead model resolve được** đầu session. Lead SE = **
|
||||
7. **`.claude/agents/README.md`** — 11-agent decision tree + skill matrix + split boundary
|
||||
8. **`.claude/agent-memory/{spawned-agent}/MEMORY.md`** — L1 HOT auto-inject (Tiered Memory v1 ~30KB) + L2 `archive/` Read-on-demand + L3 RAG `search_memory` just-in-time
|
||||
9. **User auto-memory `MEMORY.md`** — auto-loaded bởi harness (index feedback_* entries)
|
||||
10. **Liên quan task hiện tại:** `docs/rules.md`, `docs/architecture.md`, `docs/gotchas.md` (58), `docs/database/schema-diagram.md`, `docs/flows/`
|
||||
10. **Liên quan task hiện tại:** `docs/rules.md`, `docs/architecture.md`, `docs/gotchas.md` (68), `docs/database/schema-diagram.md`, `docs/flows/`
|
||||
|
||||
## Phase 2 — AUDIT (state check)
|
||||
|
||||
@ -68,9 +68,25 @@ Em main xác nhận **lead model resolve được** đầu session. Lead SE = **
|
||||
> Đầu session: 2 monitor sub BÁO LẠI trạng-thái + **diff vs session trước** (floor Harness 1 H1.2 + H2.2). INFORM-only — em main đọc + VERIFY→APPEND nếu có delta hợp-lệ (B3), KHÔNG sub tự sửa.
|
||||
|
||||
- **🟫 tooling-auditor (H1):** spawn → báo tooling-state 4-mặt (skill · sub-role · plugin · docs) + **DIFF vs last-session** (THÊM/ĐỔI/XÓA/stale). Bắt drift doc-vs-thực-tế ngay đầu session (vd roster/count lệch, skill stale, plugin pending).
|
||||
- **⬜ harvest-curator (H2):** spawn → báo **harvest-MD mới** (workflow-wave / sub-agent / agent-team kể từ last) + **delta mồ-côi chưa-APPEND** + wave-folder tồn-đọng. Bắt 0-byte memory (gotcha #53) + delta chưa thu-hoạch.
|
||||
- **⬜ harvest-curator (H2):** spawn → báo **harvest-MD mới** (run-trace `runs/<id>/` — file `sub-*`/`*-synthesis.md` phẳng h10-refine / sub-agent / agent-team kể từ last) + **delta mồ-côi chưa-APPEND** + **scan `runs/*/` tìm OPEN-beat (ledger `_ledger.md` cột closed=⏳) mà `*-synthesis.md` vắng (run cũ S71: `harvest/` rỗng) = orphan run** (C5 Layer2 post-exec rescan — bù khi C4 per-turn miss hoặc session trước chết giữa run). Bắt 0-byte memory (gotcha #53) + delta chưa thu-hoạch.
|
||||
- Cơ-chế = báo-lại-diff đầu session (FORM tự do trình bày). 2 monitor spawn parallel OK. **Light session / hỏi-đáp → có thể skip; bug/feature/multi-agent/wave session → nên chạy.**
|
||||
|
||||
### 2.1.2 Memory L2 budget-audit (Harness-9 — 2026-06-17)
|
||||
|
||||
> Read-side "vật-chất-tối": archive `agent-memory/<sub>/archive/*.md` KHÔNG vào RAG. Inject **mục-lục** (`archive/_INDEX.md`), nội dung verbatim + `.gist.md` đọc-theo-nhu-cầu. "Inject tấm bản-đồ, KHÔNG inject lãnh-thổ."
|
||||
|
||||
- **🌙 Sleep-check (Harness-10b, S72):** trong lúc đọc `memory-budget.json` (cùng file budget-audit), lấy `last_sleep_at` → nếu `null` HOẶC `today − last_sleep_at ≥ 7 ngày` → **INFORM gợi-ý** chạy `/sleep-recovery-memory-l2 <agent|all>` (nén L2 verbatim→gist additive). 🔴 **KHÔNG auto-run** — anh consent mới chạy.
|
||||
|
||||
- Đọc `.claude/agent-memory/memory-budget.json` → so kích-thước THẬT `_INDEX.md` mỗi sub vs cap. Nếu cắt-cho-vừa-ngân-sách đang rớt dấu-mốc quan trọng → **bump budget** (chốt-chặn chống "quên chỉnh ngân sách"). Đo lại bằng `scripts/measure-agent-memory.ps1` (seed-by-measure — KHÔNG đặt cap bằng số tưởng tượng).
|
||||
- L1 over-cap → curate L1→L2 (byte-exact additive) + build/refresh `_INDEX.md` (con-trỏ **substring** sha-keyed, fallback Ctrl-F) + `<period>.gist.md` (nén 4-field, `distill-gen` counter, verbatim FROZEN). Rollout đầu: 4 over-cap sub (S70).
|
||||
|
||||
### 2.1.3 Harness-11 D1 — DÒ+BÁO governance-detectors (2026-06-18 S75)
|
||||
|
||||
> Engine bộ-nhớ-và-governance tự-bảo-trì spec → [`docs/governance/harness-11-engine.md`](../../docs/governance/harness-11-engine.md) (canonical — KHÔNG copy luật ở đây, B1). DÒ tự-động; SỬA qua em-main single-writer (D6/D9).
|
||||
|
||||
- Chạy `powershell.exe -ExecutionPolicy Bypass -File scripts/governance-detectors.ps1` → báo cờ: **C1** con-trỏ-gãy (gotcha#/wikilink) · **C2/B3** derived-doc stale vs `docs/STATUS.md` canonical (mig#/test#/gotcha#/table#) · **C3** vocab-fork (1-khái-niệm-nhiều-tên). NO-API, **DÒ+NÊU-CỜ-only KHÔNG tự sửa** (D6 tầng). Cờ → em-main soạn bản sửa (gated B4).
|
||||
- Nấc: detector = LƯỚI giảm-sót (khoảng-mù giữa 2 nhịp), count-token soft-net có false-pos (sev LOW khi |lệch|<10) → đọc cờ bằng phán-đoán, KHÔNG auto-fix. **Light/hỏi-đáp session → có thể skip; governance/doc-heavy session → nên chạy.**
|
||||
|
||||
### 2.2 Skill registry (6 skill)
|
||||
- Liệt kê: `contract-workflow` · `form-engine` · `permission-matrix` · `dependency-audit-erp` · `ef-core-migration` · `iis-deploy-runbook`
|
||||
- Dùng skill khi task khớp (KHÔNG tự suy luận lại). Phân bổ per agent: xem README skill matrix.
|
||||
|
||||
117
.claude/commands/sleep-recovery-memory-l2.md
Normal file
117
.claude/commands/sleep-recovery-memory-l2.md
Normal file
@ -0,0 +1,117 @@
|
||||
---
|
||||
description: Nén verbatim L2 cũ (agent-memory archive/<period>.md) thành gist ADDITIVE (.gist.md file MỚI, KHÔNG đè). CHỈ P1 gist-compress — KHÔNG build index. SCOPE = repo SOLUTION_ERP-only.
|
||||
argument-hint: <agent | all> (vd — implementer-backend · reviewer · all)
|
||||
---
|
||||
|
||||
# /sleep-recovery-memory-l2 — Giấc ngủ L2 (sleep-compress, P1-only) · SE
|
||||
|
||||
> Trigger nén L2 dark-matter. **CHỈ** xử lý `.claude/agent-memory/<name>/archive/<period>.md` (verbatim cũ) → sinh `<period>.gist.md` (**FILE MỚI, additive**). KHÔNG đụng L1 (`MEMORY.md`) · KHÔNG đụng RAG/L3 · KHÔNG build `_INDEX.md` (đó = P2 archival-event, không nằm trong command này).
|
||||
> 🔴 **Scope:** CHỈ repo **SOLUTION_ERP** (self=`se`). KHÔNG đụng repo khác / corpus federated.
|
||||
> 🔴 **Phân-vai (Cách-B):** lead = em-main single-writer (B3) · `harvest-curator` PROPOSE-ONLY (charter cấm-ghi → substring:"KHÔNG ghi/sửa BẤT KỲ file") · `reviewer` gate Fidelity (lead KHÔNG tự chấm, G-001).
|
||||
>
|
||||
> 📌 **NOTE — provenance:** Command này **port từ AI_INFRA** `/sleep-recovery-memory-l2` (`D:/Dropbox/CONG_VIEC/AI_INFRA/.claude/commands/sleep-recovery-memory-l2.md`) + design (AI_INFRA repo — KHÔNG có bản SE-local) `D:/Dropbox/CONG_VIEC/AI_INFRA/docs/architecture/MEMORY-SLEEP-RECOVERY-L2-DESIGN-v3.md` §4 + §10-P1. Bản gốc gate **§J2 = AI_INFRA-only (no-sister)**; ở đây anh yêu-cầu port để **parity** → scope tailor thành **SE-repo-only** (KHÔNG đụng sister/corpus khác). KHUNG federated giữ nguyên (function-floor), CHỈ tailor form theo SE (roster · path · 4-field tiếng Việt · pointer-style · gotcha#).
|
||||
|
||||
**Tham số:** `$ARGUMENTS` — 1 agent (vd `implementer-backend`) HOẶC `all`. Trống → hỏi anh chọn, **KHÔNG mặc-định `all`**.
|
||||
|
||||
## 📋 BƯỚC 0 — Show command body (visibility, no wait)
|
||||
|
||||
Em main PHẢI echo **TOÀN BỘ nội dung command body này** (đầy đủ Phase 0-5 + tất cả guard rule) trong response đầu tiên ĐỂ ANH USER ĐỌC LẠI.
|
||||
|
||||
**Quy trình (KHÔNG wait confirm):**
|
||||
1. Em echo full content command (raw markdown, KHÔNG tóm tắt, KHÔNG cắt)
|
||||
2. Em proceed execute Phase 0 → 5 sequential ngay
|
||||
3. Anh user điều chỉnh **cuối** nếu cần (KHÔNG mid-flow interrupt)
|
||||
|
||||
## 🎯 Mục đích (function-floor)
|
||||
|
||||
Nén **verbatim L2 cũ** thành **gist súc-tích** mà KHÔNG mất signal-quan-trọng:
|
||||
- **ADDITIVE (B3/§F1/G-009):** gist = ghi ra **file MỚI** `archive/<period>.gist.md`. **KHÔNG đè** `<period>.md` (verbatim) → verbatim ở-lại-đĩa (git history giữ nguyên). Đây là cách giải B3-data-loss: nén KHÔNG phá nguồn.
|
||||
- Kết quả: 1 verbatim file dài + 1 gist file ngắn cùng kỳ. Lookup sâu vẫn Read verbatim; đọc nhanh đọc gist.
|
||||
- 🔴 **KHÔNG ép L1 nhỏ hơn 30KB** (design BỎ — rotation L1→L2 là function per-project; L1-cap-audit của SE đã sống ở `/session-start §2.1.2` + `memory-budget.json`, KHÔNG phải việc của command này).
|
||||
|
||||
## 👥 SE roster — target hợp-lệ
|
||||
|
||||
`all` = **9 sub có-ký-ức** của SE:
|
||||
`investigator-codebase · investigator-api · implementer-backend · implementer-frontend · test-specialist · reviewer · cicd-monitor · frontend-designer · database-agent`.
|
||||
|
||||
> 🟡 **harvest-curator + tooling-auditor = INFORM-only monitor** (KHÔNG vào `all` mặc-định). CHỈ xử-lý nếu chúng **thật-sự có** `archive/<period>.md` verbatim (Phase 0.2 phát-hiện) — gọi tay đích-danh tên.
|
||||
> ℹ️ Thực-tế hiện-tại (đo S71, `memory-budget.json`): chỉ **4/9** sub có archive-content — `cicd-monitor · implementer-backend · investigator-codebase · reviewer`. 5 sub còn-lại archive rỗng/chưa-có → `all` tự skip (Phase 0.2 guard-rỗng). Con-số này = báo-cáo runtime, KHÔNG hardcode: luôn quét đĩa thật.
|
||||
|
||||
## Trigger (2 đường)
|
||||
|
||||
| Đường | Cơ chế |
|
||||
|---|---|
|
||||
| **Tay** | Anh gõ `/sleep-recovery-memory-l2 <agent\|all>` bất kỳ lúc nào |
|
||||
| **Auto-check (INFORM-only)** | `/session-start` + `/session-end` đọc `last_sleep_at` trong `.claude/agent-memory/memory-budget.json` → nếu `today − last_sleep_at ≥ 7` (ngày) → **gợi-ý** chạy command (INFORM anh, **KHÔNG tự-chạy autonomous**). Anh consent → chạy. |
|
||||
|
||||
> 🔴 **State-file `last_sleep_at` có 1 home DUY NHẤT:** `.claude/agent-memory/memory-budget.json` (root-key `last_sleep_at`, cạnh `tiers`/`measured`). KHÔNG ghi ở `_INDEX.md` / nơi khác. *(SE đặt budget-file trong `agent-memory/`, KHÁC AI_INFRA gốc đặt `.claude/memory-budget.json` — đây là tailor path SE.)* Field `last_sleep_at` **đã thêm** (null baseline, S72) vào budget-file SE — auto-check **đã wired** ở `/session-start §2.1.2` + `/session-end §L.b(c)` (đọc field → INFORM gợi-ý nếu null/≥7d). Lần sleep đầu lead set `= today` (Phase 4.4). Lead = single-writer field này (B3).
|
||||
|
||||
## Phase 0 — Prep (em main / lead)
|
||||
|
||||
1. Parse `$ARGUMENTS` → list agent target. `all` → 9 sub có-ký-ức SE (xem roster ở trên). Agent lạ (không có dir `.claude/agent-memory/<name>/`) → cảnh-báo + skip. `harvest-curator`/`tooling-auditor` chỉ chạy nếu gọi tay đích-danh **và** có archive thật.
|
||||
2. Mỗi agent: liệt-kê `archive/<period>.md` (verbatim) **đủ cũ** (per-project aging, size-driven — KHÔNG federated-time). **Guard-rỗng:** agent KHÔNG có `archive/` hoặc 0 file `<period>.md` → skip (0 token). Bỏ qua file nào ĐÃ có `<period>.gist.md` tương-ứng (tránh nén lại).
|
||||
3. 🛡️ **double-distill guard (đọc header TRƯỚC khi làm):** nếu **gist nguồn/đối-tượng** đã chứa counter `distill-gen: N` với `N ≥ 1` → đã là sản-phẩm distill → **REFUSE pass-2 tự-động** (skip, log lý-do). Chỉ nén nguồn **verbatim gen-0** (file `<period>.md` chưa-từng-distill). *(SE thực-tế: có gist đã ở `distill-gen: 2` — investigator-codebase 2026-06 — guard PHẢI bắt; KHÔNG nén `.gist.md`, chỉ nén `.md` verbatim.)*
|
||||
4. Đọc `.claude/agent-memory/memory-budget.json` lấy `last_sleep_at` (xác-nhận đủ 7-ngày nếu đường auto). Field vắng → coi như "chưa từng sleep" (đường tay vẫn chạy bình-thường).
|
||||
|
||||
## Phase 1 — GATHER + DEDUP (harvest-curator PROPOSE-ONLY)
|
||||
|
||||
> `harvest-curator` charter **cấm-ghi-file** → output = **proposal MD** (text trả-về), KHÔNG Write.
|
||||
|
||||
Spawn `harvest-curator` đọc từng verbatim file target → đề-xuất **bản nén nháp**:
|
||||
- Gom entry trùng/liên-quan (dedup spawn-record cùng-chủ-đề across nhiều ngày/session).
|
||||
- Đánh **importance-tag** mỗi cụm: `{cao · vừa · thấp}` (khớp nhãn giá-trị-tái-dùng SE đang dùng trong gist hiện-có).
|
||||
- Trả proposal (KHÔNG ghi đĩa). Lead nhận → Phase 2 distill + verify.
|
||||
|
||||
## Phase 2 — DISTILL (method)
|
||||
|
||||
Lead distill proposal thành gist theo 4 bước:
|
||||
1. **4-field giữ-khung (SE):** mỗi entry gốc = spawn-record **`VIỆC · KẾT-LUẬN(+commit/file:line) · BÀI-HỌC · BẤT-NGỜ`**. Nén = rút-gọn câu-chữ, **KHÔNG bỏ field**. *(Đây là form SE — tương-đương `task · verdict · learned · surprise` bản gốc.)*
|
||||
2. **GỘP:** cụm entry cùng-chủ-đề → 1 entry tổng-hợp (cite ngày/session gốc + **pointer back-resolve** vào verbatim).
|
||||
3. **reflection-synthesis:** rút **3 tới 5 entry mỗi file** (meta-insight cấp cao hơn liệt-kê thô), KHÔNG vượt 5.
|
||||
4. **importance-tag drop:** **drop `thấp` TRƯỚC** khi cần cắt độ-dài. `cao`/`vừa` giữ. ratio nén = **báo-cáo KHÔNG phải target** (KHÔNG cắt để đạt con-số).
|
||||
|
||||
🔴 **Pointer-style SE (khớp gist hiện-có):** mỗi dòng kết bằng back-resolve `→ substring:"<unique>"` grep-UNIQUE vào verbatim file đã-tên — git-SHA / Mig-name / Run#NNN / unique-phrase keyed (ngày bị collide). **NO line-hint** (additive append làm xê dòng).
|
||||
|
||||
🔴 **Header gist file BẮT BUỘC** có counter `distill-gen: 1` (sản-phẩm distill thế-hệ-1 → chặn pass-2 sau này per Phase 0.3) + ghi rõ `source-verbatim` (tên file + #record) + `pointer-style: substring`. *(Nếu hiếm-hoi distill từ gist gen-1 — chỉ khi anh ép tay đè guard — stamp `distill-gen: 2`.)*
|
||||
|
||||
## Phase 3 — GATE (coverage-diff DETERMINISTIC + Fidelity)
|
||||
|
||||
> Gate = chốt-chặn mất-signal. 2 lớp.
|
||||
|
||||
**(a) coverage-diff DETERMINISTIC (lead scan, rẻ, LUÔN chạy):**
|
||||
- Mọi **`{surprise · guard · file:line · root-cause · gotcha#}`** xuất-hiện trong verbatim **PHẢI** xuất-hiện (hoặc đánh `N/A` có-chủ-đích) trong gist. *(SE thêm `gotcha#` so với 4-token gốc — khớp rule `memory-budget.json` l2_gist: "every surprise/guard/file:line/root-cause/gotcha# in verbatim must survive".)*
|
||||
- Cơ-chế: grep các loại token trên ↔ đối-chiếu gist (Grep tool). Thiếu bất-kỳ → **FAIL** → bổ vào gist TRƯỚC khi tiếp.
|
||||
- **ratio nén = báo-cáo** (đo để biết), KHÔNG phải target/floor — KHÔNG ép gist nhỏ-đi để đạt ratio đẹp.
|
||||
|
||||
**(b) Fidelity gate (reviewer escalate):**
|
||||
- `harvest-curator` Fidelity-FLAG → escalate `reviewer` chấm **keep-vs-drop** (entry nào nén/drop có đúng không, gist có bịa / lệch nghĩa không).
|
||||
- Lead KHÔNG tự chấm Fidelity (G-001). reviewer PASS → ghi. FAIL → re-distill đúng sự-thật. *(reviewer no-StructuredOutput / chết ×2 → self-gate-fallback bằng evidence-checklist hợp-lệ, ghi rõ trong report — feedback agent-kill-recovery.)*
|
||||
|
||||
## Phase 4 — WRITE (lead single-writer, B3)
|
||||
|
||||
1. **Tool-ghi:** `Write` (file mới) / `Edit` — 🔴 **G-009: KHÔNG** PowerShell `Add-Content` / bash here-string. UTF-8 no-BOM.
|
||||
2. Ghi `archive/<period>.gist.md` (file MỚI). **KHÔNG** chạm `<period>.md` verbatim (additive).
|
||||
3. **G-009 post-scan (BẮT BUỘC sau ghi):** Grep mojibake (`§` · `â†` · `ðŸ` · `KHÃ` — seq cụ-thể, né bare-`Ã`) + dollar-expansion (`/usr/bin/bash`) trên file vừa ghi → **phải sạch**. Bẩn → de-corrupt qua Write tool.
|
||||
4. Cập-nhật `.claude/agent-memory/memory-budget.json` field `last_sleep_at = <today>` (root-key; **thêm mới nếu chưa có**). B3 single-writer, Write/Edit. 🔴 KHÔNG hand-edit `measured_bytes` (chỉ script `scripts/measure-agent-memory.ps1` được sửa số đo) — chỉ chạm `last_sleep_at`.
|
||||
|
||||
## Phase 5 — REPORT + COMMIT
|
||||
|
||||
1. **Report:** mỗi agent xử-lý → `{verbatim-file · gist-file MỚI · #entry trước→sau · ratio (báo-cáo) · importance-drop thấp=N · coverage-diff PASS/FAIL · Fidelity PASS/self-gate}`.
|
||||
2. **Commit** (anh OK): `[CLAUDE] Memory: sleep-compress L2 <agent> — <period>.gist.md additive`
|
||||
- Scope = `Memory`. 🔴 `git add` **file cụ-thể** (KHÔNG `-A`/`.` — secret-leak risk; feedback rag-mcp).
|
||||
- verbatim KHÔNG đổi → KHÔNG trong diff (chỉ `.gist.md` mới + `memory-budget.json` `last_sleep_at`).
|
||||
- 🔴 docs-only path → CI skip (`paths-ignore`, gotcha #41) — commit này 0s.
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Guards (đọc kỹ)
|
||||
|
||||
- 🔴 **Additive-only:** gist = file MỚI. TUYỆT-ĐỐI KHÔNG đè/sửa/xóa verbatim `<period>.md` (B3 · §F1 · G-009 data-loss).
|
||||
- 🔴 **CHỈ P1 gist-compress:** command này KHÔNG build `_INDEX.md` (= P2 archival-event riêng, sub tự-ghi lúc rời L1→L2) · KHÔNG inject read-side · KHÔNG đụng L1/RAG/L3.
|
||||
- 🔴 **double-distill refuse:** `distill-gen ≥ 1` ở đối-tượng → skip (Phase 0.3). Gist mới luôn stamp `distill-gen: 1`. Chỉ nén verbatim `<period>.md` gen-0 (KHÔNG nén `.gist.md`).
|
||||
- 🔴 **coverage-diff = deterministic gate:** surprise/guard/file:line/root-cause/**gotcha#** thiếu → FAIL, KHÔNG ship. ratio = metric KHÔNG target.
|
||||
- 🔴 **Phân-vai cứng (Cách-B):** lead Write+commit (B3 single-writer) · harvest-curator PROPOSE-ONLY (charter cấm-ghi) · reviewer gate Fidelity (G-001 lead-không-tự-chấm).
|
||||
- 🔴 **KHÔNG ép L1 < 30KB** (design BỎ — không phải scope command này; L1-cap-audit sống ở `/session-start §2.1.2`).
|
||||
- 🔴 **Scope SE-repo-only:** CHỈ agent-memory `.claude/agent-memory/**` của SOLUTION_ERP. KHÔNG đụng repo/corpus khác (port-từ §J2-AI_INFRA, tailor parity — xem NOTE đầu file).
|
||||
- 🔴 **G-009 tool-ghi:** Write/Edit only + post-scan mojibake/dollar-exp.
|
||||
- 🔴 **last_sleep_at single home:** chỉ `.claude/agent-memory/memory-budget.json` root-key — KHÔNG nơi khác.
|
||||
@ -17,7 +17,7 @@ Skill này là tài liệu chuyên biệt để Claude (và developer khác) dù
|
||||
| Skill | Mục đích | Trigger ví dụ | Trạng thái |
|
||||
|---|---|---|---|
|
||||
| `dependency-audit-erp` | Scan CVE NuGet + npm 2 FE, respect pin constraint (MediatR 12.4.1, Swashbuckle 6.9.0) | "npm audit", "dotnet vulnerable", "deps scan", "nâng cấp package" | ✅ New Tier 3 |
|
||||
| `ef-core-migration` | Tạo/revert EF Core 10 migration, 3-file rule, DesignTimeDbContextFactory, **50 migration history** (Init → ReplaceBudgetModuleWithPeWorkItemBudgets Mig 50) | "thêm migration", "EF migration", "schema update", "snapshot lỗi" | ✅ Updated S61 (Mig 50 Budget→PeWorkItemBudgets) |
|
||||
| `ef-core-migration` | Tạo/revert EF Core 10 migration, 3-file rule, DesignTimeDbContextFactory, migration history (mới nhất Mig 55; số → `docs/STATUS.md`) | "thêm migration", "EF migration", "schema update", "snapshot lỗi" | ✅ Updated S74 (Mig 55 PE CcmNote) |
|
||||
| `iis-deploy-runbook` | 3 IIS site + win-acme cert + gitea-runner + LibreOffice + debug 500/502/SignalR prod + **G-084 IPv4/IPv6 hardening** | "prod 500", "IIS fail", "cert hết hạn", "restart app pool", "deploy IIS", "port hijack" | ✅ Updated (G-084) |
|
||||
|
||||
## Format chuẩn 1 skill
|
||||
@ -87,5 +87,5 @@ when-to-use:
|
||||
## Related
|
||||
|
||||
- `docs/CLAUDE.md` — quick rules + full stack context
|
||||
- `docs/gotchas.md` — 64 bẫy đã gặp (latest #64 `dotnet ef database update` áp Design-DB 0-rows ≠ Dev-DB → data-migrate untested-before-prod, S61; #63 EF scaffold RenameColumn sai-semantics)
|
||||
- `docs/gotchas.md` — bẫy đã gặp (số hiện tại → `docs/STATUS.md`; latest #69 FE bundle-hash non-deterministic + deploy rebuild-FE-unconditional, S72; #68 IDE TS diagnostic giữa background-agent/workflow = snapshot dở-dang → chỉ tin build sạch sau-cùng, S69; #67 Tailwind v4 accent palette thiếu-stop vỡ-màu im-lặng; #66 rule `h1-h4{color}` unlayered thắng utility `text-white`, S68)
|
||||
- `docs/changelog/migration-todos.md` — roadmap 5 phase + Tier 3
|
||||
|
||||
@ -150,6 +150,6 @@ Lưu vào `docs/changelog/deps-audit-{YYYY-MM-DD}.md` nếu có action.
|
||||
|
||||
## Related
|
||||
|
||||
- `docs/gotchas.md` — 64 bẫy package compat / CI / IIS / Identity / per-NV refactor / SQLite tie-break đã gặp
|
||||
- `docs/gotchas.md` — bẫy package compat / CI / IIS / Identity / per-NV refactor / SQLite tie-break đã gặp (số hiện tại → `docs/STATUS.md`)
|
||||
- `docs/changelog/migration-todos.md` Phase 5.1 — checklist deps scan CI
|
||||
- `SolutionErp.slnx` + `global.json` — .NET version pin
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
name: ef-core-migration
|
||||
description: Tạo/sửa/revert EF Core 10 migration cho SOLUTION_ERP. Dùng khi thêm entity mới, thay đổi schema, rollback migration, debug DesignTimeDbContextFactory fail. Đã có 50 migration sẵn (Init → ReplaceBudgetModuleWithPeWorkItemBudgets Mig 50, S61). Snapshot + Designer + Migration 3-file rule bắt buộc commit đủ.
|
||||
description: Tạo/sửa/revert EF Core 10 migration cho SOLUTION_ERP. Dùng khi thêm entity mới, thay đổi schema, rollback migration, debug DesignTimeDbContextFactory fail. Đã có nhiều migration (số hiện tại → docs/STATUS.md canonical; mới nhất Mig 56 AddProBudgetSplitToPeWorkItemBudget, S76). Snapshot + Designer + Migration 3-file rule bắt buộc commit đủ.
|
||||
when-to-use:
|
||||
- "thêm migration"
|
||||
- "EF Core migration"
|
||||
@ -16,7 +16,7 @@ when-to-use:
|
||||
|
||||
> **Context:** .NET 10 + EF Core 10 + SQL Server. DbContext: `ApplicationDbContext` ở `Infrastructure/Persistence/`. Startup: `SolutionErp.Api`.
|
||||
|
||||
## Migration history (50 migration hiện có)
|
||||
## Migration history (số hiện tại → `docs/STATUS.md` canonical; mới nhất Mig 56)
|
||||
|
||||
| # | Name | Tables added / changed |
|
||||
|---|---|---|
|
||||
@ -70,8 +70,14 @@ when-to-use:
|
||||
| **48** | **`AddProjectMasterFields`** | **🎯 S55 — Project +4 cột nullable (Year int · Investor 250 · Location 500 · Package 300). AddColumn-only, no new table. Kèm `SeedRealMasterDataAsync` ungated nạp 62 dự án + 71 hạng mục + 3 NCC real từ Excel.** |
|
||||
| **49** | **`AddWorkItemToPurchaseEvaluation`** | **🎯 S57bis — PE.WorkItemId `Guid?` loose-Guid (KHÔNG FK vật lý — convention PE: ProjectId/SelectedSupplierId đều loose) + `IX_PurchaseEvaluations_WorkItemId`. AddColumn+CreateIndex-only, no new table. Guard = validator NotEmpty (create) + handler AnyAsync IsActive→Conflict.** |
|
||||
| **50** | **`ReplaceBudgetModuleWithPeWorkItemBudgets`** | **🎯 S61 (2026-06-13) — bảng mới `PeWorkItemBudgets` (1 record/cặp Dự án × Hạng mục, UNIQUE filtered `[IsDeleted]=0`) + **DROP module Budget cũ** (Budgets/BudgetDetails/BudgetApprovals/BudgetChangelogs…) + PE/Contracts DROP `BudgetId` + **backfill `BudgetManualAmount→BudgetPeriodAmount` TRƯỚC DropColumn** (phiếu UAT giữ số) + DELETE menu/permission `Bg_*` IN-list children-first. ⚠️ database-agent advise: KHÔNG FK vật lý PE/Contracts→Budgets → no DropForeignKey · DropIndex TRƯỚC DropColumn (SQL 5074) · IN-list thay LIKE `Bg_%`. Ngân sách giờ per-gói-thầu nhập theo role PRO/CCM. **gotcha #63** (EF scaffold RenameColumn SAI-semantics → Add+UPDATE+Drop) + **#64** (`dotnet ef database update` áp Design-DB 0-rows ≠ Dev-DB → backfill chạy thật lần đầu trên prod).** |
|
||||
| **51** | **`AddDepartmentParentId`** | **🎯 S65 — Department.ParentId `Guid?` loose-Guid (KHÔNG FK vật lý — org-tree phân cấp; `GET /departments/tree` ráp cây in-memory + rollup count theo `User.DepartmentId` + cycle-guard HashSet chặn tự-cha + vòng A→B→A). AddColumn-only, no new table.** |
|
||||
| **52** | **`AddHoSoLinkToPurchaseEvaluation`** | **🎯 S65 — PE.HoSoLink `nvarchar(1000)?` hyperlink NAS (mục "e. Link hồ sơ", FE `<a target=_blank rel=noopener>` null-safe). AddColumn-only, no new table.** |
|
||||
| **53** | **`AddPeUrgentAndCeoApprovalThreshold`** | **🎯 S69 — PE +`IsUrgentByPro`/`IsUrgentByCcm` (bit, cờ gấp PRO đỏ/CCM xanh) + ApprovalWorkflow +`CeoApprovalThreshold` `decimal(18,2)?` (ngưỡng gói CEO — CCM role CostControl duyệt-final khi winnerQuoteTotal < ngưỡng). 3 AddColumn, no new table. anh Kiệt FDC.** |
|
||||
| **54** | **`AddPeSuggestedAndApprovedPrice`** | **🎯 S73 — PE +5 cột giá đề xuất (ProSuggestedMin/Max + CcmSuggested + ApprovedPriceAmount + ApprovedPriceSource) additive-nullable — CEO chọn giá chốt + CCM duyệt-done opt-in. anh Kiệt FDC go-live 22/06.** |
|
||||
| **55** | **`AddCcmNoteToPeWorkItemBudget`** | **🎯 S74 — PeWorkItemBudget +`CcmNote nvarchar(1000)?` (ô "Ghi chú từ CCM" mirror ProNote). AddColumn-only, no new table.** |
|
||||
| **56** | **`AddProBudgetSplitToPeWorkItemBudget`** | **🎯 S76 — PeWorkItemBudget +`ProInitialAmount`+`ProAdjustmentAmount` decimal(18,2)? (cột PRO "Ban hành lần đầu"+"V0/hiệu chỉnh", mirror CCM Initial/Adjustment). AddColumn additive + `Sql()` data-migrate `ProInitial=ProEstimate WHERE ProEstimate NOT NULL` (4 rows prod, gotcha #64). no new table. Form ngân sách → MA TRẬN 3 cột Dự án/PRO/CCM (bảng lưới `<table>`).** |
|
||||
|
||||
Total: **88 bảng** dbo + `__EFMigrationsHistory` (re-ground S62 cicd `sys.tables` — last Mig 50 ReplaceBudgetModuleWithPeWorkItemBudgets: DROP module Budget + CREATE PeWorkItemBudgets → net 93→88). Xem `docs/database/schema-diagram.md` migration table + §11-15 module ERD (§16+ Mig 27-50 chi tiết pending).
|
||||
Total: **88 bảng** dbo + `__EFMigrationsHistory` (re-ground S62 cicd `sys.tables`; last schema-changing Mig 50 net 93→88 — Mig 51-55 đều AddColumn-only, không đổi số bảng). Xem `docs/database/schema-diagram.md` migration table + §11-15 module ERD (§16+ Mig 27-55 chi tiết pending).
|
||||
|
||||
## N-stage workflow pattern (Mig 18-20 — Session 12-13)
|
||||
|
||||
@ -279,7 +285,7 @@ sqlcmd -S .\SQLEXPRESS -d SolutionErp -U vrapp -P <pw> -i migrate.sql
|
||||
|
||||
## Code pointers
|
||||
|
||||
- `src/Backend/SolutionErp.Infrastructure/Persistence/ApplicationDbContext.cs` — DbSet cho 88 bảng (50 migration)
|
||||
- `src/Backend/SolutionErp.Infrastructure/Persistence/ApplicationDbContext.cs` — DbSet cho 88 bảng (số migration → `docs/STATUS.md`)
|
||||
- `src/Backend/SolutionErp.Infrastructure/Persistence/DesignTimeDbContextFactory.cs` — EF tooling factory
|
||||
- `src/Backend/SolutionErp.Infrastructure/Persistence/DbInitializer.cs` — seed + warn + migrate runtime + backfill (idempotent reconcile pattern)
|
||||
- `src/Backend/SolutionErp.Infrastructure/Persistence/Configurations/` — IEntityTypeConfiguration<T> per entity
|
||||
@ -288,5 +294,5 @@ sqlcmd -S .\SQLEXPRESS -d SolutionErp -U vrapp -P <pw> -i migrate.sql
|
||||
## Related
|
||||
|
||||
- `docs/database/database-guide.md` — conventions + migration workflow chi tiết
|
||||
- `docs/database/schema-diagram.md` — **ERD 88 bảng** + §11 PE + §12 ~~Budget~~ (REMOVED Mig 50) + §13 PEDeptOpinions (Mig 15) + §14 ApprovalWorkflow V2 (Mig 22-25) + §15 PE Level Opinions V2 (Mig 26); §16+ Mig 27-50 detail pending (xem migration table)
|
||||
- `docs/database/schema-diagram.md` — **ERD 88 bảng** + §11 PE + §12 ~~Budget~~ (REMOVED Mig 50) + §13 PEDeptOpinions (Mig 15) + §14 ApprovalWorkflow V2 (Mig 22-25) + §15 PE Level Opinions V2 (Mig 26); §16+ Mig 27-55 detail pending (xem migration table)
|
||||
- `docs/gotchas.md` #7, #17, #38 — migration pitfalls + Identity 4-field rename
|
||||
|
||||
@ -1,47 +1,52 @@
|
||||
# `.claude/workflows/` — Workflow + wave-folder convention (Harness 2)
|
||||
# `.claude/workflows/` — Workflow fan-out + run-trace convention (Harness-10)
|
||||
|
||||
> **Mục đích:** convention cho HMW workflow fan-out + **wave-folder memory-isolation** (adopt AI_INFRA Harness 2, anh 06-07). Canonical rule: AI_INFRA `CANONICAL-RULES.md` §J4 (return-delta default) + §J6 (wave-mode + agent-team) — pull qua `cross_project_search`, KHÔNG copy. Tailor SE 8-role roster + S1 scope.
|
||||
> **Mục đích:** convention cho HMW workflow fan-out + **run-trace folder** (mỗi workflow run → 1 thư mục `runs/<run-id>/` git **TRACKED**, gom plan + per-sub + synthesis + ledger 2-nhịp). Adopt AI_INFRA Harness-10 (anh 06-18) — kế thừa wave-folder memory-isolation Harness-2 nhưng **đổi từ transient-gitignored sang tracked-run-folder** để audit trực-tiếp qua git-diff. 🆕 **Cấu trúc PHẲNG (h10-refine 06-18):** file phẳng cùng cấp trong run-folder (phân biệt RAW vs VERIFIED bằng TÊN), KHÔNG subfolder. Canonical rule: AI_INFRA `CANONICAL-RULES.md` §J4 (return-delta default) + §J6 (run-trace + agent-team) — pull qua `cross_project_search`, KHÔNG copy. Tailor SE 9-role roster + S1 scope.
|
||||
|
||||
## Files (tracked)
|
||||
- `hmw.js` — HMW P2 fan-out script. 2 mode: DEFAULT return-delta-only (§J4) · WAVE-MODE (§J6, `args.wave`).
|
||||
- `hmw.js` — HMW P2 fan-out script. 2 mode: DEFAULT return-delta-only (§J4) · RUN-TRACE mode (§J6, `args.run`).
|
||||
- `README.md` — file này (convention).
|
||||
- `wave-<tên>/` — **gitignored** (`.gitignore:93` `.claude/workflows/wave-*/`), transient per-workflow.
|
||||
- `runs/` — **git TRACKED** (qua negation `.gitignore:83 !.claude/**`), không gitignore. Mỗi workflow run = 1 sub-folder `runs/<run-id>/`. Xem `runs/README.md` cho cấu trúc chi-tiết (FLAT) + ledger 2-nhịp + 3-layer anti-miss + C8 migration + detector-tailored-out.
|
||||
|
||||
## 2 MODE memory (ADD — anh 06-07, KHÔNG thay return-delta)
|
||||
## Run-trace = mỗi workflow run → `runs/<run-id>/` TRACKED (FLAT)
|
||||
Mỗi lần chạy workflow fan-out (RUN-TRACE mode) → **1 thư mục run** git theo dõi, file **phẳng cùng cấp**:
|
||||
|
||||
| | DEFAULT return-delta-only (§J4) | WAVE-MODE (§J6, Harness 2) |
|
||||
```
|
||||
.claude/workflows/runs/<run-id>/ ← TRACKED · FLAT h10-refine (hiện trong git-diff = audit trực-tiếp)
|
||||
├── run.md ← Run-MD chính — EM MAIN ghi @P1 (plan + agents-table + spec + guards + status OPEN→CLOSE)
|
||||
├── sub-<role>-<i>.md ← per-sub RAW (prefix `sub-`) — full detail (write-sub ghi @P2 · read-only sub → em main scribe @P3)
|
||||
└── <stage>-synthesis.md ← gom/VERIFIED (suffix `-synthesis.md`) — EM MAIN ghi NGAY sau mỗi fan-out turn (C4 per-turn primary)
|
||||
```
|
||||
Phân biệt RAW (prefix `sub-`) vs VERIFIED (suffix `-synthesis.md`) bằng **TÊN file**, KHÔNG subfolder. **C8:** 5 run cũ S71 (`h10-invest`…`h910-curate`) giữ `sub-md/`+`harvest/` (đừng rewrite history); close-gate chấp nhận CẢ HAI dạng.
|
||||
- `runs/_ledger.md` — sổ run **2-nhịp**: ghi **OPEN-beat** lúc mở run + **CLOSE-beat** (timestamp + verdict + harvest) lúc đóng. **Orphan** = OPEN mà không CLOSE → phải giải-quyết-cứng (điều tra + đóng tay hoặc đánh-dấu aborted). Chi-tiết `runs/README.md`.
|
||||
|
||||
## 2 MODE memory (anh 06-07, KHÔNG thay return-delta)
|
||||
|
||||
| | DEFAULT return-delta-only (§J4) | RUN-TRACE mode (§J6) |
|
||||
|---|---|---|
|
||||
| Khi dùng | fan-out NHẸ (~2-3 phút, read/analyze — vd recon wave) | workflow DÀI / sinh nhiều detail |
|
||||
| Sub ghi file? | KHÔNG — chỉ return `memoryDelta` | GHI full-detail vào `wave-<tên>/sub-<role>-<i>.md` |
|
||||
| Lead làm | VERIFY + APPEND @P3 (B3) | đọc wave on-demand + H2 gom @session-end (B5) |
|
||||
| Rủi ro mất detail | có (delta lossy) — chấp nhận cho việc nhẹ | KHÔNG (full-detail giữ isolated) |
|
||||
| Khi dùng | fan-out NHẸ (~2-3 phút, read/analyze — vd recon) | workflow DÀI / sinh nhiều detail / cần audit-trail |
|
||||
| Sub ghi file? | KHÔNG — chỉ return `memoryDelta` + `findings` | write-sub GHI full-detail vào `runs/<run-id>/sub-<role>-<i>.md` (phẳng); read-only sub → `findings` + `subMdPath` → em main scribe |
|
||||
| Lead làm | VERIFY + APPEND @P3 (B3) | đọc `sub-<role>-<i>.md` on-demand + ghi `<stage>-synthesis.md` per-turn (C4) + H2 gom @session-end (B5, backstop) |
|
||||
| Rủi ro mất detail | có (delta lossy) — chấp nhận cho việc nhẹ | KHÔNG (full-detail giữ trong run-folder tracked) |
|
||||
|
||||
> Mặc định DEFAULT. WAVE-MODE chỉ bật khi workflow dài/nhiều detail (set `args.wave`). KHÔNG bắt mọi fan-out wave-folder.
|
||||
> Mặc định DEFAULT. RUN-TRACE chỉ bật khi workflow dài/nhiều detail/cần dấu-vết (set `args.run = {name, dir}`). KHÔNG bắt mọi fan-out tạo run-folder.
|
||||
|
||||
## Wave-folder structure (WAVE-MODE)
|
||||
```
|
||||
.claude/workflows/wave-<tên>/ ← gitignored (transient; H2 gom rồi → có thể xóa sau commit)
|
||||
├── wave.md ← Wave-MD chính — EM MAIN ghi @P1 (task-list + vai + spec + status + harvest-state)
|
||||
├── sub-<role>-<i>.md ← sub-MD — SUB tự ghi @P2 (vd sub-investigator-codebase-0.md) — full working detail
|
||||
└── _harvest.md ← H2 (harvest-curator) ghi propose @session-end (gom gì về agent-memory nào)
|
||||
```
|
||||
|
||||
## Quy trình WAVE-MODE (B1–B6)
|
||||
1. **B3 SCAFFOLD TRƯỚC (em main @P1):** tạo folder `wave-<tên>/` + `wave.md` (task-list + vai rõ). ⚠️ `hmw.js` chạy JS-sandbox **no-filesystem** → KHÔNG tự tạo folder; **em main Write @P1** TRƯỚC khi invoke Workflow.
|
||||
2. **B1 spawn-from-real-sub:** mỗi task `role ∈ VALID_ROLES` (8 sub) → workflow-agent = sub THẬT (`agentType` inherit memory-pack slice + skill identity), KHÔNG agent vô-danh.
|
||||
3. **B4 phân-quyền TOOL-AWARE:** `hmw.js` inject vào prompt mỗi sub đường-dẫn `sub-<role>-<i>.md` + lệnh ghi ĐÚNG file đó.
|
||||
## Quy trình RUN-TRACE (B1–B6)
|
||||
1. **B3 SCAFFOLD TRƯỚC (em main @P1):** tạo `runs/<run-id>/` + `run.md` (FLAT — KHÔNG cần `sub-md/`/`harvest/` subfolder hay `.gitkeep`; file `sub-*`/`*-synthesis.md` sinh phẳng cùng cấp khi fan-out chạy), **và ghi OPEN-beat vào `runs/_ledger.md`**. ⚠️ `hmw.js` chạy JS-sandbox **no-filesystem** → KHÔNG tự tạo folder; **em main Write @P1** TRƯỚC khi invoke Workflow. (Đây là fragile-point — quên scaffold = run mất dấu-vết âm-thầm; xem `runs/README.md` §C7.)
|
||||
2. **B1 spawn-from-real-sub:** mỗi task `role ∈ VALID_ROLES` (9 sub) → workflow-agent = sub THẬT (`agentType` inherit memory-pack slice + skill identity), KHÔNG agent vô-danh.
|
||||
3. **B4 phân-quyền TOOL-AWARE:** `hmw.js` inject vào prompt mỗi sub đường-dẫn `runs/<run-id>/sub-<role>-<i>.md` (phẳng) + lệnh ghi ĐÚNG file đó.
|
||||
- **Write sub (CÓ Write/Edit):** implementer-backend · implementer-frontend · test-specialist · frontend-designer → ghi-direct sub-MD via Write/Edit.
|
||||
- **Read-only sub (CHỈ Bash):** investigator-codebase · investigator-api · reviewer · cicd-monitor → 🔴 KHÔNG Bash-write MD (mojibake) → full-detail vào `findings` + `subMdPath` → **em main scribe @P3** (single-writer).
|
||||
4. **B6 ISOLATION (AUDIT cẩn-thận):** sub CHỈ ghi `wave-<tên>/sub-*.md` (+ code-file-disjoint nếu giao). 🔴 KHÔNG ghi `agent-memory/*` chính · KHÔNG MD canonical (CLAUDE/README/STATUS/agents) · KHÔNG sub-MD agent khác. **Em main `git status`/`git diff` + chunk-count sau P2** → tracked-file đổi NGOÀI code-disjoint = **vi-phạm** (wave-folder gitignored nên KHÔNG hiện trong diff = sạch). Verify pattern bằng `git check-ignore -v` (test match thật, đừng tin .gitignore text).
|
||||
5. **B5 HARVEST (⬜ harvest-curator H2 @session-end §L.b(f)):** đọc `wave-<tên>/sub-*.md` → 5-trục integrity → đề-xuất em main consolidate APPEND vào `agent-memory/<role>` sub tương-ứng → sub-chính có đầy-đủ memory. Ghi `_harvest.md` propose.
|
||||
- **Read-only sub (CHỉ Bash):** investigator-codebase · investigator-api · reviewer · cicd-monitor → 🔴 KHÔNG Bash-write MD (mojibake) → full-detail vào `findings` + `subMdPath` → **em main scribe @P3** (single-writer).
|
||||
4. **B6 ISOLATION (AUDIT cẩn-thận):** sub CHỈ ghi trong `runs/<run-id>/` (file `sub-<role>-<i>.md` phẳng của mình) + code-file-disjoint nếu giao. 🔴 KHÔNG ghi `agent-memory/*` chính · KHÔNG MD canonical (CLAUDE/README/STATUS/agents) · KHÔNG sub-MD agent khác. **Em main `git status`/`git diff` + chunk-count sau P2** → **run-folder TRACKED → mọi write trong run-folder HIỆN trong diff = audit trực-tiếp**; tracked-change NGOÀI `runs/<run-id>/` VÀ NGOÀI code-disjoint đã giao = **vi-phạm** (thay model Harness-2 B6 "mọi tracked-change = vi-phạm"). Verify pattern bằng `git check-ignore -v` (test match thật, đừng tin .gitignore text — bẫy exit-code: dùng `&& IGNORED || NOT`).
|
||||
5. **B5 HARVEST (per-turn primary C4 + close-gate backstop):** em main ghi `<stage>-synthesis.md` (phẳng) **NGAY sau mỗi fan-out turn** (đọc `sub-<role>-<i>.md` + findings → 5-trục integrity → consolidate). @session-end ⬜ harvest-curator H2 §L.b(f) **VERIFY per-turn harvest đã xong cho mọi `runs/<id>/`** (idempotent — KHÔNG re-APPEND, chống DUPLICATE-HARVEST) + giữ 5-trục GATE làm backstop, rồi đề-xuất em main APPEND vào `agent-memory/<role>` sub tương-ứng.
|
||||
|
||||
## Agent-team (`.claude/agent-teams/<tên>/` — gitignored `.gitignore:94`)
|
||||
- Cùng nguyên-lý isolation: teammate **KHÔNG có memory-dir built-in** (khác subagent) → folder riêng cho teammate ghi MD-session (A1, tránh overwrite memory chuẩn).
|
||||
- Team spawn TỪ **sub-agent chính có memory dự-án rõ-ràng** (A2 — mang identity/skill sub thật trong 8 roster).
|
||||
- H2 harvest-curator gom `.claude/agent-teams/<tên>/` → agent-memory tương-ứng (giống wave).
|
||||
- Team spawn TỪ **sub-agent chính có memory dự-án rõ-ràng** (A2 — mang identity/skill sub thật trong 9 roster).
|
||||
- H2 harvest-curator gom `.claude/agent-teams/<tên>/` → agent-memory tương-ứng (giống run-trace).
|
||||
- ⚠️ **Caveat: Agent-Team experimental + Windows 11 in-process only** (no split-pane) → SE **CHƯA dùng team thật** → A = **convention-ready** (n-a runtime), cơ-chế isolation chung qua workflow.
|
||||
|
||||
## Guard
|
||||
- **S1:** Workflow CHỈ repo SOLUTION_ERP — KHÔNG fan-out repo/corpus khác (`cross_project_search` = READ reference only).
|
||||
- **S2/S3:** chỉ chạy khi HMW-mode ON (`/ultra-on` → marker `.claude/hmw-mode.on`) + checkpoint INFORM (`hmw.js` throw nếu `checkpointApproved≠true`) + sub KHÔNG spawn sub.
|
||||
- **G-015 accuracy:** isolation = defense-in-depth (gitignore wave-*/ + em main git-diff post-P2 + chunk-count), KHÔNG sandbox cứng. Read-only sub vẫn giữ Bash = ghi-ngoài-repo (git-diff mù) / curl Qdrant (chunk-count bắt). KHÔNG claim "ENFORCED".
|
||||
- **Anti-bypass detector (h10-refine b): SE TAILORED-OUT** — SE chạy workflow qua Anthropic Workflow tool (KHÔNG có CLI-launcher để lách như node-CLI) → bypass-surface ~N/A; containment = git-diff + run-folder TRACKED + ledger orphan-scan (G-015). 3 nguyên-tắc detector (whitelist launcher · path-variant match · anchor launch-key + nghiệm-thu quan-hệ) đã cân-nhắc, N/A cho threat-model SE. Chi-tiết `runs/README.md`.
|
||||
- **G-015 accuracy (no-overclaim):** run-folder TRACKED ≠ read-only-ENFORCED — sub vẫn giữ Bash (write-channel mở: ghi-ngoài-repo git-diff mù / curl Qdrant). Containment THẬT = **em-main single-writer + git-diff (in-repo, run-folder tracked nên hiện) + chunk-count (RAG)**, defense-in-depth, KHÔNG sandbox cứng. KHÔNG claim "ENFORCED", KHÔNG bỏ chunk-count.
|
||||
|
||||
@ -2,11 +2,11 @@
|
||||
// top-level await/return hợp lệ); KHÔNG node-runnable trực tiếp (`node hmw.js` sẽ lỗi await).
|
||||
// Em main lo P0/P1/P3/P4 NGOÀI workflow; script này CHỈ lo P2 fan-out.
|
||||
// Invoke bằng {scriptPath} (no hot-reload — restart/re-invoke sau khi sửa). Scope = repo SOLUTION_ERP ONLY (S1).
|
||||
// ⚠️ Script chạy JS-sandbox KHÔNG filesystem → KHÔNG tự tạo folder/ghi file. Scaffold wave-folder = EM MAIN @P1 (Harness 2 B3).
|
||||
// ⚠️ Script chạy JS-sandbox KHÔNG filesystem → KHÔNG tự tạo folder/ghi file. Scaffold run-folder runs/<run-id>/ (TRACKED) = EM MAIN @P1 (Harness-10, supersedes Harness 2 B3 wave-folder).
|
||||
|
||||
export const meta = {
|
||||
name: 'hmw',
|
||||
description: 'HMW P2 execute (SOLUTION_ERP) — fan-out 9-agent roster có MEMORY-PACK slice (qua args vì script không đọc file) + return findings + checklistEvidence + memoryDelta (spawn-record 4-field). 2 MODE (Harness 2, 06-07): (A) DEFAULT return-delta-only — fan-out nhẹ, sub KHÔNG ghi file, git-diff verify. (B) WAVE-MODE (args.wave) — workflow DÀI, em main scaffold .claude/workflows/wave-<tên>/ @P1, sub ghi full-detail vào CHỈ sub-MD mình (B4/B6), H2 harvest-curator gom wave→agent-memory @session-end (B5). taskList thoải mái (queue theo slot, không cap cứng). memoryDelta KHÔNG tự ghi — em main VERIFY + APPEND-only @P3 (no-overwrite-unverified, B3). Two-tier model H4.5 (Harness-4 2026-06-10): promote-roles inherit Fable 5 · demoted-roles pin Opus 4.8 (frontmatter) · role-less \'opus\' · per-task tier:\'fable\'|\'opus\' override. Scope = repo SOLUTION_ERP ONLY (S1 — KHÔNG fan-out repo/corpus khác).',
|
||||
description: 'HMW P2 execute (SOLUTION_ERP) — fan-out 9-agent roster có MEMORY-PACK slice (qua args vì script không đọc file) + return findings + checklistEvidence + memoryDelta (spawn-record 4-field). 2 MODE (Harness 2, 06-07): (A) DEFAULT return-delta-only — fan-out nhẹ, sub KHÔNG ghi file, git-diff verify. (B) RUN-TRACE mode (args.run, Harness-10) — workflow DÀI, em main scaffold .claude/workflows/runs/<run-id>/ TRACKED FLAT (run.md + sub-<role>-<i>.md + <stage>-synthesis.md phẳng cùng cấp) @P1, sub ghi full-detail vào CHỈ sub-<role>-<i>.md mình (B4/B6), harvest per-turn primary (C4) + H2 gom @session-end = backstop verify-idempotent. taskList thoải mái (queue theo slot, không cap cứng). memoryDelta KHÔNG tự ghi — em main VERIFY + APPEND-only @P3 (no-overwrite-unverified, B3). Model H8 all-inherit (Harness-8 2026-06-16): role-có-frontmatter inherit top-tier lead · role-less inherit · per-task tier:\'fable\'|\'opus\' escape-hatch. Scope = repo SOLUTION_ERP ONLY (S1 — KHÔNG fan-out repo/corpus khác).',
|
||||
phases: [{ title: 'Execute', detail: 'fan-out memory-pack-injected agents, structured return' }],
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ export const meta = {
|
||||
// spec: '<acceptance-criteria / context chung>',
|
||||
// checkpointApproved: true, // em main set SAU khi BÁO {số agent·vai·task} @inform (S2)
|
||||
// taskList: [ { role:<VALID_ROLES|null>, label:'..', prompt:'..', tier:'fable'|'opus'? }, ... ] // tier = per-task model override (H4.5)
|
||||
// wave: { name:'<tên-wave>', dir:'.claude/workflows/wave-<tên>' } // OPTIONAL — bật WAVE-MODE (B). Folder + wave.md em main ĐÃ scaffold @P1 (script no-fs).
|
||||
// run: { name:'<run-id>', dir:'.claude/workflows/runs/<run-id>' } // OPTIONAL — bật RUN-TRACE mode (B, Harness-10 TRACKED). Folder + run.md em main ĐÃ scaffold @P1 (script no-fs). [legacy alias: args.wave]
|
||||
// }
|
||||
|
||||
const VALID_ROLES = [
|
||||
@ -26,21 +26,21 @@ const VALID_ROLES = [
|
||||
'database-agent', // +S57 — S56 dùng 3× qua fail-soft WARN; read-advisory DB lens (DB1-DB11)
|
||||
]
|
||||
|
||||
// ─── H4.5 two-tier model (Harness-4 adopt 2026-06-10) ───────────────────────
|
||||
// Promote-list (gate/verdict-class — frontmatter `model: inherit` = ăn Fable 5 1M từ lead):
|
||||
// investigator-codebase (H4.3b) · reviewer (H4.3a) · database-agent (H4.3b).
|
||||
// [harvest-curator (H4.3c) cũng promote nhưng là monitor — không fan-out qua hmw.]
|
||||
// Demoted roles còn lại: frontmatter ĐÃ pin `claude-opus-4-8` → hmw KHÔNG set model (frontmatter tự lo).
|
||||
// Role-less (H6.2 governed-ultracode adopt S63): default = inherit lead-model (top-tier) — KHÔNG pin rẻ làm default.
|
||||
// Sweep/cost → per-task tier:'opus' escape-hatch (KHÔNG còn default 'opus'). Per-task tier:'fable' = force lead-tier khi hệ-trọng.
|
||||
// ─── H8 all-inherit top-tier (Harness-8 adopt 2026-06-16 — thay thế two-tier H4.5) ──
|
||||
// MỌI sub-agent có memory → frontmatter `model: inherit` = ăn top-tier lead (hiện Opus 4.8 1M do
|
||||
// Fable suspended H5; tự lên Fable 5 khi về). KHÔNG còn demote-pin `claude-opus-4-8` (H4.5 đã gỡ).
|
||||
// hmw KHÔNG set model cho role-có-frontmatter → return undefined, frontmatter (inherit) tự lo.
|
||||
// Role-less (H6.2 governed-ultracode): default = inherit lead-model (top-tier) — KHÔNG pin rẻ làm default.
|
||||
// H8.2 "workflow nhanh nhất" = song song tối đa + xuất nhanh, KHÔNG hạ model làm rẻ.
|
||||
// Escape-hatch per-task tier:'opus' GIỮ cho sweep/cost (ngoại lệ có chủ đích); tier:'fable' = force lead-tier khi hệ-trọng.
|
||||
function resolveModel(role, rawRole, tier, i) {
|
||||
if (tier === 'fable' || tier === 'opus') return tier
|
||||
if (tier) log(`⚠️ hmw: tier "${tier}" lạ (task #${i}) → bỏ qua, dùng tier-map mặc định H4.5`)
|
||||
if (tier) log(`⚠️ hmw: tier "${tier}" lạ (task #${i}) → bỏ qua, dùng mặc định H8 (inherit top-tier)`)
|
||||
// Invalid-role (typo ∉ VALID_ROLES, WARN đã log ở caller) → fail-UP inherit Fable 5 — H4.5 "chưa-phân-loại
|
||||
// → nghiêng quality" (KHÔNG rơi 'opus': task có thể là gate-class gõ nhầm tên role).
|
||||
if (!role && rawRole) return undefined
|
||||
if (!role) { log(`hmw: task #${i} role-less → inherit lead-model (H6.2 governed-ultracode; per-task tier:'opus' = escape-hatch sweep/cost)`); return undefined }
|
||||
return undefined // role có frontmatter: promote=inherit Fable 5 · demoted=pin Opus 4.8 — KHÔNG override
|
||||
return undefined // role có frontmatter: tất cả `inherit` (H8 all-inherit top-tier) — KHÔNG override
|
||||
}
|
||||
|
||||
const SCHEMA = {
|
||||
@ -49,7 +49,7 @@ const SCHEMA = {
|
||||
properties: {
|
||||
findings: { type: 'string', description: 'Kết quả chính. MỌI claim kèm evidence file:line. KHÔNG narrative suông.' },
|
||||
checklistEvidence: { type: 'string', description: 'Bằng chứng cho acceptance-checklist P1 (số đo / PASS-FAIL / verdict).' },
|
||||
subMdPath: { type: 'string', description: 'WAVE-MODE: đường-dẫn sub-MD agent đã ghi (em main/H2 đọc on-demand). DEFAULT-mode: bỏ trống.' },
|
||||
subMdPath: { type: 'string', description: 'RUN-TRACE mode FLAT: đường-dẫn sub-<role>-<i>.md agent đã ghi (em main/H2 đọc on-demand). DEFAULT-mode: bỏ trống.' },
|
||||
memoryDelta: {
|
||||
type: 'object',
|
||||
description: 'Spawn-record 4-field — RETURN-only để EM MAIN harvest @P3. Agent KHÔNG tự ghi ký ức (KHÔNG file MEMORY.md, KHÔNG store_memory/RAG). Em main VERIFY + APPEND-only (KHÔNG overwrite entry cũ nếu chưa kiểm tra — B3).',
|
||||
@ -84,14 +84,15 @@ if (A.taskList.length > 16) {
|
||||
const memoryPack = A.memoryPack || {}
|
||||
const spec = A.spec || ''
|
||||
|
||||
// ─── WAVE-MODE (Harness 2 B) ─────────────────────────────────────────────────
|
||||
// wave = { name, dir }. Folder + wave.md em main ĐÃ scaffold @P1 (script no-fs). Bật → sub ghi full-detail
|
||||
// vào CHỈ sub-MD mình + return memoryDelta. Isolation B6 (gitignore wave-*/ + em main git-diff post-P2 + chunk-count).
|
||||
const wave = (A.wave && A.wave.dir) ? A.wave : null
|
||||
if (wave) log(`hmw: WAVE-MODE on → dir=${wave.dir} (sub ghi sub-MD isolated; em main scaffold @P1; H2 harvest-curator gom @session-end B5).`)
|
||||
// ─── RUN-TRACE mode (Harness-10, supersedes Harness-2 wave-mode B) ────────────
|
||||
// run = { name, dir }. Folder runs/<run-id>/ (TRACKED) + run.md em main ĐÃ scaffold @P1 (script no-fs). Bật → sub ghi full-detail
|
||||
// vào CHỈ sub-<role>-<i>.md phẳng mình + return memoryDelta. Containment: tracked-change NGOÀI run-folder + code-disjoint = vi-phạm
|
||||
// (run-folder TRACKED → HIỆN trong git-diff = audit trực-tiếp; em main git-diff post-P2 + chunk-count RAG). [legacy alias args.wave]
|
||||
const wave = (A.run && A.run.dir) ? A.run : ((A.wave && A.wave.dir) ? A.wave : null)
|
||||
if (wave) log(`hmw: RUN-TRACE mode on → dir=${wave.dir} (TRACKED FLAT; sub ghi sub-<role>-<i>.md phẳng isolated; em main scaffold @P1; harvest per-turn primary C4, H2 gom @session-end = backstop).`)
|
||||
|
||||
phase('Execute')
|
||||
log(`HMW P2: fan-out ${A.taskList.length} task (${wave ? 'WAVE-MODE' : 'return-delta-only'}, two-tier H4.5 promote-inherit/demoted-opus, memory-pack-injected, scope=SOLUTION_ERP repo only)`)
|
||||
log(`HMW P2: fan-out ${A.taskList.length} task (${wave ? 'RUN-TRACE' : 'return-delta-only'}, H8 all-inherit top-tier, memory-pack-injected, scope=SOLUTION_ERP repo only)`)
|
||||
|
||||
const results = await parallel(A.taskList.map((t, i) => () => {
|
||||
const raw = t && t.role
|
||||
@ -99,19 +100,19 @@ const results = await parallel(A.taskList.map((t, i) => () => {
|
||||
if (raw && !role) log(`⚠️ hmw: agentType "${raw}" ∉ VALID_ROLES → default subagent cho task #${i}`)
|
||||
|
||||
const mem = role && memoryPack[role] ? memoryPack[role] : ''
|
||||
const subMd = wave ? `${wave.dir}/sub-${role || 'task'}-${i}.md` : null
|
||||
const subMd = wave ? `${wave.dir}/sub-${role || 'task'}-${i}.md` : null // Harness-10 FLAT (h10-refine 2026-06-18): sub-<role>-<i>.md phẳng cùng cấp dưới runs/<run-id>/ — KHÔNG sub-md/ subdir
|
||||
|
||||
// Write-guard TOOL-AWARE theo MODE (B6 isolation). SE read-only sub (KHÔNG Write tool): investigator-codebase/api,
|
||||
// reviewer, cicd-monitor (+ monitor tooling-auditor/harvest-curator). Write sub: implementer-backend/frontend, test-specialist, frontend-designer.
|
||||
const writeGuard = wave
|
||||
? [
|
||||
`## ✍️ WAVE-MODE ghi sub-MD (Harness 2 B4/B6) — TOOL-AWARE (chống mojibake G-009):`,
|
||||
`## ✍️ RUN-TRACE ghi sub-<role>-<i>.md FLAT (Harness-10 h10-refine, supersedes subfolder sub-md/) — TOOL-AWARE (chống mojibake G-009):`,
|
||||
`- Full-detail công-việc của mày → ĐÚNG 1 file: \`${subMd}\` (folder đã scaffold sẵn — KHÔNG tạo folder).`,
|
||||
` • NẾU mày CÓ Write/Edit tool (implementer-backend/frontend, test-specialist, frontend-designer): GHI TRỰC TIẾP via Write/Edit. 🔴 KHÔNG Bash-write MD ($-expansion/mojibake).`,
|
||||
` • NẾU mày CHỈ có Bash (read-only sub: investigator-codebase/api, reviewer, cicd-monitor — KHÔNG Write tool): 🔴 TUYỆT ĐỐI KHÔNG Bash-write MD → để full-detail trong "findings" + đặt subMdPath="${subMd}"; EM MAIN scribe @P3 (single-writer Write-tool, no-corruption).`,
|
||||
`- 🔴 ISOLATION (B6, AUDIT): CHỈ ghi \`${subMd}\` (+ code-file-disjoint nếu task giao). TUYỆT ĐỐI KHÔNG ghi/sửa: agent-memory/* (MEMORY.md BẤT KỲ sub) · MD canonical (CLAUDE/README/STATUS/agents) · sub-MD agent khác. Em main git-status/diff audit sau P2 — tracked-file đổi ngoài code-disjoint = vi-phạm.`,
|
||||
`- LUÔN return: findings (FULL) + checklistEvidence + memoryDelta (4-field) + subMdPath="${subMd}". H2 harvest-curator gom @session-end (B5) → agent-memory/${role || 'sub'}.`,
|
||||
`- 🔴 KHÔNG store_memory/RAG-write · KHÔNG Bash curl/HTTP Qdrant (:6333 = git-diff MÙ, chỉ chunk-count bắt) · KHÔNG ghi file NGOÀI repo/wave-folder. RAG single-writer=em main; containment = git-diff(in-repo)+chunk-count(RAG) [G-015].`,
|
||||
`- 🔴 ISOLATION (B6→Harness-10, AUDIT): CHỈ ghi \`${subMd}\` (+ code-file-disjoint nếu task giao). TUYỆT ĐỐI KHÔNG ghi/sửa: agent-memory/* (MEMORY.md BẤT KỲ sub) · MD canonical (CLAUDE/README/STATUS/agents) · sub-MD agent khác. Em main git-status/diff audit sau P2 — tracked-file đổi NGOÀI run-folder (runs/<run-id>/) + code-disjoint = vi-phạm (run-folder TRACKED → HIỆN trong diff).`,
|
||||
`- LUÔN return: findings (FULL) + checklistEvidence + memoryDelta (4-field) + subMdPath="${subMd}". Harvest per-turn primary (C4); H2 gom @session-end = backstop verify-idempotent → agent-memory/${role || 'sub'}.`,
|
||||
`- 🔴 KHÔNG store_memory/RAG-write · KHÔNG Bash curl/HTTP Qdrant (:6333 = git-diff MÙ, chỉ chunk-count bắt) · KHÔNG ghi file NGOÀI repo/run-folder. RAG single-writer=em main; containment = git-diff(in-repo)+chunk-count(RAG) [G-015].`,
|
||||
].join('\n')
|
||||
: [
|
||||
`## OUTPUT write-guard (DEFAULT return-delta-only):`,
|
||||
@ -137,7 +138,7 @@ const results = await parallel(A.taskList.map((t, i) => () => {
|
||||
agentType: role || undefined,
|
||||
schema: SCHEMA,
|
||||
label: (t && t.label) || `hmw:${role || 'task'}-${i}`,
|
||||
model: resolveModel(role, raw, t && t.tier, i), // H4.5 two-tier (undefined = theo frontmatter/inherit)
|
||||
model: resolveModel(role, raw, t && t.tier, i), // H8 all-inherit (undefined = theo frontmatter = inherit)
|
||||
})
|
||||
}))
|
||||
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
# IMPLEMENT synthesis — Harness-10 applied (em-main single-writer, C4 per-turn)
|
||||
|
||||
> Workflow `wf_e4e46725-231` · 3 general-purpose file-disjoint + em-main cluster. **Self-gate: 3/3 agents DONE tốt, 0 stray, wording đồng-bộ.**
|
||||
|
||||
## Files changed (containment audit CLEAN — git status khớp tập dự kiến)
|
||||
| Actor | File | Δ |
|
||||
|---|---|---|
|
||||
| 🟦 Agent 1 | `agents/README.md` | :111 decision-tree wave→run-harvest · :162 G-015 containment model mới |
|
||||
| 🟦 Agent 1 | `agents/harvest-curator.md` | scan-path wave→`runs/<id>/sub-md/` · per-turn-primary C4 · **DEDUP note** · B6→tracked model |
|
||||
| 🟦 Agent 1 | `agents/tooling-auditor.md` | :40 wave-gom→run-harvest (H1/H2 split intact) |
|
||||
| 🟦 Agent 2 | `commands/session-end.md` | :51 close-gate C5 L3 **idempotent VERIFY-not-re-APPEND** + 5-trục backstop · :32 repoint |
|
||||
| 🟦 Agent 2 | `commands/session-start.md` | :71 C5 L2 orphan-scan (`runs/*/` OPEN-beat + harvest/ rỗng) |
|
||||
| 🟦 Agent 3 | `workflows/README.md` | FULL rewrite wave→run-trace · :38 STALE "gitignored=sạch" REPLACED · :50 G-015 repoint |
|
||||
| 🟦 Agent 3 | `workflows/runs/README.md` (NEW) | 80 dòng C1-C7 + caveat trung-thực + verify-pattern exit-code trap |
|
||||
| 👤 Em-main | `.gitignore` | block Harness-10 (runs/ tracked via negation, wave-*/ legacy, exit-code trap note) |
|
||||
| 👤 Em-main | `hmw.js` | accept `args.run` (alias wave) · path `sub-md/` · wording containment · 9 comment/string refs wave→RUN-TRACE |
|
||||
|
||||
## Self-gate verdict
|
||||
- **Containment 🟢 CLEAN:** git status = 8 Harness-10 file + runs/ untracked + (residual) investigator MEMORY + (pre-existing) CLAUDE.md. KHÔNG stray ngoài tập.
|
||||
- **Wording đồng-bộ 4 file 🟢:** model containment khớp `_ledger:4` ↔ `hmw.js:89/113` ↔ `README:38/50` ↔ `runs/README:78/80`. (risk #6 xử xong.)
|
||||
- **Stale-wave sweep:** còn lại đều contextualized (transition-note / frozen-historical `agents/README:8` = upgrade-log Harness-2 06-07, đúng giữ). Em fix 3 ref em sót hmw.js (:52/:95/:109).
|
||||
|
||||
## Residuals → closeout (KHÔNG block REVIEW)
|
||||
1. `investigator-codebase/MEMORY.md` (+6, 29.8KB over-cap) = race INVEST (4 agent tự ghi). Em-main reconcile (consolidate→1 entry, curate dưới cap) @closeout.
|
||||
2. `CLAUDE.md` pre-existing = flush test-count 263→306 (đúng, KEEP+commit — resolve H1 stale flag).
|
||||
3. `session-start.md:72` "wave session" = session-TYPE (fan-out session), semantically OK — Agent 2 giữ đúng (không phải wave-folder mechanism).
|
||||
|
||||
## → REVIEW (Stage 3): verify 0-byte-loss frozen evidence · hmw.js parse · floor C1-C8 đúng-nấc · wording 4-file · containment model valid.
|
||||
25
.claude/workflows/runs/2026-06-18-h10-implement/run.md
Normal file
25
.claude/workflows/runs/2026-06-18-h10-implement/run.md
Normal file
@ -0,0 +1,25 @@
|
||||
# RUN — Harness-10 adap · STAGE 2 IMPLEMENT
|
||||
|
||||
- **run-id (folder):** 2026-06-18-h10-implement
|
||||
- **workflow run-id (evidence B3):** wf_e4e46725-231
|
||||
- **adap:** Harness-10 run-trace folder convention
|
||||
- **checkpoint:** APPROVED (anh chốt full-adopt + dogfood qua HMW)
|
||||
- **opened:** 2026-06-18 08:42 +07
|
||||
- **input spec:** `../2026-06-18-h10-invest/harvest/invest-synthesis.md`
|
||||
- **status:** OPEN → running
|
||||
|
||||
## Split (file-disjoint — KHÔNG chồng lấn)
|
||||
| Actor | Files | Nội dung |
|
||||
|---|---|---|
|
||||
| 🟦 Agent 1 (general-purpose) | `agents/README.md` · `harvest-curator.md` · `tooling-auditor.md` | text repoint wave→run-trace (:111/:162 · :22-28/:52 · :40) |
|
||||
| 🟦 Agent 2 (general-purpose) | `commands/session-end.md` · `session-start.md` | C4 close=verify-idempotent · C5 L2 orphan-scan · L3 close-gate |
|
||||
| 🟦 Agent 3 (general-purpose) | `workflows/README.md` (full rewrite) · NEW `runs/README.md` | convention doc + caveat C7 + repoint G-015 :35/:47 |
|
||||
| 👤 Em-main (single-writer) | `.gitignore` · `hmw.js` · `harvest-curator/MEMORY.md` | MECHANISM cluster (live engine + wording-critical :112) + reconcile |
|
||||
|
||||
## Guards áp cho agent (từ synthesis RISKS)
|
||||
- Return-delta-only, **KHÔNG tự ghi MEMORY.md** (race observed @INVEST).
|
||||
- Containment wording PHẢI khớp `runs/_ledger.md:4` (3 chỗ đồng bộ: README/hmw.js/_ledger).
|
||||
- DO-NOT-EDIT frozen evidence (broadcasts/** · adap-reports/2026-06-07-harness-2 · error-ledger:86 · sessions/* · STATUS:217-226 · HANDOFF · archives).
|
||||
- G-015 no-overclaim: TRACKED ≠ read-only-enforced.
|
||||
|
||||
## Output → `harvest/implement-synthesis.md` (em main @P3)
|
||||
@ -0,0 +1,43 @@
|
||||
# INVEST synthesis — Harness-10 build spec (em-main single-writer, C4 per-turn)
|
||||
|
||||
> Workflow `wf_9c2cd2cd-2e7` · 4× investigator-codebase. **Self-gate:** B+C+D xuất sắc, **A hỏng (stub rác `area:test`)** — B đã cover trọn hmw.js wiring → bù đủ, KHÔNG cần redo A.
|
||||
|
||||
## Build plan (2-tier theo recommendation B/C/D)
|
||||
|
||||
### TIER 1 — MECHANISM (careful, đổi behavior)
|
||||
**1. `.gitignore`** (B/C/D đồng thuận):
|
||||
- `runs/` **ĐÃ tracked** qua negation `!.claude/**` (`:83`) → **KHÔNG cần thêm dòng**.
|
||||
- `:93` `.claude/workflows/wave-*/` → **giữ làm legacy** (no wave-*/ tồn tại; xóa cũng được nhưng giữ an toàn hơn) + thêm comment "superseded by runs/ (Harness-10, tracked)".
|
||||
- `:92` verify-comment STALE (`wave-x` path) → cập nhật sang `runs/` + ghi-chú **bẫy exit-code** (`check-ignore` exit 0 cho CẢ negation lẫn ignore → dùng `&& IGNORED || NOT`).
|
||||
- `:94` agent-teams = n-a Windows in-process (giữ).
|
||||
|
||||
**2. `.claude/workflows/hmw.js`** (rename wave→run, 2-MODE logic GIỮ):
|
||||
- `:9` meta.description · `:19` args doc (`wave:{name,dir}`→`run:{name,dir}`) · `:52` SCHEMA subMdPath · `:87-91`/`:90` WAVE-MODE detect (`const wave`→`const run`) · `:91`/`:94` log · `:102` subMd path (`wave.dir`→`runs/<run-id>/sub-md/<role>-<i>.md`) · writeGuard `:106-120` (thêm `harvest/` path + đổi model wording) · prompt `:122-134`/`:131`.
|
||||
- 🔴 **`:112` CRITICAL** — đổi "tracked-file đổi NGOÀI code-disjoint = vi-phạm" → "...NGOÀI **run-folder** + code-disjoint = vi-phạm" (chỉ thiếu chữ "run-folder").
|
||||
- C5 Layer1: thêm reminder "run trước OPEN-beat chưa harvest" vào prompt-builder.
|
||||
- ⚠️ **No hot-reload** (`:4`) → executed-file VERIFIED-pending-restart.
|
||||
|
||||
### TIER 2 — TEXT (rename + repoint, no logic)
|
||||
| File | Đổi |
|
||||
|---|---|
|
||||
| `.claude/workflows/README.md` (48 dòng) | **Full rewrite** wave→runs convention (run.md+sub-md/+harvest/+ledger 2-nhịp). 🔴 `:35` xóa parenthetical STALE "(wave gitignored nên KHÔNG hiện diff = sạch)" → "run-folder TRACKED nên HIỆN diff = audit trực-tiếp". `:47` repoint G-015. |
|
||||
| `session-end.md` | `:51` §L.b(f) wave-gom B5 → **VERIFY per-turn harvest đã xong cho mọi runs/<id>/** + giữ 5-trục GATE làm backstop (C5 L3). 🔴 **idempotent: VERIFY không re-APPEND** (chống DUPLICATE-HARVEST). `:32`/`:49` repoint. |
|
||||
| `session-start.md` | `:71` §2.1.1 H2 mở rộng: scan `runs/*/` tìm **OPEN-beat (ledger closed=⏳) mà harvest/ rỗng = orphan** (C5 L2). |
|
||||
| `agents/README.md` | `:111` decision-tree wave-gom→run-harvest · `:162` repoint G-015 containment caveat (wave-gitignored claim giờ false cho runs/). |
|
||||
| `agents/harvest-curator.md` | `:22-28` scan path `wave-<ten>/sub-*.md`→`runs/<run-id>/sub-md/` · `:52` B6 audit repoint · cân nhắc thêm **DEDUP axis** (`:23` 5-trục) chống double-APPEND. |
|
||||
| `agents/tooling-auditor.md` | `:40` wave-gom→run-harvest (giữ H1/H2 split). |
|
||||
| `agent-memory/harvest-curator/MEMORY.md` | `:13/:20/:26` diary (em-main proxy, low-pri). |
|
||||
|
||||
### NEW machinery (em-main)
|
||||
- `.claude/workflows/runs/README.md` — convention doc (cấu trúc 3-phần + ledger 2-nhịp + 3-layer + **caveat C7 trung thực**: engine no-fs, scaffold = em-main @P1, 3-layer = lưới KHÔNG khoá-cứng, fragile-point C2).
|
||||
- `_ledger.md` — đã có (2-nhịp).
|
||||
- C4 per-turn primary = quy ước em-main: viết `harvest/` NGAY sau mỗi fan-out turn (như file này).
|
||||
|
||||
## RISKS/GUARDS (B+C+D)
|
||||
1. 🔴 **DO-NOT-EDIT frozen evidence:** `broadcasts/**` · `adap-reports/2026-06-07-Agent-harness-2.md` · `error-ledger.md:86` · `docs/changelog/sessions/*` · `STATUS.md:217-226` · `HANDOFF.md:341-365` · `agent-memory/*/archive/*` + `_INDEX.md` · `inbox/README.md:15`.
|
||||
2. **gitignore last-match-wins** (`:82-83` negation) — đừng thêm ignore phá runs/.
|
||||
3. **check-ignore exit-code trap** — verify dùng `&& IGNORED || NOT`.
|
||||
4. **G-015 no-overclaim** — TRACKED ≠ read-only-enforced; Bash residual còn; containment = em-main single-writer + git-diff + chunk-count. KHÔNG bỏ chunk-count.
|
||||
5. **DUPLICATE-HARVEST** — per-turn + close-gate: close-gate VERIFY idempotent.
|
||||
6. **3 chỗ wording "vi-phạm" phải đồng-bộ:** `README.md:35` + `hmw.js:112` + `_ledger.md:4`.
|
||||
7. **Concurrency** — fan-out same-role → sub return-delta-only, KHÔNG tự ghi MEMORY chung (race observed run này); 1 sub-MD/role/turn.
|
||||
22
.claude/workflows/runs/2026-06-18-h10-invest/run.md
Normal file
22
.claude/workflows/runs/2026-06-18-h10-invest/run.md
Normal file
@ -0,0 +1,22 @@
|
||||
# RUN — Harness-10 adap · STAGE 1 INVESTIGATE
|
||||
|
||||
- **run-id (folder):** 2026-06-18-h10-invest
|
||||
- **workflow run-id (evidence B3):** wf_9c2cd2cd-2e7
|
||||
- **adap:** Harness-10 (run-trace folder convention) + checklist Harness-9/10 self-verify
|
||||
- **mandate:** Harness-9 PART 2 — 2-workflow tách biệt; anh chốt full-adopt + dogfood qua HMW đủ 3 stage (invest → implement → review)
|
||||
- **checkpoint:** APPROVED (HMW-mode ON + anh chốt "full-adap + dogfood ngay qua HMW đủ các bước")
|
||||
- **opened:** 2026-06-18 08:29 +07
|
||||
- **status:** OPEN → running
|
||||
|
||||
## Mục tiêu stage
|
||||
Recon đĩa THẬT để dựng build-plan Harness-10 chính xác, tránh sai sót. KHÔNG ghi production/governance (read-only) — chỉ trả findings có cite `file:line`.
|
||||
|
||||
## Agents (4× investigator-codebase, read-only, parallel)
|
||||
| # | role | task |
|
||||
|---|---|---|
|
||||
| A | investigator-codebase | hmw.js wave-mode wiring chính xác + đổi gì cho run-trace + scaffold-cả-3-đầu-run |
|
||||
| B | investigator-codebase | sweep MỌI ref `wave-*/` · `agent-teams` · harvest across `.gitignore`/docs/commands/governance (migration completeness — không sót) |
|
||||
| C | investigator-codebase | luồng harvest hiện tại (H2 @session-end B5) + cách thêm per-turn primary (C4) + 3-layer (C5) + session-start/end wiring |
|
||||
| D | investigator-codebase | model containment G-015 hiện tại (git-diff + chunk-count) + đổi gì khi run-folder TRACKED + cách giữ containment valid |
|
||||
|
||||
## Output → `harvest/invest-synthesis.md` (em main viết sau khi workflow trả, C4 per-turn)
|
||||
@ -0,0 +1,24 @@
|
||||
# REVIEW synthesis — Harness-10 adap (em-main single-writer, C4 per-turn)
|
||||
|
||||
> Workflow `wf_636bc95b-939` · 3× reviewer adversarial độc-lập. **Verdict tổng: PASS sau-fix** (1 CONCERN thật bắt được + đã đóng).
|
||||
|
||||
## Verdict per-lens
|
||||
| Lens | Verdict | Tóm tắt |
|
||||
|---|---|---|
|
||||
| R1 frozen+containment | ✅ PASS | git status = đúng tập · frozen-evidence 0-byte-loss (per-path git status = empty) · containment sync 4 file · gitignore runs/ NOT-IGNORED + wave-*/ IGNORED · **`node --check hmw.js` PARSE-OK** · 1 CONCERN: runs/ chưa commit (C3 flag) |
|
||||
| R2 hmwjs-integrity | ⚠️→✅ CONCERN-fixed | engine 4/4 PASS (template balance ✓ · logic unchanged ngoài :91 alias · WAVE-MODE grep=0) · CONCERN: L1 over-claim (doc≠engine) |
|
||||
| R3 floor C1-C8 | ⚠️→✅ CONCERN-fixed | 7/8 PASS (C7 caveat "điểm sáng nhất"; C3 honest no-claim-committed) · C5 CONCERN: L1 over-claim |
|
||||
|
||||
## CONCERN bắt được (R2 + R3 độc-lập cùng kết luận = high-confidence)
|
||||
**C5 Layer-1 over-claim:** `runs/README.md:51` + C7:72 nói L1 in-run reminder fire trong "hmw.js prompt-builder" với text cụ thể → grep hmw.js = 0. Engine no-fs KHÔNG đọc được ledger → L1 "check prior-run-harvested" KHÔNG THỂ là hmw.js prompt.
|
||||
→ **FIXED (path a, em-main):** sửa L1 = em-main @P1 convention (đọc `_ledger` trước scaffold; nếu run trước `closed=⏳` → harvest+CLOSE trước) + C7:72 timing "run-open @P1" + ghi rõ "cả 3 layer = convention em-main/H2, KHÔNG engine-enforce". Verify: grep hmw.js L1-text=0 / C4-text=1 → **doc khớp engine THẬT**. C5 CONCERN đóng.
|
||||
|
||||
→ **Đây là dogfood thành công của mandate B2 (review-workflow RIÊNG) + Harness-10 C5 chính nó:** 1 workflow vừa-làm-vừa-chấm đã bỏ sót L1 over-claim (IMPLEMENT synthesis không nhắc); review-workflow độc-lập bắt được TRƯỚC commit.
|
||||
|
||||
## Flag pre-commit (KHÔNG defect)
|
||||
- **C3 nấc đầy-đủ cần commit:** `git ls-files runs/` rỗng = tracked-ELIGIBLE chưa committed. Em-main `git add .claude/workflows/runs/` + commit → C3 thành tracked+committed. (Review chạy TRƯỚC commit = đúng trình tự.)
|
||||
- **investigator-codebase/MEMORY.md** race INVEST (+6, ~29.8KB) → em-main reconcile @closeout.
|
||||
|
||||
## Nấc THẬT cuối (honest, no-overclaim)
|
||||
- C1/C2/C4/C6/C8 = executed-file + convention ✓ · C3 = tracked-eligible → **committed sau commit này** · C5 = L2/L3 wired + L1 honest-doc (em-main convention) · C7 = caveat đủ 4 trục.
|
||||
- Review = STATIC disk-truth (git/grep/node --check), KHÔNG curl/runtime (governance adap, no endpoint). hmw.js = source-clean, runtime-pending-restart (no hot-reload).
|
||||
17
.claude/workflows/runs/2026-06-18-h10-review/run.md
Normal file
17
.claude/workflows/runs/2026-06-18-h10-review/run.md
Normal file
@ -0,0 +1,17 @@
|
||||
# RUN — Harness-10 adap · STAGE 3 REVIEW (double-check độc lập, mandate B2)
|
||||
|
||||
- **run-id (folder):** 2026-06-18-h10-review
|
||||
- **workflow run-id (evidence B3):** wf_636bc95b-939
|
||||
- **checkpoint:** APPROVED (mandate Harness-9 PART 2 — review-workflow RIÊNG)
|
||||
- **opened:** 2026-06-18 08:52 +07
|
||||
- **input:** INVEST `../2026-06-18-h10-invest/harvest/invest-synthesis.md` + IMPLEMENT `../2026-06-18-h10-implement/harvest/implement-synthesis.md`
|
||||
- **status:** OPEN → running
|
||||
|
||||
## Agents (3× reviewer, adversarial, read-only ∥)
|
||||
| # | lens | verify |
|
||||
|---|---|---|
|
||||
| R1 | frozen-evidence + containment | 0-byte-loss DO-NOT-EDIT (broadcasts/** · adap-reports/2026-06-07-harness-2 · error-ledger:86 · sessions/* · STATUS:217-226 · HANDOFF · archives) NOT touched · containment wording đồng-bộ 4 file · gitignore runs/ tracked + wave-*/ ignored (exit-code trap) |
|
||||
| R2 | hmw.js engine integrity | hmw.js cấu-trúc valid (var `wave` consistent · `A.run`/`A.wave` logic · sub-md/ path · template-literal không vỡ) · 9 ref wave→run updated/contextualized · KHÔNG đổi logic ngoài convention |
|
||||
| R3 | floor C1-C8 đúng-nấc | adversarial mỗi item C1-C8: nấc THẬT? đặc biệt **C3 2-level** (check-ignore NOT-IGNORED ✓ vs `git ls-files` EMPTY = chưa commit → tracked-ELIGIBLE not committed) · C7 caveat đủ honest · flag over-claim |
|
||||
|
||||
## Output → `harvest/review-synthesis.md` (em main @P3) — verdict PASS/CONCERN/FAIL + nấc THẬT
|
||||
@ -0,0 +1,81 @@
|
||||
# AUDIT SYNTHESIS — Harness-11 adap (2026-06-18-h11-audit · `wf_7fdc3bd5-930`)
|
||||
|
||||
> 4× investigator-codebase (read-only ∥, no Write tool → findings-in-return, **em-main scribe @P3 per writeGuard hmw.js:112**). Ground-truth đọc-disk. Nấc trung-thực: executed-file (tĩnh) / runtime (chạy-quan-sát) / mechanized (cổng-máy) vs convention (người tuân-thủ).
|
||||
|
||||
## Ground-truth canonical (STATUS.md = nguồn-chuẩn state)
|
||||
mig **55** (last `AddCcmNoteToPeWorkItemBudget`) · gotcha **69** · test **339** · tables **88**.
|
||||
|
||||
---
|
||||
|
||||
## PHẦN A — hot-mem auto-archive by budget (🟡 TAILORABLE)
|
||||
|
||||
| Item | Verdict | Evidence |
|
||||
|---|---|---|
|
||||
| A1 session-end byte-gate đo→kích | **PARTIAL** | `measure-agent-memory.ps1:14,32` đo-byte THẬT nhưng KHÔNG call-site auto-run; `session-end.md:48` chỉ prose "L1>~30KB→archive". mechanized-MEASURE, KHÔNG mechanized-TRIGGER |
|
||||
| A2 additive MOVE→archive | **PRESENT (runtime)** | `h910-curate` reviewer 36738→24844 (moved 10) "+22 -0 grep-Fxf byte-exact + md5sum"; budget.json:30 "NEVER rewrite, APPEND-only" |
|
||||
| A3 _INDEX pointer-only append | **PRESENT** | 3 `_INDEX.md` on-disk; budget.json:19 pointer substring sha-keyed, NO line-hint |
|
||||
| A4 hysteresis ~0.85 | **GAP** | grep `0.85\|hysteresis`=0; chỉ 2 cap rời (25600/30720), không band |
|
||||
| A5 keep-floor ≥5 | **GAP** | grep `keep-floor`=0; curate "N oldest" theo phán-đoán người |
|
||||
| A6 2-strike anti-thrash (archive) | **GAP** | 2-strike duy nhất = Active-Guards (`session-end.md:47`), KHÔNG cho archive |
|
||||
| A7 NO-API L1-eval (pointer-resolve+byte-0-loss) | **PARTIAL** | chạy 1-lần trong `h910-curate` (grep-Fxf 10/10+md5sum) NHƯNG one-off em-main-driven, KHÔNG standing-gate |
|
||||
|
||||
**Verdict A:** convention-người-đo (mechanized-MEASURE + mechanized-VERIFY nhưng KHÔNG mechanized-TRIGGER). A4/A5/A6 GAP **hợp-lệ vì A=🟡**. → IMPLEMENT chọn mechanize để A mạnh hơn (optional nhưng giá-trị).
|
||||
|
||||
## PHẦN B — derived→canonical pointer + freshness (🔴 FUNCTION-FLOOR)
|
||||
|
||||
| Item | Verdict | Evidence |
|
||||
|---|---|---|
|
||||
| B1 derived TRỎ canonical | **GAP** | derived COPY hard-code count, 0 pointer. Sites: root `CLAUDE.md:53`(53mig→55)/:66(306test→339)/:131(88table)/:133(68→69) · `docs/CLAUDE.md:70`(93bảng pre-Mig50!) · `ef-core-migration/SKILL.md:3,19,77,285,294`(53mig) · `skills/README.md:20,90` · `dependency-audit-erp/SKILL.md:153`. CLEAN exemplar: `PROJECT-MAP.md` (0 count-token, 241 dòng) |
|
||||
| B2 readable (no pointer-soup) | **PRESENT** | root CLAUDE.md:1-9 readable; stable facts inline đúng |
|
||||
| B3 freshness-DETECT grep gate | **GAP** | NO detector (`.claude/hooks`+`.claude/scripts` absent; hmw.js no-fs ≠ comparator; grep 0 hit) |
|
||||
| B4 fix-after-FLAG GATED qua người | **PRESENT (mechanized)** | em-main single-writer `workflows/README:38,39` + `agents/README:199` + git-diff commit-gate backstop |
|
||||
|
||||
**Verdict B:** B2+B4 ĐẠT · **B1+B3 = function-floor GAP**. B4 fix-path đã sẵn → B3 detector output trực-tiếp actionable.
|
||||
|
||||
## PHẦN C — 3 deterministic-grep detectors (🔴 FUNCTION-FLOOR MANDATE)
|
||||
|
||||
| Item | Verdict | Evidence |
|
||||
|---|---|---|
|
||||
| C1 broken-pointer detector | **GAP** | 0 detector-script (find .claude *.ps1/sh=0; CI deploy.yml 0 grep-gate). Chỉ tooling-auditor agent-judgement |
|
||||
| C2 staleness detector (=B3) | **GAP** | trùng B3; monthly-drift-audit = agent đọc tay, KHÔNG grep tất-định |
|
||||
| C3 vocab-fork detector | **GAP** | 0; vocab-fork SỐNG THẬT chưa ai dò: `wave↔run-trace`(_ledger:15), `Dự trù↔Ngân sách PRO↔PeWorkItemBudget`, PRO=Procurement |
|
||||
| C4 self-line exclusion | **N/A** | chưa detector → chưa self-exclusion |
|
||||
| C5 resolve-condition+2-strike | **PARTIAL** | 2-strike chỉ ở memory-archive convention, KHÔNG ở detector-flag |
|
||||
|
||||
**Verdict C:** **detector-script-thật = CHƯA CÓ.** Chỉ 2 monitor-agent (tooling-auditor/harvest-curator) LLM-judgement propose-only = convention KHÔNG mechanized. → **GAP lớn nhất, IMPLEMENT trọng-tâm.** (Lưu ý: `runs/README:122` "anti-bypass detector TAILORED-OUT" = threat-model KHÁC, KHÔNG phải C1-C3.)
|
||||
|
||||
## PHẦN D — orchestration engine (🔴 FUNCTION-FLOOR)
|
||||
|
||||
| Item | Verdict | Evidence |
|
||||
|---|---|---|
|
||||
| D1 session-start DÒ+BÁO | **PRESENT** | §2.1.1 monitor RE-REPORT + §2.1.2 budget-audit, INFORM-only |
|
||||
| D2 session-end archive+gác-cờ | **PRESENT** | `session-end.md:39-52` §L + harvest GATE 5-trục |
|
||||
| D3 per-turn distill-APPEND | **PRESENT (convention)** | C4 primary harvest-LIỀN sau P2 |
|
||||
| D4 threshold→workflow-gate | **PRESENT-MẠNH (mechanized)** | `hmw.js:76-78` checkpoint THROW (anti-accidental 515K) |
|
||||
| D5 tầng AUTO (semantic-null) | **PARTIAL** | hành-vi có (archive/_INDEX/gist) NHƯNG chưa nhãn 3-tier |
|
||||
| D6 tầng DÒ+NÊU-CỜ | **PARTIAL** | monitor INFORM-only flag, chưa gom thành tier có-tên |
|
||||
| D7 tầng OWNER-APPROVE | **PARTIAL** | consent+single-writer ngầm, chưa nhãn 3-tier |
|
||||
| D8 one-direction lock (canonical→derived) | **GAP** | grep `one-direction\|1-chiều`=0; khái-niệm H11 mới |
|
||||
| D9 append-only single-writer (BAR) | **PRESENT-MẠNH (mechanized)** | store_memory strip runtime S48 0/8 subs; B3 |
|
||||
| D10 file-tool-write-only | **PRESENT (convention)** | `hmw.js:111` + gotcha #61; CHƯA mechanized-block (Bash residual) |
|
||||
| D11 archive MOVE-không-XOÁ | **PRESENT-MẠNH (mechanized)** | byte-0-loss md5sum/grep-Fxf artifact `_ledger:14` |
|
||||
|
||||
**Verdict D:** 7/11 PRESENT (D4/D9/D11 mechanized-mạnh) · **D5/D6/D7 PARTIAL** (3-tier chưa explicit) · **D8 GAP**. H11 "chuẩn-hoá-lại" = nhãn-hoá cái-đã-có, KHÔNG xây-mới.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 GAP-LIST → IMPLEMENT (completeness-gate B+C+D phải đủ-trọn)
|
||||
|
||||
**🔴 FUNCTION-FLOOR (bắt-buộc cho ĐẠT):**
|
||||
1. **PHẦN C — 3 grep detector script** (`scripts/governance-detectors.ps1`): C1 broken-pointer · C2 staleness (=B3) · C3 vocab-fork · C4 self-line exclusion (0 self-match) · C5 resolve-condition + 2-strike. NO-API (grep+measure only). RUNTIME-prove (chạy + FLAG drift thật + fake-drift test).
|
||||
2. **PHẦN B — B1 pointer + B3 detector.** B3 = C2 (cùng script). B1 = derived count-copy → pointer "→ docs/STATUS.md (canonical)" + FIX drift hiện-tại (gated em-main). Sites: root CLAUDE.md + ef-core SKILL + skills/README + dep-audit SKILL + docs/CLAUDE.md.
|
||||
3. **PHẦN D — D5/D6/D7 3-tier explicit + D8 one-direction lock.** Codify khối nhãn-hoá (AUTO semantic-null / DÒ+FLAG / OWNER-APPROVE) + luật canonical→derived 1-chiều. → engine-doc + agents/README.
|
||||
|
||||
**🟡 TAILORABLE (optional-mechanize, làm để A mạnh):**
|
||||
4. **PHẦN A — A4/A5/A6 + standing-gate.** Add hysteresis(0.85)/keep-floor(≥5)/2-strike params → budget.json + session-end archive-gate script (mechanize A1/A7 thành standing).
|
||||
|
||||
**Engine consolidation doc:** `docs/governance/harness-11-engine.md` — codify D1-D11 + 3-tier + locks + trỏ detector-script + canonical, để engine có 1 nguồn-chuẩn.
|
||||
|
||||
**Single-writer split (D9):** sub viết SCRIPT (.ps1 non-canonical, testable runtime) · **em-main viết governance MD** (engine-doc + B1 pointer + cadence-wire + agents/README — vì đụng canonical/luật, B4 gated).
|
||||
|
||||
**Nấc dogfood trung-thực:** A2/A3/D4/D9/D11 = SE đã runtime-mechanized SẴN (H11 = chuẩn-hoá). C1-C3 + B3 = MỚI build (chưa từng có). D5-D8 + B1 = nhãn-hoá/codify cái ngầm-có.
|
||||
25
.claude/workflows/runs/2026-06-18-h11-audit/run.md
Normal file
25
.claude/workflows/runs/2026-06-18-h11-audit/run.md
Normal file
@ -0,0 +1,25 @@
|
||||
# RUN — 2026-06-18-h11-audit (Harness-11 adap · STAGE 1 AUDIT)
|
||||
|
||||
> **Harness-10 FLAT run-trace** (TRACKED). Sub ghi `sub-<role>-<i>.md` phẳng cùng cấp. Synthesis → `audit-synthesis.md`.
|
||||
|
||||
- **Workflow:** Harness-11 adap — AUDIT (ground-truth GAP vs SE-present)
|
||||
- **Mode:** hmw RUN-TRACE, 4× investigator-codebase (read-only ∥)
|
||||
- **Opened:** 2026-06-18 (S75)
|
||||
- **Mandate:** anh giao `/check-email AI_INFRA + /adap-apply` (mỗi stage workflow review kiểm + report trung thực). H11 ⑤ = IMPLEMENT + REVIEW tách biệt.
|
||||
|
||||
## Mục tiêu
|
||||
Harness-11 = engine bộ-nhớ-và-governance TỰ-BẢO-TRÌ. Tự-DÒ toàn diện (luôn tươi) + AUTO chỉ semantic-null + single-writer bar-không-hạ + đổi-luật owner-approve. **Completeness-gate: phải đủ TRỌN PHẦN B+C+D (🔴 function-floor) mới ĐẠT; A 🟡 tailor.** H11 = chuẩn-hoá lại cái SE đã có một phần (H9 L2 + H10 run-trace) → AUDIT để biết PRESENT/PARTIAL/GAP từng item, tránh xây lại.
|
||||
|
||||
## Task list (4 lane, mỗi lane 1 PHẦN)
|
||||
| Lane | Role | PHẦN | Scope |
|
||||
|---|---|---|---|
|
||||
| audit-A | investigator-codebase | A1–A7 (hot-mem auto-archive 🟡) | session-end.md · measure-agent-memory.ps1 · memory-budget.json · agent-memory/*/archive/_INDEX/gist |
|
||||
| audit-B | investigator-codebase | B1–B4 (derived→canonical + freshness 🔴) | root CLAUDE.md · docs/CLAUDE.md · agents/README · skills · có freshness-detector chưa |
|
||||
| audit-C | investigator-codebase | C1–C5 (3 grep detectors 🔴) | scripts/ · .claude/ — có broken-pointer/staleness/vocab-fork detector chưa · self-exclusion · resolve-condition |
|
||||
| audit-D | investigator-codebase | D1–D11 (orchestration 🔴) | session-start/end cadence · 3-tier safe-split · 4 locks (1-direction/append-single-writer/file-tool/move-not-delete) |
|
||||
|
||||
## Acceptance
|
||||
Mỗi item (A1-7, B1-4, C1-5, D1-11) phân loại **PRESENT / PARTIAL / GAP** + evidence file:line + artifact nếu có. Output → `audit-synthesis.md` (em main gom).
|
||||
|
||||
## Run-id
|
||||
`wf_7fdc3bd5-930`
|
||||
@ -0,0 +1,39 @@
|
||||
# CHECKLIST-VERIFY SYNTHESIS — Harness-11 (2026-06-18-h11-checklist-verify · `wf_39cd4cbe-f07`)
|
||||
|
||||
> 3× investigator-codebase (read-only ∥, evidence-mapping). Em-main scribe @P3. **VERDICT: ✅ completeness-gate H11 ĐẠT — B+C+D đủ-trọn, A 🟡 tailored.** Rà từng item bằng bằng-chứng thật (run-output/file:line), KHÔNG trí-nhớ.
|
||||
|
||||
## CL1 — PHẦN A (🟡) + PHẦN B (🔴) → PASS
|
||||
**A1-A7 PRESENT (🟡 tailored), runtime qua `memory-archive-gate.ps1`:**
|
||||
| Item | Nấc | Loại | Evidence |
|
||||
|---|---|---|---|
|
||||
| A1 byte-gate | exec+runtime | mechanized | cap 25600 echoed; flag 3 over-cap (cicd 26798·inv 31502·reviewer 38755) |
|
||||
| A2 additive MOVE | exec (design) | convention | DRY-RUN plan-only, MOVE thật = em-main D5 (no auto-move memory canonical) |
|
||||
| A3 _INDEX pointer | exec+runtime | mechanized | A7 đọc 4 _INDEX, 186 pointer |
|
||||
| A4 hysteresis 0.85 | exec+runtime | mechanized | low-water 21760 echoed |
|
||||
| A5 keep-floor 5 | exec+runtime | mechanized | WARN fired inv+reviewer (oldest-movable exhausted trước lowMark) |
|
||||
| A6 2-strike | exec / runtime-PARTIAL | mechanized | **legit-gap by-design:** cần 2× `-Apply` (DRY-RUN strike=1 WATCH, `.archive-strikes.json` absent); script self-doc [TAILOR] |
|
||||
| A7 NO-API L1-eval | exec+runtime | mechanized | **GATE PASS 186/186 resolve, 0 fail, exit 0** |
|
||||
|
||||
**B1-B4 PRESENT (🔴 floor MET), completeness B PASS:**
|
||||
- B1 ✅ 5/5 derived docs ≥1 STATUS pointer (CLAUDE.md:53/66/87 · ef-core SKILL:3/19 · skills/README:20). Caveat: residual soft-net FP (module-local "6 test"/"4 bảng Budget") — B2 tradeoff, KHÔNG B1-fail.
|
||||
- B2 ✅ readable giữ (ef-core SKILL:85-120 table inline, không pointer-soup).
|
||||
- B3 ✅ exec+runtime (C2 ran, canonical mig55/test339/gotcha69/table88 == disk, FLAG 10 stale = detect-works).
|
||||
- B4 ✅ GATED (engine:42 FLAG→em-main + git-diff backstop + D9 single-writer).
|
||||
|
||||
## CL2 — PHẦN C (🔴 MANDATE) → ĐẠT 5/5, completeness-gate CỨNG met
|
||||
26 flag, exit 0, qua `governance-detectors.ps1`:
|
||||
- C1 ✅ exec+runtime mechanized (gotcha-ref 0 broken + 13 dangling-wikilink LOW).
|
||||
- C2/B3 ✅ exec+runtime mechanized (canonical 55/339/69/88 + disk cross-check [OK] + 10 MED).
|
||||
- C3 ✅ exec+runtime mechanized (CẢ 3 fork: wave↔run-trace 15/19f · Dự trù↔Ngân sách PRO 7/6f · two-tier↔all-inherit 17/10f).
|
||||
- C4 ✅ exec+runtime mechanized (self-match=0, 5 paths excluded ALL exist, leaked=0).
|
||||
- C5 ✅ resolve 26/26 + 2-strike HONEST-scoped (convention em-main, detector stateless — KHÔNG over-claim).
|
||||
- NO-API grep 0-hit · 0-auto-write grep 0-hit.
|
||||
|
||||
## CL3 — PHẦN D (🔴) → ĐẠT 11/11, completeness-gate D PASS
|
||||
- D1 session-start.md:83 (detector) · D2 session-end.md:48 (archive-gate) · D3 ultra-on.md:35 (per-turn) · **D4 hmw.js:76-78 THROW MECHANIZED** (verify dòng throw tồn-tại ✓).
|
||||
- **D5/D6/D7 explicit-label = YES** (engine:62-69 table NHÃN-HOÁ EXPLICIT — H11 chuẩn-hoá-mới).
|
||||
- **D8 one-direction codify = YES** (engine:72 'codify mới H11' canonical→derived KHÔNG ghi ngược).
|
||||
- D9 store_memory strip MECHANIZED (grep tools-line 0-hit) · D10 file-tool-write convention (engine tự nhận Bash chưa block cứng) · **D11 byte-0-loss RUNTIME** (h910-curate md5sum+grep-Fxf 10/10 proven).
|
||||
|
||||
## TỔNG: completeness-gate H11 ĐẠT
|
||||
**B (4/4) + C (5/5) + D (11/11) đủ-trọn** = function-floor MET. **A 🟡 tailored** (A6 runtime cần 2× -Apply = legit-gap có-chủ-đích, đã self-doc). Honest residual: B1 soft-net FP (advisory), A6 runtime-partial (by-design), C3 console mojibake (display-only). KHÔNG bộ-khung nào thiếu → KHÔNG phải "áp một phần".
|
||||
@ -0,0 +1,25 @@
|
||||
# RUN — 2026-06-18-h11-checklist-verify (Harness-11 adap · CHECKLIST self-verify, anh giao)
|
||||
|
||||
> **Harness-10 FLAT run-trace** (TRACKED). 3× investigator-codebase (read-only ∥, evidence-mapping). Synthesis → `checklist-verify-synthesis.md`.
|
||||
|
||||
- **Workflow:** Harness-11 CHECKLIST formal self-verify (anh giao "workflow double check lại checklist 1 lần nữa")
|
||||
- **Mode:** hmw RUN-TRACE. Rà TỪNG item checklist H11 broadcast → chấm nấc + bằng-chứng. "Completeness-gate: bằng chứng thật, KHÔNG bằng trí nhớ."
|
||||
- **Khác double-check #1:** DC#1 = adversarial correctness/regression. Cái này = formal checklist scorecard từng-item theo đúng rubric checklist H11 (Hành-động · Tự-verify · Nấc · Loại).
|
||||
|
||||
## Rubric (theo checklist H11)
|
||||
- **Nấc:** executed-file (file tĩnh có trên đĩa) / runtime (đã chạy-quan-sát) / executed-file+runtime.
|
||||
- **Loại:** mechanized (artifact/cổng-máy bảo-chứng) / convention (người tuân-thủ, không cổng máy).
|
||||
- **Completeness-gate:** B+C+D phải hiện-diện ĐỦ-TRỌN; thiếu 1 = CHƯA-ĐẠT. A = 🟡 tailorable.
|
||||
|
||||
## 3 lane
|
||||
| Lane | Role | Checklist section |
|
||||
|---|---|---|
|
||||
| CL1 | investigator-codebase | PHẦN A (A1-A7 🟡) + PHẦN B (B1-B4 🔴) |
|
||||
| CL2 | investigator-codebase | PHẦN C (C1-C5 🔴 mandate) — chạy detector |
|
||||
| CL3 | investigator-codebase | PHẦN D (D1-D11 🔴) — D.1 nhịp + D.2 3-tier + D.3 4-chốt |
|
||||
|
||||
## Acceptance
|
||||
Mỗi item: status + evidence (file:line / run-output) + nấc + loại, KHÔNG trí-nhớ. Section verdict ĐẠT/CHƯA. Completeness-gate cuối: B+C+D đủ-trọn?
|
||||
|
||||
## Run-id
|
||||
`wf_39cd4cbe-f07`
|
||||
@ -0,0 +1,28 @@
|
||||
# DOUBLE-CHECK SYNTHESIS — Harness-11 adap (2026-06-18-h11-doublecheck · `wf_a0b68d2f-30e`)
|
||||
|
||||
> 3× reviewer (read-only, adversarial ∥). Em-main scribe @P3. **VERDICT: ✅ PASS — 0 blocker.** Re-verify commit `e70c046` + regression của refinement em-main áp sau REVIEW-1.
|
||||
|
||||
## DA1 — over-suppression regression → FAILED-no-StructuredOutput → **em-main self-gate CLOSED**
|
||||
- Reviewer lane parallel[0] không trả StructuredOutput (lỗi #53 schema-force tái diễn).
|
||||
- **Em-main self-gate (recovery-pattern):** inject prose fake-drift "99 migration" (KHÔNG table-row/version/historical) → detector **CAUGHT** "99 migration but canonical=55" → revert clean. → **no over-suppression, runtime-proven.**
|
||||
- Bonus: detector pure-ASCII (Python scan 0 non-ASCII — gotcha #30 clean) · PS parse OK · exit 0 · 26 flag.
|
||||
|
||||
## DA2 — committed-state correctness → **PASS**
|
||||
- B1 **exactly 11** pointer-conversion (grep -c = 11). root CLAUDE.md:53 **tail byte-identical** (`sed -n '53p'` ends "...phiếu cũ.)" = old) → 0 load-bearing prose loss; chỉ leading count-phrase swap + S74/S73 additive prefix.
|
||||
- ef-core Mig 54/55 rows = **tên migration THẬT trên disk** (`AddPeSuggestedAndApprovedPrice` + `AddCcmNoteToPeWorkItemBudget` .cs EXIST).
|
||||
- 0 stale-count residual (grep 53mig/306test/68gotcha/93bảng = 0). cadence §2.1.3/§L.b cú-pháp đúng, path tồn-tại + run clean. engine-doc line-ref accurate (D5=:67, hmw.js:76/103/111, budget.json:19).
|
||||
- 2 MINOR (đã FIX): agents/README "(pending)" stale + C2 FP CLAUDE.md:84/:90.
|
||||
|
||||
## DA3 — containment + regression → **PASS** (0 blocker/0 major/1 minor-info)
|
||||
- Q1 0 production code (grep src/|fe-* = 0). Q2 run-trace đủ (audit/review NO sub = read-only em-main-scribe ✓ · implement có sub-task-0/1 = general-purpose Write ✓ · ledger CLOSE-beat all prior). Q4 single-writer (0 sub MEMORY.md residual · .archive-strikes.json absent). Q5 budget.json pure-additive (measured/tiers/last_sleep_at untouched).
|
||||
- **Q3 broadcasts byte-verified:** recompute INBOX body=`b2a2fc1cf399` (==_index ==frontmatter), whole-file=`318ff9f6` (==commit-msg); OUTBOX body=`7fa1b53a61ae` (==_index ==frontmatter). KHỚP TUYỆT-ĐỐI.
|
||||
- **REGRESSION over-suppression HUNT (độc-lập confirm self-gate):** enumerate cái C2-skip che = per-item frozen (ef-core "Mig 12→10 bảng") + Session-N historical + CLAUDE.md "88 table"==canonical — KHÔNG cái nào là live-aggregate-state-count. Cả 11 prose-drift VẪN bắt. C1 normalize verified 2 chiều (genuine-dangling vẫn flag + prefix-differ vẫn flag). **NO real drift hidden.**
|
||||
- Anti-finding: C3 console mojibake "D? tr<74>" = console-codepage Bash-capture artifact, KHÔNG script bug ([Console]::OutputEncoding=UTF8 render "Dự trù PRO" đúng; Select-String so-sánh UTF-8 chính-xác).
|
||||
|
||||
## Em-main actions post-doublecheck
|
||||
1. ✅ self-gate fake-drift (close DA1) — no over-suppression, runtime.
|
||||
2. ✅ +C2 "test project" skip (line 90 FP gone, 27→26) — ASCII clean.
|
||||
3. ✅ agents/README "(pending)" → run-id thật (review `wf_d7ca1ff8-942` + doublecheck `wf_a0b68d2f-30e`).
|
||||
4. ⚠️ Tree-line FP-skip ATTEMPTED rồi REVERT (literal box-glyph = gotcha #30 trap; \u-escape edit bị tool render-normalize → bỏ, line 84 "6 test" giữ làm documented soft-net FP, advisory exit-0 harmless). **Bài học: KHÔNG đưa box-glyph vào .ps1 — kể cả qua Edit tool (normalize).**
|
||||
|
||||
## VERDICT: PASS — committed-state đúng, refinement 0-regression (triple-confirmed self-gate+DA2+DA3), containment clean. Sẵn-sàng checklist-verify + push.
|
||||
20
.claude/workflows/runs/2026-06-18-h11-doublecheck/run.md
Normal file
20
.claude/workflows/runs/2026-06-18-h11-doublecheck/run.md
Normal file
@ -0,0 +1,20 @@
|
||||
# RUN — 2026-06-18-h11-doublecheck (Harness-11 adap · DOUBLE-CHECK #1, anh giao)
|
||||
|
||||
> **Harness-10 FLAT run-trace** (TRACKED). 3× reviewer (read-only, adversarial ∥). Synthesis → `doublecheck-synthesis.md`.
|
||||
|
||||
- **Workflow:** Harness-11 adap — DOUBLE-CHECK vòng 2 (anh giao "double check lại 1 turn nữa")
|
||||
- **Mode:** hmw RUN-TRACE free-text. Re-review state ĐÃ COMMIT `e70c046` (post-refinement).
|
||||
- **Trọng tâm:** REVIEW vòng 1 (`wf_d7ca1ff8-942`) đã PASS + em-main refine C2/C1 (59→27). Vòng này soi **regression của chính refinement** + bất kỳ thứ gì 2 vòng trước sót.
|
||||
|
||||
## 3 lane
|
||||
| Lane | Role | Focus |
|
||||
|---|---|---|
|
||||
| DA1 over-suppression regression | reviewer | C2 context-skip (table-row/version/historical) + C1 [-_] normalize có làm detector MÙ drift thật không? Test: tạo fake-drift trong context KHÔNG-skip → chạy → confirm bắt → revert. Over-suppress = nguy cơ #1 của refinement |
|
||||
| DA2 committed-state correctness | reviewer | git show e70c046: engine-doc + B1 ×11 + cadence-wire ĐÚNG trên disk? B1 có vô-tình đổi nghĩa/xóa nội-dung load-bearing? cadence §2.1.3/§L.b cú-pháp đúng? |
|
||||
| DA3 containment + run-trace integrity | reviewer | commit có gì sai? run-trace 4 folder đủ synthesis? broadcasts hash khớp? 0 production code thật? sub-task MD present (impl) vs read-only-scribe (audit/review)? |
|
||||
|
||||
## Acceptance
|
||||
PASS = refinement KHÔNG over-suppress (detector vẫn bắt drift thật) + committed-state đúng + containment clean. Issue → em-main fix trước checklist-verify + push.
|
||||
|
||||
## Run-id
|
||||
`wf_a0b68d2f-30e`
|
||||
@ -0,0 +1,42 @@
|
||||
# IMPLEMENT SYNTHESIS — Harness-11 adap (2026-06-18-h11-implement · `wf_c5e5844e-7c1`)
|
||||
|
||||
> 2× general-purpose (script, file-disjoint ∥) + em-main single-writer (governance MD cluster). Em-main scribe synthesis @P3.
|
||||
|
||||
## Sub Lane 1 — `scripts/governance-detectors.ps1` (PHẦN C + B3) ✅ RUNTIME-PROVEN
|
||||
- 401 dòng, ASCII-only body, PS 5.1, exit 0. Flag: **71 pre-B1 → 59 post-B1 → 27 post-refinement (R2)** (HIGH=0) — drift-fix + FP-cut (C2 context-skip table-row/version/historical + C1 `[-_]` normalize), C4 0 self-match suốt.
|
||||
- **C1 broken-pointer:** gotcha-ref 0 dangling (sạch) + **29 dangling-wikilink** (hyphen-form `[[feedback-x]]` vs file underscore `feedback_x.md` — REAL inconsistency, để REVIEW judge FP-vs-real).
|
||||
- **C2/B3 staleness:** bắt đúng drift root CLAUDE.md (mig53→55·test306→339·gotcha68→69) = runtime proof. Canonical đọc STATUS.md + cross-check disk (mig=55, gotcha=69 khớp → 0 'canonical-stale').
|
||||
- **C3 vocab-fork:** `wave-folder=15f vs run-trace=18f` + bonus `two-tier=17f vs all-inherit=10f` + `Dự trù PRO=7f vs Ngân sách PRO=6f` (rename S65 còn 2 tên sống).
|
||||
- **C4 self-exclusion:** 5 paths excluded, 0 self-match ✓.
|
||||
- **C5 resolve-condition:** 100% FLAG có `resolve:`.
|
||||
- **NO-API verified:** 0 hit Invoke-WebRequest/HttpClient/System.Net/6333/store_memory. Detector **0 auto-write** (Set-Content/Out-File = 0) → DÒ+FLAG-only ✓.
|
||||
- **gotcha #30 RUNTIME-CATCH:** vòng-1 chỉ 53 flag — PS5.1 `-File` decode .ps1 UTF-8-no-BOM bằng ANSI-1252 → literal Việt mojibake → MISS 18 flag (gồm '68 bẫy' + vocab-fork Việt). FIX: body ASCII-only + build token Việt từ code-point runtime (`U @(0x62,0x1EAB,0x79)`). **Minh-chứng giá-trị mandate "chạy thật"** (không runtime → over-claim detector hoạt-động khi nó MÙ token Việt).
|
||||
- **HONEST limitation:** C2 count-token = soft-net ~12 TRUE / ~29 false-pos (version "Core 10", per-row "N bảng module", historical "154 test", greedy cross-line). sev LOW khi |lệch|<10. → người xử cờ, KHÔNG auto-fix (đúng D6).
|
||||
|
||||
## Sub Lane 2 — `scripts/memory-archive-gate.ps1` (PHẦN A) ✅ RUNTIME-PROVEN
|
||||
- 289 dòng, exit 0, DRY-RUN default, NO-API, FLAG-only.
|
||||
- budget.json `archive_gate` block ADDITIVE (autoinject_cap 25600 · low_watermark_ratio 0.85 · keep_floor_entries 5 · strike_threshold 2). `measured`/`tiers`/`last_sleep_at` UNTOUCHED.
|
||||
- **A4 hysteresis** proven (cicd after-est ~21180 < low-water 21760, không dừng ở vạch). **A5 keep-floor** proven (inv/reviewer WARN "keep-floor hit (5); cannot auto-drain - SPLIT/condense" — từ-chối vét-sạch khi 5 entry mới-nhất đã > cap). **A6 2-strike** lifecycle proven (-Apply ×2: run1 strike1 WATCH → run2 strike2 PROPOSE). **A7 NO-API gate** 186/186 pointer resolve across 4 sub có archive + byte-sanity.
|
||||
- DRY-RUN flag over-cap đúng: reviewer 38755 · inv-codebase 31502 · cicd 26798.
|
||||
- 3 bug fixed mid-build (honest): PS-5.1 parser cascade (typo `'`→`"`) · gotcha #30 mojibake (ReadAllText UTF8) · legend-line FP (`^\s*>` skip).
|
||||
|
||||
## Em-main cluster (governance MD — single-writer D9, B4 gated) ✅
|
||||
- **Engine-doc** `docs/governance/harness-11-engine.md` — canonical SE: artifact-map + PHẦN A/B/C/D + **3-tier D5(AUTO)/D6(DÒ+FLAG)/D7(owner-approve) nhãn-hoá EXPLICIT** + **one-direction-lock D8 codify** + CAVEAT honest. Doc khác TRỎ về đây (B1 dogfood).
|
||||
- **B1 pointer + drift-fix (11 edit):** root CLAUDE.md ×7 (count→`docs/STATUS.md` pointer) · ef-core SKILL ×6 (incl +Mig 54/55 rows) · skills/README ×2 · dep-audit SKILL ×1 · docs/CLAUDE.md ×2 (93→88 + 58→pointer). **Post-B1 detector re-run: drift THẬT root CLAUDE.md RESOLVED** (3 real flag gone); còn lại = documented FP (version/per-row/historical).
|
||||
- **Cadence-wire:** session-start §2.1.3 (D1 chạy detector @start) + session-end §L.b(c) (D2 archive-gate @end) + agents/README Upgrade S75 — tất cả TRỎ engine-doc.
|
||||
|
||||
## Completeness-gate status (B+C+D function-floor)
|
||||
- **PHẦN B:** B1 ✅ (pointer + drift-fix) · B2 ✅ (readable giữ) · B3 ✅ (detector C2) · B4 ✅ (em-main gated). ĐẠT.
|
||||
- **PHẦN C:** C1 ✅ · C2 ✅ · C3 ✅ · C4 ✅ (0 self-match) · C5 ✅ (resolve-condition). ĐẠT (runtime-proven).
|
||||
- **PHẦN D:** D1 ✅ (wired session-start) · D2 ✅ (wired session-end) · D3-D4 PRESENT (sẵn) · D5/D6/D7 ✅ (3-tier codify engine-doc) · D8 ✅ (one-direction-lock codify) · D9/D10/D11 PRESENT-mạnh (sẵn). ĐẠT.
|
||||
- **PHẦN A (🟡):** A1-A3 sẵn · A4/A5/A6 ✅ (params + gate) · A7 ✅ (NO-API standing-gate). Mechanized-hoá hơn (tailored).
|
||||
|
||||
## Single-writer (D9) respected
|
||||
git status: sub Lane 1 chỉ tạo `governance-detectors.ps1` + sub-task-0.md · Lane 2 chỉ `memory-archive-gate.ps1` + budget.json + sub-task-1.md · em-main = mọi canonical MD. KHÔNG sub đụng canonical/agent-memory/sibling. Containment CLEAN.
|
||||
|
||||
## Cho REVIEW (W3) đánh-giá
|
||||
1. Completeness-gate: B+C+D đủ-trọn? (claim ĐẠT — verify độc-lập).
|
||||
2. Detector quality: FP-rate C2 (~29/71) có chấp-nhận-được không, hay cần refine regex context-aware? (em KHÔNG tự-refine = tránh self-bias).
|
||||
3. C1 wikilink 29 flag: hyphen-vs-underscore = REAL inconsistency hay detector normalization-FP?
|
||||
4. Honesty: nấc executed-file vs runtime đúng chưa, có over-claim không.
|
||||
5. Containment: single-writer giữ, no auto-write-of-law.
|
||||
24
.claude/workflows/runs/2026-06-18-h11-implement/run.md
Normal file
24
.claude/workflows/runs/2026-06-18-h11-implement/run.md
Normal file
@ -0,0 +1,24 @@
|
||||
# RUN — 2026-06-18-h11-implement (Harness-11 adap · STAGE 2 IMPLEMENT)
|
||||
|
||||
> **Harness-10 FLAT run-trace** (TRACKED). Sub ghi `sub-<role>-<i>.md` phẳng. Synthesis → `implement-synthesis.md`.
|
||||
|
||||
- **Workflow:** Harness-11 adap — IMPLEMENT (fill GAP từ audit-synthesis)
|
||||
- **Mode:** hmw RUN-TRACE. 2× general-purpose (script, file-disjoint ∥) + em-main MD cluster (single-writer D9)
|
||||
- **Opened:** 2026-06-18 (S75)
|
||||
- **Input:** `../2026-06-18-h11-audit/audit-synthesis.md` GAP-list
|
||||
|
||||
## Phân-công (single-writer split D9: sub=SCRIPT non-canonical · em-main=governance MD)
|
||||
| Lane | Owner | Deliverable | PHẦN |
|
||||
|---|---|---|---|
|
||||
| impl-detectors | general-purpose (Write+Bash) | `scripts/governance-detectors.ps1` — C1 broken-pointer + C2 staleness(=B3) + C3 vocab-fork + C4 self-exclusion + C5 resolve-condition · NO-API grep · RUNTIME-prove | C1-C5 + B3 |
|
||||
| impl-archive-gate | general-purpose (Write+Bash) | `scripts/memory-archive-gate.ps1` (hysteresis 0.85 + keep-floor ≥5 + 2-strike + NO-API L1-eval) + budget.json A4/A5/A6 params | A1/A4/A5/A6/A7 |
|
||||
| em-main cluster | 👤 em-main | `docs/governance/harness-11-engine.md` (D1-D11 + 3-tier D5/D6/D7 + one-direction-lock D8 + trỏ detector+canonical) · B1 pointer+drift-fix (root CLAUDE.md·ef-core SKILL·skills/README·dep-audit·docs/CLAUDE.md) · cadence-wire (session-start D1·session-end D2) · agents/README ref | B1·D5-D8 |
|
||||
|
||||
## Acceptance (completeness-gate: B+C+D đủ-trọn)
|
||||
- C: 3 detector chạy được (runtime), FLAG drift thật + 0 self-match (C4)
|
||||
- B: derived count → pointer canonical; B3 detector FLAG mismatch
|
||||
- D: 3-tier + one-direction-lock codify explicit
|
||||
- Single-writer giữ: sub KHÔNG đụng canonical MD; em-main scribe
|
||||
|
||||
## Run-id
|
||||
`wf_c5e5844e-7c1`
|
||||
124
.claude/workflows/runs/2026-06-18-h11-implement/sub-task-0.md
Normal file
124
.claude/workflows/runs/2026-06-18-h11-implement/sub-task-0.md
Normal file
@ -0,0 +1,124 @@
|
||||
# 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.md` CURRENT 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 `*.cs` trong **mọi** dir tên `Migrations` dưới `src` (exclude `bin|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+)\.` trong `docs/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 → FLAG `derived-stale` (MED nếu |lệch|≥10, else LOW).
|
||||
|
||||
### C1 — broken-pointer ✅
|
||||
- **(a) gotcha-ref**: grep `docs/** + .claude/** *.md` bắt `gotcha[s]?\s*#?(\d+)` + bare `#(\d+)`. `gotcha #N` luôn validate (N>max → broken MED; thiếu `### N.` anchor → LOW). Bare `#N` chỉ 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>.md` khô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 in `self-exclusion: N paths excluded` + assert `self 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|curl|api|http` |
|
||||
| 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 <row> to <disk>" · 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)
|
||||
1. **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-local `4 bảng Budget`, Phase-historical `71 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.
|
||||
2. **`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).
|
||||
3. **bare `#N` gotcha-ref** cố-ý bỏ qua khi N>max (tránh nhầm Run #312/PR #) → có thể MISS broken-ref dạng `#999` thật. Trade-off ít-noise.
|
||||
4. **agent-memory `2026-06.gist.md` 2 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.
|
||||
5. **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).
|
||||
6. **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.
|
||||
```
|
||||
156
.claude/workflows/runs/2026-06-18-h11-implement/sub-task-1.md
Normal file
156
.claude/workflows/runs/2026-06-18-h11-implement/sub-task-1.md
Normal file
@ -0,0 +1,156 @@
|
||||
# sub-task-1 — Harness-11 PART-A — memory-archive-gate.ps1 (hot-mem auto-archive standing-gate, NO-API)
|
||||
|
||||
**Role:** general-purpose (implementer, has Write/Edit) · **Run:** 2026-06-18-h11-implement
|
||||
**Files owned (single-writer, file-disjoint):**
|
||||
- `scripts/memory-archive-gate.ps1` (NEW, 266 lines)
|
||||
- `.claude/agent-memory/memory-budget.json` (ADD `archive_gate` block only)
|
||||
- this sub-MD
|
||||
|
||||
**Verdict:** DONE + RUNTIME-PROVEN. DRY-RUN clean (A7 GATE PASS 186/186 pointers, exit 0). A6 2-strike lifecycle proven across 2 `-Apply` runs (WATCH@strike1 → PROPOSE@strike2). NO-API + FLAG-ONLY audited clean.
|
||||
|
||||
---
|
||||
|
||||
## 1. budget.json — additive params (acceptance item 1)
|
||||
|
||||
Added `archive_gate` block **before** `measured` (did NOT touch `measured` / `tiers` / `last_sleep_at` / `seeded_date`):
|
||||
|
||||
```json
|
||||
"archive_gate": {
|
||||
"_note": "Harness-11 PART-A ... ADDITIVE ...",
|
||||
"autoinject_cap_bytes": 25600,
|
||||
"low_watermark_ratio": 0.85,
|
||||
"keep_floor_entries": 5,
|
||||
"strike_threshold": 2
|
||||
}
|
||||
```
|
||||
|
||||
- `autoinject_cap_bytes: 25600` = A1 over-cap line (matches existing `tiers.l1_hot.autoinject_cap_bytes`).
|
||||
- `low_watermark_ratio: 0.85` → `floor(0.85 * 25600) = 21760` = A4 hysteresis drain target.
|
||||
- `keep_floor_entries: 5` = A5.
|
||||
- `strike_threshold: 2` = A6.
|
||||
|
||||
**Untouched-block proof (runtime):**
|
||||
```
|
||||
JSON parse OK
|
||||
archive_gate: cap=25600 low_ratio=0.85 keep_floor=5 strike=2
|
||||
measured block still present? cicd l1_hot=23653
|
||||
last_sleep_at preserved? 2026-06-18
|
||||
```
|
||||
|
||||
## 2. memory-archive-gate.ps1 — structure
|
||||
|
||||
Two independent passes, DRY-RUN by default (`-Apply` only advances the strike counter; **never** moves/edits a `.md`).
|
||||
|
||||
| Pass | Maps to | What it does |
|
||||
|---|---|---|
|
||||
| PASS 1 PLANNER | A1 / A4 / A5 / A6 | per `<sub>/MEMORY.md`: measure bytes (A1); if > cap, plan #oldest-entries to MOVE to get **below** low-watermark (A4 hysteresis), keeping ≥ keep_floor newest (A5), gated behind 2-strike (A6). Prints `sub bytes over? entries strike after-est resolve`. |
|
||||
| PASS 2 A7-GATE | A7 NO-API | per `<sub>/archive/_INDEX.md`: extract every `substring:"..."`; literal `.Contains()` search across `archive/*.md` (UTF-8); byte-sanity (file exists + size>0). Prints PASS/FAIL per pointer. |
|
||||
|
||||
Key design points:
|
||||
- **Entry boundary** (A5 counting) = first of `^##` / `^###` / `^---` (regex `^(#{2,3}\s|---\s*$)`). MEMORY.md files here are h2-only today (verified) so marker-count == entry-count; regex also tolerates h3/HR files.
|
||||
- **after-est** = `total − sum(line.Length+2)` for the moved prefix (CRLF-aware estimate; prefixed `~` to flag it as approximate — the gate never performs a real cut).
|
||||
- **A6 strike state** persisted to `.claude/agent-memory/.archive-strikes.json` (flat `{sub:int}`, ASCII). Reset-to-0 on any clean run ⇒ *consecutive*-over-cap semantics. Mutated **only under `-Apply`** so DRY-RUN is side-effect-free.
|
||||
- **A7 robustness**: resolves the substring against ALL `archive/*.md` for the sub (not just the arrow-named file) because the 3 `_INDEX` formats name the target differently (reviewer `→ \`file\``; cicd `\`file\` · substring:`; inv-codebase q-shorthand table). A unique substring landing anywhere in the sub's frozen archive == resolved. Skips `^\s*>` blockquote legend lines (the `substring:"<unique-string>"` template is documentation, not a record).
|
||||
- **Exit code**: `2` on A7 integrity failure (broken pointer / 0-byte archive); `0` otherwise. Over-cap is a FLAG, not an error (gate reports, human curates).
|
||||
|
||||
## 3. RUNTIME — DRY-RUN (canonical evidence)
|
||||
|
||||
`powershell.exe -ExecutionPolicy Bypass -File scripts\memory-archive-gate.ps1` → EXIT 0:
|
||||
|
||||
```
|
||||
============================================================
|
||||
memory-archive-gate.ps1 - Harness-11 PART-A
|
||||
mode : DRY-RUN (no writes at all)
|
||||
cap : 25600 bytes (autoinject_cap)
|
||||
low-water : 21760 bytes (A4 hysteresis drain target = ratio 0.85)
|
||||
keep-floor : 5 newest entries (A5)
|
||||
strike-need : 2 consecutive over-cap runs to PROPOSE (A6)
|
||||
============================================================
|
||||
|
||||
### PASS 1 - hot-tier over-cap planner (FLAG ONLY, no moves)
|
||||
|
||||
sub bytes over? entries strike after-est resolve
|
||||
------------------------ --------- ----- ---------- ------- ------------ -------
|
||||
cicd-monitor 26798 YES 18 1 ~21180 WATCH (strike 1<2): re-run; propose only after 2 consecutive over-cap
|
||||
database-agent 5917 no 6 0 - ok
|
||||
frontend-designer 24004 no 6 0 - ok
|
||||
harvest-curator 18952 no 6 0 - ok
|
||||
implementer-backend 17692 no 23 0 - ok
|
||||
implementer-frontend 13394 no 16 0 - ok
|
||||
investigator-api 8510 no 9 0 - ok
|
||||
investigator-codebase 31502 YES 20 1 ~27069 WARN keep-floor hit (5); cannot auto-drain - SPLIT/condense entries by hand
|
||||
reviewer 38755 YES 14 1 ~33738 WARN keep-floor hit (5); cannot auto-drain - SPLIT/condense entries by hand
|
||||
test-specialist 24663 no 17 0 - ok
|
||||
tooling-auditor 18431 no 6 0 - ok
|
||||
|
||||
[A6] DRY-RUN: strike counters NOT persisted (run with -Apply to advance strikes)
|
||||
|
||||
### PASS 2 - A7 archive-integrity gate (NO-API: grep + measure only)
|
||||
|
||||
[cicd-monitor] _INDEX.md + 7 archive file(s)
|
||||
-> PASS pointers 76 resolved 76 failed 0
|
||||
[implementer-backend] _INDEX.md + 7 archive file(s)
|
||||
-> PASS pointers 41 resolved 41 failed 0
|
||||
[investigator-codebase] _INDEX.md + 7 archive file(s)
|
||||
-> PASS pointers 40 resolved 40 failed 0
|
||||
[reviewer] _INDEX.md + 5 archive file(s)
|
||||
-> PASS pointers 29 resolved 29 failed 0
|
||||
|
||||
------------------------------------------------------------
|
||||
A7 GATE PASS - total pointers 186, resolved 186, failed 0
|
||||
------------------------------------------------------------
|
||||
EXITCODE=0
|
||||
```
|
||||
|
||||
**Reads the spec's expected over-cap subs:** reviewer 38755 (~37.8KB, over), investigator-codebase 31502 (~30.8KB, over). cicd-monitor 26798 also over (current, > spec's stale ~note). All 8 under-cap subs print `ok`.
|
||||
|
||||
## 4. RUNTIME — A6 2-strike lifecycle (`-Apply` ×2)
|
||||
|
||||
The strike-counter is an **executed-file** mechanism; it needs 2 runs to demonstrate the PROPOSE gate (honest n"executed-file vs runtime" tier).
|
||||
|
||||
**APPLY run 1** → strikes file written `{cicd:1, inv:1, reviewer:1, all-clean:0}`; over-cap subs show `WATCH (strike 1<2)`:
|
||||
```
|
||||
cicd-monitor 26798 YES 18 1 ~21180 WATCH (strike 1<2): re-run; propose only after 2 consecutive over-cap
|
||||
investigator-codebase 31502 YES 20 1 ~27069 WARN keep-floor hit (5); cannot auto-drain - SPLIT/condense entries by hand
|
||||
reviewer 38755 YES 14 1 ~33738 WARN keep-floor hit (5); cannot auto-drain - SPLIT/condense entries by hand
|
||||
[A6] strikes persisted -> ...\.archive-strikes.json
|
||||
```
|
||||
|
||||
**APPLY run 2** → strikes advance to 2; cicd-monitor flips WATCH → **PROPOSE** (it has drainable headroom: move 6 oldest → ~21180 < low-water 21760); inv/reviewer stay WARN (A5 keep-floor priority — newest 5 entries alone exceed cap):
|
||||
```
|
||||
cicd-monitor 26798 YES 18 2 ~21180 PROPOSE archive (strike 2>=2): move 6 oldest -> curate L1->L2 by hand
|
||||
investigator-codebase 31502 YES 20 2 ~27069 WARN keep-floor hit (5); cannot auto-drain - SPLIT/condense entries by hand
|
||||
reviewer 38755 YES 14 2 ~33738 WARN keep-floor hit (5); cannot auto-drain - SPLIT/condense entries by hand
|
||||
```
|
||||
strikes file after run 2: `{cicd:2, inv:2, reviewer:2, all-clean:0}`.
|
||||
|
||||
**Strike artifact cleaned up** post-test (`rm .archive-strikes.json`) — DRY-RUN default never creates it; shipped repo carries no stale strike state. Cold-start DRY-RUN re-verified: EXIT 0, A7 GATE PASS 186/186.
|
||||
|
||||
## 5. Bugs hit + fixed during build (honesty log)
|
||||
|
||||
1. **PS 5.1 parser cascade (mid-build, self-fixed):** line 94 had a typo `'resolve")` — a `"` where a `'` belonged — which left a single-quoted string unterminated and consumed forward, throwing 4 misleading errors anchored at *later* valid lines (102/222/233/266). Root-caused via per-line `0x22`-byte hexdump (`xxd | grep 22`) → the closing `'` of the last `-f` arg was a `"`. Fixed → `'resolve')`. Lesson: PS 5.1 mis-locates the error site of an unterminated string; bisect by counting quotes, not by trusting the reported line.
|
||||
2. **gotcha #30 mojibake (A7 false-FAIL):** first A7 run reported 55 PTR-FAILs, all on pointers containing Vietnamese diacritics / em-dash / arrows (`â€"`, `×`, `â†'`). Cause: PS 5.1 `Get-Content` / `Select-String` default to ANSI codepage and mangle UTF-8. Fix: read BOTH `_INDEX.md` and archive files via `[System.IO.File]::ReadAllText($path, UTF8)` + `String.Contains()` → 189/190 resolve. This **is the gotcha #30 trap the precedent script's "ASCII-only" header warns about**, hit from the read side.
|
||||
3. **legend-line false-positive (true-positive catch):** cicd `_INDEX.md:4` blockquote documents the format with literal `substring:"<unique-string>"`; my regex matched it (77 vs the index's self-declared 76 real pointers). Added `^\s*>` skip → clean 76/76. The gate correctly distinguished a non-record from a broken pointer.
|
||||
|
||||
## 6. Acceptance checklist evidence
|
||||
|
||||
| Item | Verdict | Evidence |
|
||||
|---|---|---|
|
||||
| (1) NO-API | PASS | grep audit: only `Set-Content` is the strikes int-map (line 177); zero http/curl/api/model/qdrant calls. `.md` files are read-only. |
|
||||
| (2) FLAG-ONLY / no auto-write of rules | PASS | DRY-RUN header "no writes at all"; over-cap = printed FLAG; no Move/Edit of any MEMORY.md or archive. `-Apply` writes only the counter. |
|
||||
| (3) PowerShell .ps1, `-ExecutionPolicy Bypass` | PASS | runs via `powershell.exe -ExecutionPolicy Bypass -File`; ASCII-only output; PS 5.1 parse OK. |
|
||||
| (4) RUNTIME-prove | PASS | DRY-RUN output §3 (exit 0) + 2× `-Apply` strike lifecycle §4 pasted from real runs. |
|
||||
| (5) sub writes only file-disjoint + sub-MD | PASS | `git status --porcelain` after cleanup = only `M memory-budget.json` + `?? memory-archive-gate.ps1` (+ this sub-MD in run-folder). No canonical MD touched. |
|
||||
| A1 measure | PASS | bytes column real (reviewer 38755 etc.). |
|
||||
| A4 hysteresis | PASS | drains to BELOW low-water (cicd after-est ~21180 < 21760), not to the line. |
|
||||
| A5 keep-floor | PASS | inv/reviewer WARN "keep-floor hit (5); cannot auto-drain" — refuses to vét-sạch. |
|
||||
| A6 2-strike | PASS | run1 WATCH(1<2) → run2 PROPOSE(2>=2); strikes file 1→2; reset-on-clean. |
|
||||
| A7 pointer-resolve + byte-sanity | PASS | 186/186 across 4 subs w/ archive; UTF-8 round-trip; legend-line skipped. |
|
||||
|
||||
## 7. Tailoring declared (PART-A = 🟡 tailorable)
|
||||
|
||||
Documented inline in the script `[TAILOR]` footer block:
|
||||
- **after-est is an ESTIMATE** (`line.Length+2` CRLF heuristic, `~` prefixed) — real bytes only known post-move, which the gate deliberately never does.
|
||||
- **Entry boundary** = first of `^##`/`^###`/`^---` (today's files are h2-only → exact; tolerant of other styles).
|
||||
- **A6 strike** = flat `{sub:int}` JSON, consecutive semantics via reset-on-clean, requires 2 real `-Apply` runs to reach PROPOSE (matches spec "runtime needs 2 runs").
|
||||
- **A7** resolves substring against ALL `archive/*.md` for the sub (the 3 index formats name targets differently) + skips `>` legend lines.
|
||||
@ -0,0 +1,37 @@
|
||||
# REVIEW SYNTHESIS — Harness-11 adap (2026-06-18-h11-review · `wf_d7ca1ff8-942`)
|
||||
|
||||
> 3× reviewer (read-only, adversarial ∥). Em-main scribe @P3. **VERDICT TỔNG: ✅ PASS** (0 blocker · refinement applied · nit fixed).
|
||||
|
||||
## R1 — COMPLETENESS-GATE → **ĐẠT** (B+C+D function-floor đủ-trọn)
|
||||
- **PHẦN B 4/4 PRESENT:** B1 (engine:39 + landed root CLAUDE.md 7 pointer + ef-core 4 + README 2 + dep-audit 1) · B2 (readable giữ) · B3 (script:142-243 LIVE-catch, KHÔNG stub) · B4 (em-main gated + git-diff backstop).
|
||||
- **PHẦN C 5/5 runtime-proven:** C1 (0 gotcha-dangling + wikilink-dangling) · C2 (canonical+disk cross-check OK) · C3 (3 vocab-fork) · C4 (**0 self-match LIVE**, 5 paths excluded, leaked=0) · C5 (100% flag có resolve:).
|
||||
- **PHẦN D 11/11:** D1 wired §2.1.3 · D2 wired §L.b(c) · D3 · D4 (hmw.js:76 THROW mechanized) · **D5/D6/D7 3-tier EXPLICIT** (engine §D.2 :67-69) · **D8 one-direction-lock codify** (:72) · D9 · D10 · D11.
|
||||
- **PHẦN A 🟡 over-floor:** A4/A5/A6/A7 runtime-proven.
|
||||
- **HARDEST gate: NO-API PASS · 0-auto-write PASS.** 0 floor thiếu.
|
||||
|
||||
## R2 — DETECTOR CORRECTNESS+QUALITY → **PASS** (6/6 mechanized criteria clean)
|
||||
- C4 0-self-match ✓ · C5 59/59 resolve ✓ · NO-API grep 0-hit ✓ · 0-auto-write (chỉ Out-Null in-mem) ✓ · DRY-RUN no-write (git pre==post byte-identical) ✓ · A7 186/186 pointer-resolve + byte-sanity ✓.
|
||||
- **2 refinement non-blocking → ÁP DỤNG (review→fix loop):**
|
||||
- **(7a) C2 FP-rate ~89% (24/27 FP):** version-token "EF Core 10" / per-row "N bảng module" / historical "154 test". → **FIXED:** context-skip (table-row `^\s*\|` + historical `baseline|S\d\d|(current` + version-prefix `core|.net|react|vite|mig|phase|session`). C2 **27→11**.
|
||||
- **(7b) C1 wikilink 29 = REAL (KHÔNG normalization-FP):** 3 class (hyphen↔underscore-fork · truly-missing · cross-scope). → **FIXED:** normalize `[-_]` both sides → auto-resolve fork-where-file-exists. C1 **29→13** (13 còn lại = genuinely-missing/cross-scope, drift THẬT của memory-index).
|
||||
- **Post-refinement: TOTAL 59→27, PS parse OK, C4 vẫn 0 self-match.** Detector từ "noisy" → "sắc".
|
||||
|
||||
## R3 — HONESTY+CONTAINMENT → **PASS** (0 blocker, 1 MINOR nit)
|
||||
- OVER-CLAIM: none-material. Re-run cả 2 script khớp claim. Nấc executed-file vs runtime phân ĐÚNG (A6-2strike tự-khai executed-file; A7/DRY-RUN tick runtime). 71→59 reconcile honest (pre-B1 vs post-B1, grep CLAUDE.md `53 migration`=0 chứng-minh B1 gỡ TP).
|
||||
- SINGLE-WRITER D9: git-status grep MEMORY.md = 0-hit · `.archive-strikes.json` absent · budget.json additive-only · 0 sub đụng canonical. CLEAN (KHÔNG lặp S72 residual).
|
||||
- NO-AUTO-WRITE-OF-LAW (D6): detector 0 file-write stdout-only · archive-gate 1 Set-Content → counter `.archive-strikes.json` (-Apply-only, KHÔNG MD/luật). ✓
|
||||
- B1 GATED (B4): sub KHÔNG tự sửa canonical, em-main cluster. ✓
|
||||
- byte-0-loss D11: A7 verify pointer-resolve + nonzero-size (MOVE thật engine CỐ Ý không làm — DRY-RUN; D11 dựa precedent h910-curate md5sum). Phân-định đúng, KHÔNG over-claim.
|
||||
- CAVEAT engine-doc:79-84 trung-thực (no-OS-hook + auto-write-CỐ-Ý-CHƯA-LÀM + net-not-lock + executed≠runtime).
|
||||
- **MINOR nit → FIXED:** agents/README "71 flag" = volatile-count hardcoded (chính anti-pattern engine chống) → đổi non-volatile.
|
||||
|
||||
## Em-main actions post-review
|
||||
1. ✅ C2 context-skip + C1 normalize (script refinement, re-run 59→27, parse OK).
|
||||
2. ✅ agents/README "71 flag" → non-volatile (số → run-trace).
|
||||
3. ✅ reconcile số trong synthesis/ledger (71 pre-B1 → 59 post-B1 → 27 post-refinement).
|
||||
|
||||
## Honest residual (cho report AI_INFRA)
|
||||
- C1 13 wikilink-dangling = REAL pre-existing memory-index drift (engine SURFACE đúng; fix từng cái = chore riêng, KHÔNG block adap).
|
||||
- C2 11 residual = module-local edge ("4 bảng Budget" historical, "1 migration" prose revert-cmd) — soft-net chấp-nhận (LOW/MED advisory exit-0, self-documented).
|
||||
- C3 console mojibake VN-variant = cosmetic Bash-capture (match THẬT đúng, gotcha #30 code-point builder hoạt-động).
|
||||
- **VERDICT: PASS — engine adopted, completeness-gate ĐẠT, detector sharp + runtime-proven, containment clean.**
|
||||
21
.claude/workflows/runs/2026-06-18-h11-review/run.md
Normal file
21
.claude/workflows/runs/2026-06-18-h11-review/run.md
Normal file
@ -0,0 +1,21 @@
|
||||
# RUN — 2026-06-18-h11-review (Harness-11 adap · STAGE 3 REVIEW double-check)
|
||||
|
||||
> **Harness-10 FLAT run-trace** (TRACKED). 3× reviewer (read-only, adversarial ∥). Synthesis → `review-synthesis.md`.
|
||||
|
||||
- **Workflow:** Harness-11 adap — REVIEW (B2 mandate: workflow độc-lập soi IMPLEMENT + chắt know-how)
|
||||
- **Mode:** hmw RUN-TRACE, free-text-in-findings (KHÔNG ép custom-schema per S73 lesson). reviewer read-only → subMdPath + em-main scribe.
|
||||
- **Opened:** 2026-06-18 (S75)
|
||||
- **Input:** engine-doc `docs/governance/harness-11-engine.md` · 2 script `scripts/governance-detectors.ps1` + `memory-archive-gate.ps1` · B1 edits · cadence-wire · `../2026-06-18-h11-implement/implement-synthesis.md`
|
||||
|
||||
## 3 lane adversarial
|
||||
| Lane | Role | Focus |
|
||||
|---|---|---|
|
||||
| R1 completeness-gate | reviewer | verify B+C+D function-floor đủ-trọn (mandate gate) — từng B1-4/C1-5/D1-11 PRESENT + evidence; CHƯA-ĐẠT nếu thiếu 1 |
|
||||
| R2 detector correctness+quality | reviewer | RUN 2 script độc-lập: C4 0-self-match · C5 resolve-condition · NO-API · 0 auto-write · FP-rate chấp-nhận? · C1 wikilink 29-flag real-vs-FP |
|
||||
| R3 honesty+containment | reviewer | over-claim nấc (executed-file vs runtime)? single-writer D9 git-diff? no auto-write-law (D6)? byte-0-loss? synthesis claim khớp disk? |
|
||||
|
||||
## Acceptance
|
||||
PASS = B+C+D đủ-trọn (completeness) + detector runtime-correct (C4/C5/NO-API) + 0 over-claim + containment clean. Issue → em-main fix trước commit.
|
||||
|
||||
## Run-id
|
||||
`wf_d7ca1ff8-942`
|
||||
@ -0,0 +1,16 @@
|
||||
# CURATE synthesis — G1 close (em-main single-writer, C4)
|
||||
|
||||
> Workflow `wf_f32987b8-03f` · 2 general-purpose file-disjoint (1/file, NO same-file race). Closes finalize-review G1.
|
||||
|
||||
## Result (em-main verified git-truth, KHÔNG tin self-report)
|
||||
| File | before → after | moved | 0-byte-loss |
|
||||
|---|---|---|---|
|
||||
| reviewer/MEMORY.md | 36738 → **24844B** (<25600 ✓) | 10 oldest (S33/S35/S43/S49 gates + cumulative + S40-ptr + 2 die-meta + 2 redundant H10-adap) | archive +22 -0 · grep-Fxf 10/10 byte-exact |
|
||||
| investigator-codebase/MEMORY.md | 29819 → **23187B** (<25600 ✓) | 3 oldest (S66/S65ter/S65 recon) | archive +6 -0 · md5sum cut==append `cedc21eb` |
|
||||
|
||||
## Em-main completion (after curate)
|
||||
- **reviewer gist gen:2** added (agent skipped gistUpdated=false) — 10 moved entries distilled compact: 6 routine gates [thấp] N/A-marker · 2 die-meta → cross-ref `[[feedback_agent_kill_recovery]]` · 2 H10-adap → cross-ref kept-S71 + `[[feedback_harness10_run_trace]]`. Closes A4 coverage-diff ("survive OR N/A"). (investigator gist gen:2 = agent did.)
|
||||
- **budget.json re-measured** (script `measure-agent-memory.ps1`, transcribe not fabricate): reviewer/inv/cicd/impl-be updated; **l1_over_30kb=false ALL 11** + seeded_date→S71.
|
||||
|
||||
## Self-gate verdict: 🟢 G1 CLOSED
|
||||
Both MEMORY.md <25600 auto-inject cap · archive additive (0-byte-loss) · _INDEX +pointers · gist gen:2 both · budget accurate. **Root-cause fixed structurally** (hmw.js RUN-TRACE writeGuard cấm sub tự-ghi MEMORY → future hmw.js runs no-race; custom-workflow lesson → `feedback_harness10_run_trace` #2). G2/G3/minor done em-main (user-memory + gitignore).
|
||||
20
.claude/workflows/runs/2026-06-18-h910-curate/run.md
Normal file
20
.claude/workflows/runs/2026-06-18-h910-curate/run.md
Normal file
@ -0,0 +1,20 @@
|
||||
# RUN — Harness-9 curate G1 — reviewer + investigator L1→L2 (close finalize gap)
|
||||
|
||||
- **run-id (folder):** 2026-06-18-h910-curate
|
||||
- **workflow run-id (evidence B3):** wf_f32987b8-03f
|
||||
- **mục tiêu:** đóng G1 finalize-review — 2 file MEMORY.md over-cap (S71 same-role race) curate L1→L2 về <25600B, 0-byte-loss.
|
||||
- **checkpoint:** APPROVED (closing finalize-review G1, anh giao "hoàn chỉnh")
|
||||
- **L1 @P1:** finalize run CLOSED ✓ (no orphan)
|
||||
- **opened:** 2026-06-18 09:52 +07
|
||||
- **status:** OPEN → running
|
||||
|
||||
## Split (file-disjoint — 1 agent/file, KHÔNG same-file race như INVEST/finalize)
|
||||
| Agent | File | before → target |
|
||||
|---|---|---|
|
||||
| 🟦 A1 (general-purpose) | `reviewer/MEMORY.md` + reviewer/archive/* | 36738B → <25600 (cắt ~11KB cũ nhất) |
|
||||
| 🟦 A2 (general-purpose) | `investigator-codebase/MEMORY.md` + inv/archive/* | 29819B → <25600 (cắt ~4.3KB cũ nhất) |
|
||||
|
||||
## Cách (Harness-9 L1→L2, 0-byte-loss)
|
||||
move entry CŨ NHẤT (FIFO cuối Recent-activity, giữ foundation+S71-mới) → APPEND byte-exact `archive/2026-06.md` + 1-dòng `_INDEX.md` (substring sha/phrase-keyed) + gist distill-gen bump. Verify wc -c <25600 + numstat archive +N -0.
|
||||
|
||||
## Output → em-main verify (numstat 0-loss + wc -c) + re-measure budget.json
|
||||
@ -0,0 +1,20 @@
|
||||
# FINALIZE synthesis — H-9 + H-10 + checklist full audit (em-main single-writer, C4)
|
||||
|
||||
> Workflow `wf_73de399d-753` · 3 reviewer. **R1 (Part A) + R2 (Part B+C) no-StructuredOutput ×2-nudge → self-gate** (pattern `feedback_agent_kill_recovery`). **R3 (cross-cutting) thorough — audit luôn Part A/B/C items** → đủ coverage (+ H2 bootstrap + self-verify S71 trước).
|
||||
|
||||
## Verdict: GAPS-FOUND — **0 defect/bug/over-claim-in-code; adoption SOUND**. Findings = deferred-incompleteness cần đóng.
|
||||
|
||||
### COMPLETE (verified-on-disk, R3)
|
||||
- Containment model đồng-bộ **MỌI file** (4 owning + agents/README:111/:162 + harvest-curator:52 + tooling-auditor + session-end:51 + session-start:71) — 0 file giữ B6 cũ. agents/README:8 = frozen 06-07 chronology (acceptable).
|
||||
- Frozen-evidence byte-intact (git diff f36aab8^..HEAD: broadcasts/_index additive 2 row, harness-2/error-ledger/sessions KHÔNG touched).
|
||||
- Part C: run-folder 3-phần ×4 · gitignore (runs NOT-IGNORED / wave-*+teams IGNORED / ls-files 14) · hmw.js engine (node --check OK) · C7 caveat đủ 4 trục.
|
||||
- Part B: 3 run-id evidenced · review-workflow bắt C5 L1 (validate mandate B2). Part A: floor intact (budget/measure-script/_INDEX/gist/ragignore/session-start §2.1.2).
|
||||
- Category 6: email full-grammar VN, hash MATCH, honest self-disclose.
|
||||
|
||||
### GAPS (hoàn chỉnh)
|
||||
- **G1 HIGH:** reviewer/MEMORY.md 33782B (>30720 soft + >25600 auto-inject — harness đã truncate ~8KB HOT lúc spawn này) + investigator-codebase/MEMORY.md 29819B (>25600). Root: S71 same-role race append. budget.json `measured` STALE (S70 snapshot 24795/24052). Harness-9 claim "4 <25KB" re-violated. → re-measure + curate L1→L2 (verbatim→archive, _INDEX, gist distill-gen bump) đến <25600. Acceptance: wc -c <25600 + numstat archive +N -0.
|
||||
- **G2 MED:** (a) `feedback_harness9_l2_recovery_2workflow_adap.md:14` "cả 4 <25KB (đóng P1)" FALSE → note re-opened S71 + re-closed sau G1. (b) `feedback_harness_123_adoption.md:13` wave-folder presented operative → mark SUPERSEDED-by-Harness-10.
|
||||
- **G3 MED:** thêm `feedback_harness10_run_trace.md` (run-trace supersedes wave + containment flip + 3 lesson: engine-no-fs→em-main-scaffold-fragile-C2 · custom-workflow-must-copy-return-delta-guard · check-ignore-exit-trap-CORRECTED) + index pointer + link [[feedback_harness9_l2_recovery_2workflow_adap]].
|
||||
- **MINOR:** gitignore:96-98 exit-trap explanation imprecise ("exits 0 for BOTH" — thực: plain check-ignore exit **1** cho negation; chỉ `-v --no-index` mới 0-both). Command vẫn đúng. Fix khi touch (em fix luôn). Email frozen — KHÔNG sửa.
|
||||
|
||||
## → em-main complete: G1 curate (delegate 2-agent file-disjoint) + G2/G3/minor (em-main) + re-measure + commit.
|
||||
18
.claude/workflows/runs/2026-06-18-h910-finalize/run.md
Normal file
18
.claude/workflows/runs/2026-06-18-h910-finalize/run.md
Normal file
@ -0,0 +1,18 @@
|
||||
# RUN — Harness-9 + Harness-10 + checklist · FINALIZE double-check (toàn diện)
|
||||
|
||||
- **run-id (folder):** 2026-06-18-h910-finalize
|
||||
- **workflow run-id (evidence B3):** wf_73de399d-753
|
||||
- **mục tiêu:** anh giao "review/double-check hoàn chỉnh lại TOÀN BỘ H-9 + H-10 + checklist". KHÔNG chỉ Part C (S71) — audit cả Part A (Harness-9 memory, S70) + Part B (adap 2-workflow) + Part C (run-trace) đúng-nấc THẬT trên đĩa, tìm MỌI gap/incompleteness/over-claim/residual → em-main hoàn chỉnh.
|
||||
- **checkpoint:** APPROVED (anh giao trực tiếp, HMW-mode ON)
|
||||
- **L1 @P1:** prior 3 run CLOSED, 0 orphan ✓
|
||||
- **opened:** 2026-06-18 09:41 +07
|
||||
- **status:** OPEN → running
|
||||
|
||||
## Agents (3× reviewer adversarial, read-only ∥, map mỗi checklist item → đĩa)
|
||||
| # | scope | verify |
|
||||
|---|---|---|
|
||||
| R1 | **Harness-9 Part A (A1-A9 memory archive)** | budget.json (keys+measured khớp script) · measure-script chạy · gist additive+distill-gen · `_INDEX` pointer resolve count=1 · `.ragignore` · A7 budget-audit trong session-start · A8 gist-command separation (tailored?). Nấc THẬT mỗi item. |
|
||||
| R2 | **Harness-9 Part B (B1-B4) + Harness-10 Part C (C1-C8)** | 2-workflow mandate codify (adap-apply) · run-folder 3-phần ×4 · ledger 2-nhịp + orphan-def · 3-layer L1(em-main@P1)/L2(session-start)/L3(session-end idempotent) wired · C3 tracked+committed (git ls-files) · C7 caveat · C8 migration · hmw.js engine intact. |
|
||||
| R3 | **Cross-cutting + residuals** | containment model đồng-bộ MỌI file (4 owning + agents/README + commands) · frozen-evidence còn intact (git log) · 2 over-cap MEMORY (reviewer 33.8/inv 29.8 — gap completion?) · over-claim/doc-drift còn sót H9+H10 · user-memory có cần entry Harness-10 lesson? |
|
||||
|
||||
## Output → `harvest/finalize-synthesis.md` (em main @P3) — gap-list + em-main hoàn chỉnh từng cái
|
||||
@ -0,0 +1,68 @@
|
||||
# AUDIT SYNTHESIS — Harness 8/9/10/10-refine + checklists + hmw (re-audit, honest nấc)
|
||||
|
||||
- **run-id:** 2026-06-18-harness-audit-invest · **wf:** wf_13868efb-ea7 · **date:** 2026-06-18 (S72)
|
||||
- **method:** 4× investigator-codebase ∥. **Part B** returned StructuredOutput (full). **Part A FAILED** (no StructuredOutput after 2 nudges). **Part C / H8** truncated in notification tail. → em-main **SELF-GATE recovery** (ground-truth disk: glob/git/grep/read) per `feedback_agent_kill_recovery` — disclosed, valid review-branch (KHÔNG giấu).
|
||||
- **baseline:** đo vs **CANONICAL spec** (ground-truth AI_INFRA broadcast). `cross-project-status.md` STALE (2026-05-22, RAG Layer A — KHÔNG phải harness 8-10) → KHÔNG dùng để "chứng minh sisters ahead"; honest.
|
||||
|
||||
## Verdict
|
||||
SE đã landed **Harness-8** (all-inherit) + **Harness-9** (PART A memory + PART B adap-2wf) ở nấc tốt. NHƯNG **BEHIND Harness-10 FLAT refine**: SE đang ở run-trace SUBFOLDER cũ, 2 broadcast 06-18 mới nhất (checklist-v2 + h10-flat-detector-refine) UNADOPTED. Lệnh sleep-recovery (§J2 AI_INFRA) absent (by-design, anh muốn parity).
|
||||
|
||||
## Gap matrix (honest nấc)
|
||||
|
||||
### PART A — memory L2 (self-gated; auditor failed)
|
||||
| # | gap | nấc thật | bằng chứng |
|
||||
|---|---|---|---|
|
||||
| A1 budget.json | ok | executed-file | `.claude/agent-memory/memory-budget.json` có per-tier cap + status |
|
||||
| A2 seed-by-measure | ok | executed-file+runtime | `scripts/measure-agent-memory.ps1` + `seeded_date` re-measure S71 |
|
||||
| A3 additive gist + distill-gen | ok | runtime | reviewer/inv `.gist.md` gen:2, impl-be gen:1, verbatim còn |
|
||||
| A4 coverage-diff gate | ok | convention-met | practiced @curate S70/S71 |
|
||||
| A5 _INDEX map | ok | executed-file | 4 sub có `archive/_INDEX.md` pointer-only |
|
||||
| A6 pointer-resolve substring | ok | runtime | substring sha-keyed (S70 design) |
|
||||
| A7 budget-audit @session-start §2.1.2 | ok | convention-met + runtime | trong session-start.md; chạy session này |
|
||||
| **A8 sleep-compress command** | **missing** | none | SE KHÔNG có `/sleep-recovery-memory-l2` (AI_INFRA §J2-internal); SE nén ad-hoc @curate |
|
||||
| A9 .ragignore | ok | executed-file | `.ragignore` tồn tại |
|
||||
| watch | partial | — | frontend-designer 24.0KB + test-specialist 23.9KB sát cap, CHƯA có archive |
|
||||
|
||||
### PART B — adap 2-workflow (MANDATORY; from workflow)
|
||||
| # | gap | nấc thật | note |
|
||||
|---|---|---|---|
|
||||
| B1 implement run-id | ok | runtime | S70 wf_a58e0d15-beb · S71 wf_e4e46725-231 |
|
||||
| B2 review run-id ≠ implement | ok | runtime | S70 wf_9520d8cd-4fe · S71 wf_636bc95b-939 (self-gate disclosed khi reviewer no-StructuredOutput) |
|
||||
| B2.5 reverse-findings | ok | runtime | 3 (S70) + 4 (S71) items |
|
||||
| B3 email both run-ids + true-nấc | ok | runtime | 2 email outbox/ai_infra, hedged honest |
|
||||
| **B4 short-confirm via review** | **partial** | convention-met | codified adap-apply.md:38, CHƯA có runtime instance |
|
||||
|
||||
### PART C + refine — run-trace (MANDATORY)
|
||||
| # | gap | nấc thật | bằng chứng |
|
||||
|---|---|---|---|
|
||||
| **C1 FLAT structure** | **missing** | none | 5 run-folders đều SUBFOLDER (`sub-md/`+`harvest/`); canonical = flat |
|
||||
| C2 scaffold-at-open run.md | ok | convention-met | S71 + audit run này có run.md @open |
|
||||
| C3 git-tracked 2-level | ok | runtime | `git ls-files .claude/workflows/runs`=**22** · check-ignore exit=1 (NOT ignored) |
|
||||
| C4 per-turn harvest | ok | convention-met | — |
|
||||
| C5 3-layer anti-miss | ok | executed-file | runs/README + session-start orphan-scan + session-end gate |
|
||||
| C6 ledger 2-beat + orphan | ok | executed-file | `_ledger.md` open/close, 0 orphan |
|
||||
| C7 honest caveat | ok | convention-met | present |
|
||||
| **C8 migration old→flat + dual-accept gate** | **missing** | none | chưa migrate; close-gate chưa dual-accept |
|
||||
| **refine(a) hmw.js flat** | **missing** | none | `hmw.js:103/:109` còn `sub-md/` subdir |
|
||||
| **refine(b) anti-bypass detector** | **N/A-tailored** | none | SE KHÔNG có detector. **Threat-model khác:** SE dùng Anthropic Workflow tool (KHÔNG có CLI-launcher để lách như hmw.js CLI) → "engine-bypass" gần như N/A; containment SE = git-diff + run-folder TRACKED + ledger orphan-scan (G-015). refine nói detector = "chuyện nội bộ mỗi dự án tự quyết" |
|
||||
|
||||
### Harness-8 + hmw
|
||||
| # | gap | nấc thật | bằng chứng |
|
||||
|---|---|---|---|
|
||||
| H8 11/11 inherit | ok | runtime | H1 monitor confirm disk + `hmw.js:43` resolveModel all-inherit |
|
||||
| hmw.js args.run/wave | ok | executed-file | `:19` `:92` accept args.run + legacy wave |
|
||||
| **2 broadcast pending** | **missing** | none | checklist-v2 + h10-flat-detector-refine UNADOPTED |
|
||||
| sleep §J2 scoping | n/a | — | AI_INFRA-internal, KHÔNG phải sister-obligation |
|
||||
|
||||
## Key honest findings (cho report AI_INFRA)
|
||||
1. **hmw.js engine còn subfolder** (`:103/:109`) — flat migration phải sửa engine, không chỉ folder.
|
||||
2. **5 run cũ giữ nguyên** (đừng rewrite history); new=flat; close-gate dual-accept (C8-A).
|
||||
3. **Detector: SE tailored-out có lý-lẽ** — Workflow-tool ≠ CLI-launcher → no bypass surface; containment hiện hữu (git-diff + tracked + orphan-scan) đã phủ. KHÔNG cargo-cult. (Decision điểm — anh chốt build vs document.)
|
||||
4. **B3 imprecision THẬT (S71):** report/email cite path tắt `runs/` + count "14" cũ; thật = `.claude/workflows/runs` (**22 file**). Folder commit thật; chỉ path-string + count sai → sửa ở report mới.
|
||||
5. **checklist-v2 nấc-rubric** chưa dệt vào self-verify của SE.
|
||||
|
||||
## Fix plan → IMPLEMENT (next)
|
||||
- **em-main cluster (single-writer, coherence):** hmw.js sub-md/→flat · runs/README + workflows/README flat · session-start/end flat + dual-accept gate · agents/README repoint + Upgrade S72 · _ledger gate note · keep 5 old runs (history).
|
||||
- **agent (disjoint):** port `/sleep-recovery-memory-l2` tailored SE (A8).
|
||||
- **detector (anh chốt):** document tailored-out (default) HOẶC build untracked-run detector nhẹ.
|
||||
- **report fix:** correct path/count + B4-convention note.
|
||||
@ -0,0 +1,25 @@
|
||||
# RUN — Harness 8/9/10 re-audit · STAGE 1 INVESTIGATE (🆕 FLAT-trace)
|
||||
|
||||
- **run-id (folder):** 2026-06-18-harness-audit-invest
|
||||
- **workflow run-id (evidence B3):** wf_13868efb-ea7
|
||||
- **task (anh giao):** "double check lại HẾT Harness-8/9/10/10-refine + các checklist + hmw, lấy đầy đủ thông tin, điều chỉnh lại, report trung thực cho AI_INFRA." Stage này = INVESTIGATE: audit fidelity adoption SE vs canonical AI_INFRA, chấm **nấc trung thực** (executed-file / runtime / convention) theo rubric checklist-v2.
|
||||
- **mandate:** Harness-9 PART 2 — B1 INVESTIGATE workflow tách biệt. Tiếp theo: IMPLEMENT (fix gap) → REVIEW (double-check + B2.5 know-how harvest) → REPORT AI_INFRA (B3, kèm 2+ run-id).
|
||||
- **checkpoint:** APPROVED (HMW-mode ON = consent + anh gọi đích danh "hwm").
|
||||
- **opened:** 2026-06-18 11:09 +07
|
||||
- **structure:** 🆕 **FLAT** (dogfood `h10-flat-detector-refine`) — file phẳng cùng cấp, phân biệt bằng TÊN, KHÔNG `sub-md/` + `harvest/` subfolder. (Run cũ S71 giữ subfolder → migrate ở IMPLEMENT, C8.)
|
||||
- **status:** OPEN → running
|
||||
|
||||
## Mục tiêu stage
|
||||
Đo SE so với canonical (ground-truth), KHÔNG nhận vống. 4 mảng song song.
|
||||
|
||||
## Agents (4× investigator-codebase, read-only, parallel)
|
||||
| # | mảng | task |
|
||||
|---|---|---|
|
||||
| A | PART A memory L2 | A1-A9 vs `memory-budget.json` / gist / `_INDEX` / coverage-diff / session-start §2.1.2 / `.ragignore` + sleep-cmd absence |
|
||||
| B | PART B adap-2wf | B1-B4 vs `adap-apply.md` + adap-reports S70/S71 (2 run-id tách? B2.5 know-how? B3 email?) |
|
||||
| C | PART C + refine | C1-C8 + flat-vs-subfolder (migrate?) + git-tracked 2-nấc + anti-bypass detector 3-func presence |
|
||||
| D | H8 + hmw | 11/11 inherit? hmw.js flat-aware? broadcast pending? sleep §J2 scoping + cross_project_note |
|
||||
|
||||
## Output (FLAT — em main viết sau khi workflow trả, C4 per-turn)
|
||||
- `sub-audit-partA.md` · `sub-audit-partB.md` · `sub-audit-partC.md` · `sub-audit-h8hmw.md` (raw per-agent)
|
||||
- `audit-synthesis.md` (bản gom — verified)
|
||||
@ -0,0 +1,29 @@
|
||||
# IMPLEMENT SYNTHESIS — Harness-10 flat migration + sleep-cmd + checklist-v2 (gom/verified)
|
||||
|
||||
- **run-id:** 2026-06-18-harness-fix-implement · **wf:** wf_ac43b5ff-7d1 · 2026-06-18 11:22→11:36 +07
|
||||
- **mandate:** Harness-9 B1 (IMPLEMENT tách biệt). REVIEW (B2) theo sau.
|
||||
|
||||
## Done (verified)
|
||||
**Workflow (2 agent file-disjoint):** sleep-cmd port + runs/README flat — xem `sub-impl-*.md`.
|
||||
**em-main cluster (single-writer, post-workflow, no same-role race):**
|
||||
- `hmw.js` flat (`:103` subMd `sub-md/`→`sub-<role>-<i>.md`) + 5 stale-text + **H4.5→H8 doc-drift fix** · `node --check` PARSE-OK
|
||||
- `workflows/README.md` full-rewrite FLAT + roster 8→**9** + detector-out + C8
|
||||
- `agents/README.md` decision-tree flat + **Upgrade S72** record
|
||||
- `session-start.md` §2.1.1 + `session-end.md` close-gate flat + **dual-accept**
|
||||
- `_ledger.md` FLAT/C8 header note
|
||||
- 🔴 **5 run cũ S71 GIỮ subfolder** (C8 no-history-rewrite)
|
||||
|
||||
## Gap closure vs audit-synthesis
|
||||
| gap | trạng thái |
|
||||
|---|---|
|
||||
| C1/C8/refine-a FLAT | ✅ DONE (hmw.js + 4 docs + audit/implement runs flat) |
|
||||
| A8 sleep-cmd | ✅ DONE (ported, live skill) |
|
||||
| 2 broadcast pending (checklist-v2 + h10-refine) | ✅ ADOPTED |
|
||||
| detector refine-b | ✅ TAILORED-OUT documented (anh chốt) |
|
||||
| B3 report imprecision | ⏳ sửa trong report mới (path `.claude/workflows/runs` + 22 file) |
|
||||
| B4 short-confirm | ⏳ convention note (chưa có runtime instance) |
|
||||
|
||||
## 5-trục (em-main self-gate — REVIEW workflow sẽ double-check độc lập)
|
||||
- **Coverage:** mọi gap audit addressed. **Completeness:** cluster + 2 agent done. **Fidelity:** hmw.js PARSE-OK, 0 fabrication. **Placement:** governance files đúng chỗ. **Corruption:** G-009 clean (sleep-cmd post-scan clean per agent; em-main edits Write/Edit-tool).
|
||||
|
||||
## NEXT: REVIEW workflow (B2) — independent double-check + B2.5 know-how harvest → REPORT (B3) AI_INFRA.
|
||||
@ -0,0 +1,26 @@
|
||||
# RUN — Harness 8/9/10 re-audit · STAGE 2 IMPLEMENT (🆕 FLAT-trace)
|
||||
|
||||
- **run-id (folder):** 2026-06-18-harness-fix-implement
|
||||
- **workflow run-id (evidence B3):** wf_ac43b5ff-7d1
|
||||
- **task:** fix gap từ `audit-synthesis.md` — FLAT migration (C1/C8/refine-a) + port `/sleep-recovery-memory-l2` (A8) + adopt checklist-v2 + h10-refine + document detector TAILORED-OUT (anh chốt) + fix B3 report imprecision.
|
||||
- **mandate:** Harness-9 B1 (IMPLEMENT workflow tách biệt; REVIEW theo sau).
|
||||
- **checkpoint:** APPROVED (HMW-ON + anh "double check hết, điều chỉnh lại").
|
||||
- **opened:** 2026-06-18 11:22 +07
|
||||
- **structure:** 🆕 FLAT (dogfood h10-refine).
|
||||
- **status:** OPEN → DONE
|
||||
|
||||
## Agents (workflow `wf_ac43b5ff-7d1`, 2× general-purpose file-disjoint)
|
||||
| file | task | kết quả |
|
||||
|---|---|---|
|
||||
| `sub-impl-sleepcmd.md` | port `/sleep-recovery-memory-l2` tailored SE | ✅ `.claude/commands/sleep-recovery-memory-l2.md` (137 dòng, SE 9-sub roster, scope SE-only, 6 SE-env deviation, floor intact, live skill) |
|
||||
| `sub-impl-runsreadme.md` | rewrite `runs/README.md` FLAT | ✅ C1-C8 + detector tailored-out + flat naming khớp hmw.js |
|
||||
|
||||
## em-main cluster (single-writer, interdependent — post-workflow, no same-role race)
|
||||
- `hmw.js`: `sub-md/`→flat (`:103` subMd) + 5 stale-text + **H4.5→H8 doc-drift fix** (meta.description) · `node --check` PARSE-OK
|
||||
- `workflows/README.md`: full rewrite FLAT + roster 8→**9** + detector-tailored-out + C8
|
||||
- `agents/README.md`: decision-tree flat + **Upgrade S72** record
|
||||
- `session-start.md` §2.1.1 orphan-scan flat · `session-end.md` close-gate flat + **dual-accept**
|
||||
- `_ledger.md`: FLAT/C8 dual-accept header note
|
||||
- 🔴 **5 run cũ S71 GIỮ subfolder** (C8 — KHÔNG rewrite history)
|
||||
|
||||
## Output (FLAT) → `implement-synthesis.md` (gom) + `sub-impl-*.md` (raw)
|
||||
@ -0,0 +1,8 @@
|
||||
# sub-impl-runsreadme — rewrite runs/README.md FLAT (general-purpose · wf_ac43b5ff-7d1)
|
||||
|
||||
File: `.claude/workflows/runs/README.md` (FLAT rewrite, 11 sections).
|
||||
|
||||
- **Flat naming (khớp `../hmw.js`):** `runs/<id>/` phẳng: `run.md` + `sub-<role>-<i>.md` (raw, prefix `sub-`) + `<stage>-synthesis.md` (verified, suffix). Phân biệt bằng TÊN, KHÔNG subfolder.
|
||||
- **Cover:** C1 flat tree+table · C2 scaffold@open · C3 git-tracked 2-level (check-ignore exit + ls-files) · C5 3-layer · C6 ledger 2-beat · C7 caveat (no fully-autonomous) · C8 migration (5 old keep subfolder, dual-accept).
|
||||
- **Detector TAILORED-OUT** documented (Workflow-tool, no CLI bypass-surface; containment git-diff+tracked+orphan-scan; 3 principles considered N/A).
|
||||
- **Ground-truth flags (honest):** (1) **6 run-folders now** (5 old-subfolder + 1 new flat audit run). (2) SE `.gitignore` KHÔNG có bare `runs` rule → cả 2 path `check-ignore` exit=1; cái "bare `runs/` → 0" là `git ls-files` (no tracked file ở bare path) — documented với full-path guidance + `-v` exit-code trap. (3) parent `workflows/README.md` còn old → flagged (em-main đã fix).
|
||||
@ -0,0 +1,8 @@
|
||||
# sub-impl-sleepcmd — port /sleep-recovery-memory-l2 (general-purpose · wf_ac43b5ff-7d1)
|
||||
|
||||
File: `.claude/commands/sleep-recovery-memory-l2.md` (137 dòng, UTF-8 no-BOM, G-009 post-scan clean). Live skill.
|
||||
|
||||
- **"all" target** = 9 memory-bearing subs (investigator-codebase/api · implementer-backend/frontend · test-specialist · reviewer · cicd-monitor · frontend-designer · database-agent). harvest-curator + tooling-auditor = INFORM-only monitors, excluded (no archive). Note: chỉ 4/9 hiện có archive content (cicd · impl-be · inv-codebase · reviewer) — report-not-hardcode (luôn scan đĩa, guard-rỗng skip).
|
||||
- **Scope:** §J2 AI_INFRA-only → tailored **SE-repo-only**. Provenance note ở đầu (ported từ AI_INFRA §J2-internal theo anh, cross-ref source cmd + design doc §4/§10-P1).
|
||||
- **6 deviation (SE-env, floor preserved):** (1) `last_sleep_at` home = `.claude/agent-memory/memory-budget.json` (AI_INFRA: `.claude/memory-budget.json`); field CHƯA tồn tại trong SE budget → cmd dặn lead thêm lần đầu. (2) 4-field VN: VIỆC·KẾT-LUẬN·BÀI-HỌC·BẤT-NGỜ. (3) coverage-diff +`gotcha#` (5 token). (4) importance VN cao·vừa·thấp. (5) pointer substring sha-keyed no-line-hint. (6) double-distill guard hardened (SE đã có distill-gen:2 gist).
|
||||
- **Floor intact:** additive `.gist.md` new (verbatim untouched) · distill-gen counter · double-distill refuse · coverage-diff deterministic gate · Fidelity reviewer-escalate · INFORM-only ≥7d (no autonomous) · last_sleep_at single home · no L1<30KB force · G-009 Write/Edit + post-scan · ratio=report-not-target · Phase 0→5 mirrored.
|
||||
@ -0,0 +1,28 @@
|
||||
# REVIEW SYNTHESIS — Harness-10 flat migration + sleep-cmd (B2 double-check, gom/verified)
|
||||
|
||||
- **run-id:** 2026-06-18-harness-fix-review · **wf:** wf_d482e10d-5dd · 2026-06-18 13:27→13:4x +07
|
||||
- 3× reviewer adversarial ∥. **Dogfood B2 SUCCESS:** review độc-lập bắt đúng cái IMPLEMENT self-assessment bỏ sót (schema-stale + auto-wire overclaim).
|
||||
|
||||
## Verdicts
|
||||
| reviewer | verdict | finding |
|
||||
|---|---|---|
|
||||
| R1 flat-completeness | PASS-with-concerns | 1 minor schema-stale + 1 untracked-note |
|
||||
| R2 sleepcmd-detector | PASS-with-concerns | **1 MAJOR auto-wire overclaim** + 2 minor |
|
||||
| R3 containment-honesty | **PASS** | containment clean + honesty strong; 1 minor doc-precision |
|
||||
|
||||
→ **0 blocker. 1 major + 4 minor — TẤT CẢ FIXED** (em-main single-writer, post-review).
|
||||
|
||||
## Fixes applied + verified
|
||||
1. [R1 minor] `hmw.js:52` schema `subMdPath` desc `sub-md/`→flat ✓
|
||||
2. [R2 **MAJOR**] auto-check **WIRED**: `memory-budget.json` `+last_sleep_at:null` + `session-start.md:78` + `session-end.md:48` INFORM (null/≥7d, no-autorun) — grep-verified present (was zero) ✓
|
||||
3. [R2 minor] provenance design-doc cite → AI_INFRA absolute path ✓
|
||||
4. [R2 minor] charter substring anchor + "cam"→"cấm" typo ✓
|
||||
5. [R3 minor] `runs/README:31` dogfood reworded (audit-run=synthesis-only self-gate; implement-run=full raw) ✓
|
||||
- **post-fix verify:** `hmw.js` node --check PARSE-OK · budget.json VALID · grep `last_sleep_at` present cả 2 session cmd.
|
||||
|
||||
## B2.5 reverse-findings → AI_INFRA report
|
||||
1. **rename-migration audit phải grep runtime SCHEMA/contract-description strings** (không chỉ code-path + prose) — path duplicate ở operative-var + comment + schema-desc; schema-desc copy dễ-sót-nhất (data≠code). [bắt được hmw.js:52 self-doc divergence]
|
||||
2. **ported-command §-anchor phải grep/ls-verify IN sister repo** (re-homed path khác AI_INFRA↔SE); §-cite = wiring-claim → grep-prove (như 'wire BE' bug áp cho governance-doc).
|
||||
3. **SE `runs/` ở `.claude/workflows/runs/` không phải repo-root** → bare `git ls-files runs` false-0 = FALSE not-tracked; path-qualify + exit-branch check-ignore (negation last-match-wins vô-hình với text-read).
|
||||
|
||||
## VERDICT: ✅ PASS sau-fix → REPORT (B3) AI_INFRA.
|
||||
19
.claude/workflows/runs/2026-06-18-harness-fix-review/run.md
Normal file
19
.claude/workflows/runs/2026-06-18-harness-fix-review/run.md
Normal file
@ -0,0 +1,19 @@
|
||||
# RUN — Harness 8/9/10 re-audit · STAGE 3 REVIEW (🆕 FLAT-trace)
|
||||
|
||||
- **run-id (folder):** 2026-06-18-harness-fix-review
|
||||
- **workflow run-id (evidence B3):** wf_d482e10d-5dd
|
||||
- **task:** B2 double-check ĐỘC LẬP — flat migration completeness + sleep-cmd port fidelity + containment/honesty; harvest **B2.5 know-how**.
|
||||
- **mandate:** Harness-9 B2 (REVIEW workflow TÁCH BIỆT khỏi IMPLEMENT `wf_ac43b5ff-7d1` — một-làm-một-soi, bắt lỗi trước commit).
|
||||
- **checkpoint:** APPROVED (HMW-ON).
|
||||
- **opened:** 2026-06-18 13:27 +07
|
||||
- **structure:** 🆕 FLAT.
|
||||
- **status:** OPEN → running
|
||||
|
||||
## Agents (3× reviewer adversarial, parallel)
|
||||
| # | reviewer | task |
|
||||
|---|---|---|
|
||||
| R1 | flat-completeness | residual `sub-md/`/`harvest/` scan 6 file + naming consistency hmw.js↔READMEs + `node --check` + C8 5-old-keep |
|
||||
| R2 | sleepcmd-detector | floor-intact vs AI_INFRA source + scope SE-only + G-009 clean + detector tailored-out reasoning sound |
|
||||
| R3 | containment-honesty | 0 prod code + investigator-MEMORY pre-existing? + B3 imprecision re-verify + ledger 2-beat + 2 new-runs flat |
|
||||
|
||||
## Output (FLAT) → `review-synthesis.md` (gom) + `sub-review-r{1,2,3}.md` (raw)
|
||||
@ -0,0 +1,7 @@
|
||||
# sub-review-r1 — flat-completeness (reviewer · wf_d482e10d-5dd)
|
||||
|
||||
**VERDICT: PASS-with-concerns**
|
||||
- Migration COMPLETE + CONSISTENT. `node --check` PARSE-OK. FLAT naming byte-identical: hmw.js:103 ↔ runs/README:16-18 ↔ workflows/README:16-17. 5 old S71 folders INTACT (not rewritten). C8 dual-accept documented (runs/README · workflows/README:19 · session-end:51 · _ledger:6). session-start:71 · session-end:32/51 · agents/README:112 updated flat.
|
||||
- **MINOR (FIXED):** hmw.js:52 SCHEMA `subMdPath` desc still `sub-md/<role>-<i>.md` (stale vs :103 flat) → em-main fixed `→ sub-<role>-<i>.md`.
|
||||
- MINOR: 3 new flat folders untracked → git add at commit (eligible-vs-committed).
|
||||
- **B2.5:** rename-audit phải grep runtime SCHEMA/contract-description strings, KHÔNG chỉ code-path + prose; path duplicate ở (1) operative var, (2) comment, (3) schema-desc — schema-desc copy dễ-sót-nhất (data≠code).
|
||||
@ -0,0 +1,8 @@
|
||||
# sub-review-r2 — sleepcmd-detector (reviewer · wf_d482e10d-5dd)
|
||||
|
||||
**VERDICT: PASS-with-concerns**
|
||||
- sleep-cmd port: ALL floor INTACT (none thinned/weakened — verified line-by-line vs source). Scope SE-only + provenance accurate. `last_sleep_at` path SE-correct. G-009 CLEAN. double-distill grounded (investigator gist IS gen:2). 4/9 gist-bearing matches disk. **Detector tailored-out SOUND + HONEST** (hmw.js header states not-node-runnable → AI_INFRA CLI-launcher-bypass threat genuinely N/A; 3 refine functions each N/A-reasoned; no-overclaim caveat + re-visit condition documented; does NOT hand-wave).
|
||||
- **MAJOR (FIXED):** Auto-check ≥7d trigger UN-WIRED — session-start/end never read `last_sleep_at` = overclaim → em-main WIRED for real: budget.json `+last_sleep_at:null` + session-start:78 + session-end:48 INFORM steps; grep now matches (was zero).
|
||||
- **MINOR (FIXED):** provenance design-doc cite read SE-local but AI_INFRA-only → qualified absolute AI_INFRA path.
|
||||
- **MINOR (FIXED):** charter cites prose vs source line-anchor → added substring anchor + fixed "cam"→"cấm" typo.
|
||||
- **B2.5:** ported-command integration anchors (§-cite) phải grep/ls-verify IN SE repo (re-homed path khác AI_INFRA); §-cite = wiring-claim → grep-prove như 'wire BE' bug (claim-vs-grep áp cho governance-doc wiring).
|
||||
@ -0,0 +1,9 @@
|
||||
# sub-review-r3 — containment-honesty (reviewer · wf_d482e10d-5dd)
|
||||
|
||||
**VERDICT: PASS** (0 blocker)
|
||||
- **CONTAINMENT clean:** ZERO production code (no src/Backend, fe-*, tests/, .csproj, Migrations/). Changed = 8 `.claude` governance/workflow/docs + investigator MEMORY + 3 untracked (1 cmd + 2 run-folders). No scope drift.
|
||||
- **investigator MEMORY `M` corrected:** = THIS session's S71/S72 audit-trail (5 entries: subfolder-not-flat · detector-MISSING · hmw-flat-gap · path-trap-runs-folder · refsweep), zero code, await session-end flush = correct (NOT unrelated pre-existing race).
|
||||
- **B3 path-trap ACCURATE:** `git ls-files runs`(bare)=0 vs `.claude/workflows/runs`=22; old "14" real at 8c47bd0 (`ls-tree`=14)→22 now (h910 + 2 new runs). "14(cũ)→22(thật) + bare-path trap" precise, not fabrication.
|
||||
- Ledger 7 rows all CLOSE-beat, 0 orphan, FLAT/C8 note present (_ledger:6). 2 new runs FLAT (no sub-md/harvest subdir). hmw.js migrated flat + PARSE-OK. detector honest. sleep-cmd SE-tailored.
|
||||
- **MINOR (FIXED):** runs/README:31 dogfood example named `sub-audit-*.md` that don't exist (Part A fail→self-gate) → reworded (audit-run = run.md+synthesis only; implement-run = full raw set).
|
||||
- **B2.5:** SE `runs/` at `.claude/workflows/runs/` NOT repo-root → bare `git ls-files runs` falsely 0 = FALSE not-tracked conclusion; ALWAYS path-qualify + exit-branch check-ignore (negation `!.claude/**` last-match-wins invisible to text-read of .gitignore).
|
||||
@ -0,0 +1,47 @@
|
||||
# REVIEW SYNTHESIS — Mig 54 PE giá-đề-xuất + CCM duyệt-done (commit 1d86abc)
|
||||
|
||||
> Em-main @P3 single-writer. **Verdict tổng: KHÔNG BLOCKER — go-live thứ Hai OK với 2 UAT-note.** Trung thực: chỉ 1/4 lane review độc-lập trả kết quả; 3/4 = em-main self-gate (đánh dấu rõ).
|
||||
|
||||
## A. Lane review ĐỘC-LẬP (be-logic) — ✅ PASS, verify-chéo
|
||||
| # | Finding | Severity (sau verify) | Kết luận |
|
||||
|---|---|---|---|
|
||||
| 1 | V1 legacy (`ApproveV1LegacyAsync:992`) set DaDuyet KHÔNG bind giá chốt | **not-an-issue** | BY DESIGN — V1 = phiếu legacy (Mig 21), 5 cột giá nullable=null; `ApprovedPrice*` KHÔNG feed `CreateContractFromEvaluation.GiaTri` (giá HĐ = SUM `Details.ThanhTienNganSach`, grep-confirmed). Dev DB: 4 phiếu V1, 0 ở ChoDuyet → edge không có data sống. Không vỡ NOT-NULL. |
|
||||
| 2 | Stray `fe-user/.claude/agent-memory/implementer-frontend/MEMORY.md` untracked | **nit** | cwd-misland (gotcha S72). Commit `1d86abc` verify SẠCH (`git show --name-only` 0 path `.claude`). Action: reconcile→canonical + KHÔNG `git add -A`. |
|
||||
|
||||
→ be-logic xác nhận **lõi backend (③ opt-in + ① bind giá) đúng**, không lỗ tài chính.
|
||||
|
||||
## B. 3 lane FAILED (no StructuredOutput) → EM-MAIN SELF-GATE
|
||||
> Honest: đây KHÔNG phải review độc-lập. Tự gác dựa trên test/build/cicd + code em viết.
|
||||
|
||||
### B1. authz-security — self-gate PASS
|
||||
- 2 setter `PeSuggestedPriceFeatures`: `ForbiddenException` TRƯỚC mọi side-effect (trước `SaveChanges`), role đúng (Pro=Procurement / Ccm=CostControl / Admin cả 2). **Bằng chứng độc-lập:** `PeSuggestedPriceSetterAuthzTests` (13 test) PASS.
|
||||
- CCM bypass CEO (③): ApproveV2 fail-closed — ngưỡng null→Conflict · không CostControl→Forbidden · gói≥ngưỡng→Conflict. **Bằng chứng:** `PeCcmThresholdFinalizeTests` cases (b)(c)(d) PASS.
|
||||
|
||||
### B2. cross-stack-wire — self-gate PASS + ⚠️ 1 UAT-note (RỦI RO #1)
|
||||
- **7-layer thread:** build slnx GREEN = không drop lớp nào (compile-proof). Field camelCase FE↔BE khớp.
|
||||
- **FE "currentIsFinalApprover" (rủi ro #1 em lo nhất):** `approvalFlow.steps[cuối].levels[cuối].status==='Current'`. Trace tay:
|
||||
- **Bình thường:** người duyệt cuối → level cuối status 'Current' → bộ chọn HIỆN, có ≥ option NCC (winner đã chọn → winnerQuoteTotal>0). **OK.**
|
||||
- **⚠️ EDGE (UAT-note):** nếu phiếu tới duyệt cuối mà **KHÔNG có giá nào** (chưa chọn NCC winner + chưa nhập PRO/CCM) → `priceCandidates` rỗng → hiện "Chưa có giá nào để chọn", **nút Xác nhận KHÔNG bị disable** (`priceMissing` cần `length>0`) → bấm → BE Conflict "Chọn 1 giá chốt". **KHÔNG deadlock cứng** (sửa được: chọn NCC winner / nhập giá) nhưng **UX khó hiểu**. Xác suất thấp (duyệt cuối thường đã có winner). **Đề xuất fix nhẹ:** disable Xác nhận + đổi message khi `shouldPickPrice && candidates rỗng`.
|
||||
- "Giấu nhầm bộ chọn với người duyệt cuối thật" → chỉ xảy ra nếu BE flow-status sai (bug có sẵn, KHÔNG do Mig 54). BE enforce price ở terminal = lưới chặn cuối.
|
||||
|
||||
### B3. regression-edge — self-gate PASS
|
||||
- **Mig 54 additive-nullable:** cicd Run #313 CONFIRMED — 5 cột applied prod, `sys.tables`=88 (0 bảng mới), `Down()` reversible. 0 backfill/lock.
|
||||
- **AUTO→OPT-IN:** phiếu in-flight áp hành vi mới ở lần duyệt kế (đúng chủ đích anh chọn). V1 không ảnh hưởng (be-logic confirm).
|
||||
- **DTO positional order:** 7 field mới + 7 arg đúng thứ tự — build GREEN proof.
|
||||
|
||||
## C. UAT-notes mang sang thứ Hai (KHÔNG block, cần mắt người)
|
||||
1. **Empty-candidates UX** (B2 edge) — em có thể fix nhẹ FE trước go-live nếu anh muốn.
|
||||
2. **Giả định "CCM ngay trước CEO"** — nếu Workflow Designer đặt CCM KHÔNG sát CEO, tích "duyệt done" sẽ bỏ qua cấp giữa. Anh Kiệt xác nhận cấu trúc khi set ngưỡng.
|
||||
|
||||
## D. Deploy (cicd Run #313) — ✅ verified
|
||||
Mig 54 applied (5 cột decimal(18,2)/nvarchar) · bundle admin `OlNyG9OD` / user `DSzSLVtL` · smoke 200/401 · `sys.tables`=88. Deploy mechanics sạch (KHÔNG = logic-correct, đó là phần trên + UAT).
|
||||
|
||||
## E. Round 2 — Double-check (free-text Workflow `wf_f885d9ef`, reliable) + verify fix
|
||||
> Chạy lại bằng **free-text (KHÔNG ép-schema)** sau khi thêm fix empty-candidates (anh yêu cầu "workflow double check"). **2/3 lane PASS, 0 issue; 1 lane (regression) no-return — nội dung đã che bởi test/build/cicd + lane khác.** Free-text đáng tin hơn schema-force (Round 1 chỉ 1/4).
|
||||
|
||||
- **authz-security = PASS độc-lập:** 2 setter Forbidden-TRƯỚC-SaveChanges + role đúng · CCM finalize chặn **3 cổng orthogonal** (approver-match + threshold/role/strict-`<` + `return` no-fall-through); `winnerQuoteTotal` server-recompute (KHÔNG tin client). 0 issue.
|
||||
- **cross-stack-wire + fix = PASS độc-lập (kỹ):** 7-layer thread KHÔNG drop (trace từng boundary) · FE camelCase khớp · `currentIsFinalApprover` ĐÚNG (BE `OrderBy` cả 2 trục steps+levels; OR-of-N gated bởi `blockedByV2Level`).
|
||||
- **🎯 RỦI RO #1 ĐÓNG DỨT ĐIỂM:** empty-candidates edge **thực tế UNREACHABLE** — submit-guard `PurchaseEvaluationWorkflowService.cs:194` chặn gửi-duyệt khi `winnerQuoteTotal<=0` ("Đơn vị được chọn chưa có giá chào thầu") → mọi phiếu ở ChoDuyet đã có giá NCC>0 → `priceCandidates≥1`. Fix `length===0` = phòng-thủ thuần + sửa luôn mâu thuẫn UX cũ (nút mở trong khi message báo trống). KHÔNG deadlock mới (escape hatch: setter không phase-gate → nhập giá bất kỳ lúc nào).
|
||||
- **regression-edge = no-return** → che bởi: Mig nullable + V1-untouched + DTO order (cross-stack lane confirm) · cicd Run #313 (Mig applied) · 334 test (spec opt-in).
|
||||
|
||||
**Verdict Round 2: SẠCH — fix an toàn commit, rủi ro #1 đóng (unreachable). 2 workflow review (4+3 agent) + em-main self-gate → đủ tự tin go-live thứ Hai.**
|
||||
24
.claude/workflows/runs/2026-06-18-mig54-pe-review/run.md
Normal file
24
.claude/workflows/runs/2026-06-18-mig54-pe-review/run.md
Normal file
@ -0,0 +1,24 @@
|
||||
# RUN — Mig 54 PE giá-đề-xuất + CCM duyệt-done · ADVERSARIAL REVIEW (custom inline)
|
||||
|
||||
- **run-id (folder):** 2026-06-18-mig54-pe-review
|
||||
- **workflow run-id (evidence):** wf_8c979a93-1a4
|
||||
- **type:** ⚠️ **CUSTOM INLINE Workflow** (KHÔNG dùng `hmw.js` RUN-TRACE) — agents trả **structured schema data**, KHÔNG ghi `sub-*.md`. Em-main viết `run.md` + `review-synthesis.md` POST-HOC (anh chốt "custom OK, miễn ghi MD"). Vì author inline nên KHÔNG có run-trace tự-scaffold @P1 — đây là bù vết kiểm sau.
|
||||
- **checkpoint:** APPROVED (anh yêu cầu "workflow cho nhanh")
|
||||
- **opened:** 2026-06-18 ~15:55 +07 (post-hoc — workflow đã chạy xong khi ghi)
|
||||
- **closed:** 2026-06-18 ~16:05 +07
|
||||
- **target:** commit `1d86abc` (Mig 54) — pre-UAT financial review, go-live thứ Hai 22/06
|
||||
- **status:** CLOSED
|
||||
|
||||
## Agents (4× reviewer adversarial ∥, schema-forced) + verify-chéo
|
||||
| dim | lens | KẾT QUẢ |
|
||||
|---|---|---|
|
||||
| be-logic | ApproveV2 ③ opt-in + ① bind giá + V1/skipToFinal | ✅ **RETURNED** — PASS, 2 finding (đều **not-an-issue/nit** sau verify-chéo) |
|
||||
| authz-security | 2 setter fail-closed + CCM bypass CEO | ❌ **FAILED** — subagent KHÔNG gọi StructuredOutput (2 nudge) → 0 kết quả |
|
||||
| cross-stack-wire | 7-layer thread + FE "duyệt cuối" derive (**rủi ro #1**) | ❌ **FAILED** — no StructuredOutput → 0 kết quả |
|
||||
| regression-edge | AUTO→OPT-IN in-flight + Mig nullable + DTO order | ❌ **FAILED** — no StructuredOutput → 0 kết quả |
|
||||
|
||||
## Honest outcome
|
||||
- **3/4 lane FAILED** — đúng flakiness đã ghi `feedback_workflow_fanout_reliability` (schema-forced agents hay không gọi StructuredOutput trong harness này). Tổng 10 agent / 966K token / 198 tool-use, nhưng chỉ `be-logic` trả structured.
|
||||
- **be-logic = PASS, 0 blocker / 0 major.** 2 finding verify-chéo: (1) V1 legacy không bind giá = **BY DESIGN** (nullable, KHÔNG feed `CreateContractFromEvaluation.GiaTri` — giá HĐ = SUM Details.ThanhTienNganSach) · (2) stray `fe-user/.claude/` = **nit** (commit `1d86abc` đã verify SẠCH).
|
||||
- **3 lane CHƯA review độc-lập** → em-main **self-gate** (xem `review-synthesis.md` §B). Verdict tổng: **không blocker; 1 UAT-note (empty-candidates UX edge).**
|
||||
- **Bài học (ghi cho session sau):** review fan-out NÊN dùng free-text return (Agent-tool) thay schema-forced Workflow — schema-force = nguyên nhân 3/4 fail. HOẶC dùng `hmw.js` RUN-TRACE (sub ghi file, không phụ thuộc StructuredOutput).
|
||||
137
.claude/workflows/runs/README.md
Normal file
137
.claude/workflows/runs/README.md
Normal file
@ -0,0 +1,137 @@
|
||||
# `runs/` — Run-trace convention (Harness-10 · cấu trúc **PHẲNG**)
|
||||
|
||||
> **Mục đích:** mỗi workflow fan-out (RUN-TRACE mode) ghi lại 1 dấu-vết hoàn-chỉnh trong `runs/<run-id>/` git **TRACKED** — plan + per-sub detail + bản-gom — cộng sổ `_ledger.md` 2-nhịp. TRACKED nghĩa là mọi write trong run-folder hiện trong `git diff` → **audit trực-tiếp**, không cần tin lời agent return. Adopt AI_INFRA Harness-10 (anh 06-18) + DELTA `h10-flat-detector-refine` (06-18). Convention cha: `../README.md`.
|
||||
>
|
||||
> 🆕 **REFINE 06-18 — cấu trúc PHẲNG.** Trước đây mỗi run-folder có **2 thư mục con** (`sub-md/` + `harvest/`). NAY mọi vết tích nằm **phẳng cùng MỘT cấp** trong `runs/<run-id>/`; phân biệt **bản-thô (raw, per-sub)** vs **bản-đã-soát (verified, gom)** bằng **TÊN file**, KHÔNG bằng thư mục con. Lý do: engine (`hmw.js` JS-sandbox no-fs) chỉ tạo artifact ở **một cấp** một cách tự nhiên — bỏ thư-mục-con sâu thì xoá luôn **một điểm-hay-quên** ngay tại gốc (không còn thư-mục-con-để-quên-dựng).
|
||||
|
||||
**run-id** = `YYYY-MM-DD-h<NN>-<slug>` (vd `2026-06-18-harness-audit-invest`). Nhiều run cùng ngày → slug phân-biệt stage (`-invest` / `-implement` / `-review`).
|
||||
|
||||
---
|
||||
|
||||
## C1 — Cấu trúc PHẲNG (mỗi `runs/<run-id>/` — file cùng MỘT cấp)
|
||||
|
||||
```
|
||||
runs/<run-id>/
|
||||
├── run.md ← (1) PLAN — em main @P1: meta + mục-tiêu + agents-table + guards + status OPEN→CLOSE
|
||||
├── sub-<role>-<i>.md ← (2) PER-SUB raw — 1 file/sub/turn, prefix `sub-` (vd sub-investigator-codebase-0.md)
|
||||
├── sub-<role>-<i>.md (fan-out cùng-role → đánh số -0 / -1 …)
|
||||
└── <stage>-synthesis.md ← (3) GOM verified — em main: bản-gom 1 turn, suffix `-synthesis.md` (vd invest-synthesis.md)
|
||||
```
|
||||
|
||||
**Phân-biệt raw vs verified bằng TÊN, KHÔNG bằng subfolder:**
|
||||
|
||||
| Loại | Quy-ước TÊN | Ai ghi | Nội-dung |
|
||||
|---|---|---|---|
|
||||
| **PLAN** | `run.md` (cố định) | em main @P1 | nguồn-sự-thật của run: workflow run-id (evidence B3 `wf_…`), adap/mandate, checkpoint, opened, input-spec (nếu nối stage trước), agents-table (`# · role · task`), guards áp cho sub, output-path, status OPEN→CLOSE. |
|
||||
| **PER-SUB (raw)** | **prefix `sub-`** → `sub-<role>-<i>.md` | write-sub tự ghi @P2 · read-only sub → em main scribe @P3 | full working detail 1 sub. **1 sub-MD / role / turn** (cùng-role → `-0`/`-1`). Read-only sub (CHỈ Bash) KHÔNG Bash-write MD (mojibake) → trả `findings`+`subMdPath`, **em main single-writer**. |
|
||||
| **GOM (verified)** | **suffix `-synthesis.md`** → `<stage>-synthesis.md` | em main per-turn (C4) | gom `sub-*`+findings → 5-trục integrity → build-spec/synthesis. |
|
||||
|
||||
> 🟢 **Quy-tắc nhận-diện:** file bắt đầu bằng `sub-` = **bản-thô** (per-sub); file kết bằng `-synthesis.md` = **bản-đã-soát** (gom). `run.md` = plan. Không cần mở file/không cần subfolder để biết loại — đọc TÊN là đủ. (Khớp `../hmw.js` inject path: engine ghi-direct `runs/<run-id>/sub-<role>-<i>.md`, KHÔNG `sub-md/…`.)
|
||||
>
|
||||
> **Live dogfood:** `2026-06-18-harness-audit-invest/` = `run.md` + `audit-synthesis.md` **CHỈ** (KHÔNG subfolder, KHÔNG raw `sub-audit-*.md`) — vì 1 auditor fail-no-StructuredOutput + 2 truncated → em main self-gate ground-truth từ đĩa, không có raw-sub để scribe (xem `audit-synthesis.md:4`). Ví-dụ tên `sub-<role>-<i>.md` là minh-hoạ QUY-TẮC-ĐẶT-TÊN, KHÔNG phải artifact bắt-buộc mỗi run. Run `2026-06-18-harness-fix-implement/` mới có đủ raw (`sub-impl-sleepcmd.md` + `sub-impl-runsreadme.md` + `implement-synthesis.md`) = full flat set điển-hình.
|
||||
|
||||
---
|
||||
|
||||
## C2 — Scaffold `run.md` ở OPEN (em main @P1, TRƯỚC khi invoke Workflow)
|
||||
`hmw.js` chạy JS-sandbox **no-filesystem** → KHÔNG tự tạo folder/file. Em main PHẢI Write @P1:
|
||||
1. **Tạo run-folder + PLAN:** `runs/<run-id>/run.md` (điền plan đầy-đủ). 🆕 **KHÔNG cần `mkdir sub-md/` / `harvest/` / `.gitkeep`** — file `sub-*` và `*-synthesis.md` sẽ sinh ra **phẳng cùng cấp** trong cùng folder đó (engine + em main ghi 1 cấp).
|
||||
2. **Ghi OPEN-beat** vào `runs/_ledger.md` (1 dòng, `closed=⏳`).
|
||||
3. **(nối stage)** trỏ `input spec:` trong `run.md` sang `<stage>-synthesis.md` của run trước (vd implement đọc `invest-synthesis.md`).
|
||||
|
||||
> 🔴 **C2 là fragile-point.** Quên scaffold = run chạy nhưng KHÔNG có dấu-vết = **lỗ-hổng âm-thầm** (không lỗi, không cảnh-báo — chỉ thiếu file). 3-layer (C5) là lưới giảm-thiểu, KHÔNG khoá-cứng. Xem C7. 🆕 Cấu trúc phẳng bỏ **1 trong 2** điểm-hay-quên (không còn subfolder-to-forget); dư-lượng còn lại = **vẫn phải tự ghi `<stage>-synthesis.md` mỗi run** (close-gate C5/L3 bắt nếu thiếu).
|
||||
|
||||
---
|
||||
|
||||
## C3 — Git-tracked: VERIFY **2 nấc tách-bạch** (chỗ hay nhầm)
|
||||
Run-folder PHẢI git **TRACKED** (re-include qua `.gitignore:83 !.claude/**`, last-match-wins). Verify ĐỦ **2 nấc** — đừng dừng ở nấc 1:
|
||||
|
||||
```bash
|
||||
# Nấc (i) — GỠ-KHỎI-IGNORE (tracked-eligible): exit != 0 = KHÔNG bị ignore
|
||||
git check-ignore .claude/workflows/runs ; echo "exit=$?" # SE: exit=1 ✓ (không ignore)
|
||||
|
||||
# Nấc (ii) — THẬT-SỰ-COMMIT: liệt-kê ra file = đã `git add`/commit
|
||||
git ls-files .claude/workflows/runs | wc -l # SE: 22 ✓ (non-empty = đã commit)
|
||||
```
|
||||
|
||||
- **Nấc (i) `check-ignore` exit != 0** = thư-mục KHÔNG nằm trong ignore (tracked-**eligible**). ⚠️ Mới là *đủ-điều-kiện-track*, **CHƯA chắc đã commit**.
|
||||
- **Nấc (ii) `ls-files` non-empty** = thật-sự có file trong index (đã `git add`/commit). 🔴 **eligible ≠ committed:** một folder `check-ignore` exit!=0 NHƯNG `ls-files` rỗng = **chưa hề commit** (bằng-chứng sống tại AI_INFRA chính trường-hợp này). PHẢI chạy CẢ 2 lệnh.
|
||||
- 🔴 **ĐÚNG PATH mới đáng tin.** Path đúng = **`.claude/workflows/runs`** (đầy-đủ từ repo-root). Bare `runs/` (tên trần) là path SAI — ở repo khác, `git check-ignore runs` từ repo-root có thể trả **exit 0** (match nhầm rule khác / không match negation `.claude/**`) → **đọc sai thành "bị ignore"**. *(Quan-sát SE: cả `runs` lẫn `.claude/workflows/runs` đều trả exit=1 vì .gitignore SE không có rule `runs` trần — nhưng ĐỪNG dựa may-rủi đó: luôn dùng path đầy-đủ `.claude/workflows/runs`.)*
|
||||
- **Bẫy exit-code khi dùng `-v`:** `git check-ignore -v <path>` trả exit 0 cho CẢ ignore lẫn negation (`!.claude/**`) → đừng đọc exit của `-v` để kết-luận; dùng dạng không-`-v` ở trên (exit!=0 = không ignore) hoặc `&& echo IGNORED || echo NOT`.
|
||||
|
||||
---
|
||||
|
||||
## C4 — Per-turn primary (gom NGAY, không đợi session-end)
|
||||
Gom là **việc của turn**, không defer. Sau MỖI fan-out turn → em main đọc `sub-*`+findings → ghi `<stage>-synthesis.md` **liền trong turn đó** (chính file synthesis này là bằng-chứng). Lợi: detail tươi, không mất qua nén-context; session-end chỉ VERIFY (không tái-tạo). **Đây là đường CHÍNH (primary)**; gom toàn-bộ @session-end (L3) chỉ là **lưới an-toàn (safety-net)**, KHÔNG phải đường chính.
|
||||
|
||||
---
|
||||
|
||||
## C5 — 3-layer anti-miss (lưới chống bỏ-sót — KHÔNG khoá-cứng-cùng-lúc-fire)
|
||||
| Layer | Khi | Làm gì |
|
||||
|---|---|---|
|
||||
| **L1 in-run ledger-check** | em-main @P1, lúc mở run mới (TRƯỚC scaffold) | đọc `_ledger.md`: nếu run TRƯỚC còn `closed=⏳` (OPEN-beat chưa CLOSE / thiếu `<stage>-synthesis.md`) → gom + CLOSE nó TRƯỚC khi mở run mới. *(Engine no-fs → KHÔNG đọc được ledger → L1 là convention **EM-MAIN**, KHÔNG phải `hmw.js` prompt. hmw.js chỉ emit C4 per-turn return-instruction cho sub tại writeGuard.)* |
|
||||
| **L2 session-start orphan-scan** | đầu session (`session-start.md` §2.1.1 H2) | scan `runs/*/` tìm **orphan = OPEN-beat (ledger `closed=⏳`) mà KHÔNG có `<stage>-synthesis.md`** → báo + giải-quyết (C6). |
|
||||
| **L3 session-end close-gate** | cuối session (`session-end.md` §L.b(f) H2) | VERIFY per-turn gom đã xong cho mọi `runs/<id>/` (**idempotent — KHÔNG re-APPEND**, chống DUPLICATE-HARVEST) + 5-trục GATE backstop trước khi commit. 🆕 close-gate **chấp nhận CẢ 2 dạng** (old-subfolder + new-flat) trong giai-đoạn chuyển — xem C8. |
|
||||
|
||||
> 3 layer **độc-lập, fire ở 3 thời-điểm khác nhau** (run-open @P1 / session-start / session-end) — **KHÔNG layer nào do engine enforce** (hmw.js no-fs), cả 3 là convention em-main/H2. → giảm xác-suất sót, KHÔNG triệt-tiêu. Sót ở P1 (C2) cùng blind-spot với L1 (đều @P1) → chỉ bắt MUỘN ở L2 (session sau) / L3 (close-gate).
|
||||
|
||||
---
|
||||
|
||||
## C6 — Ledger 2-nhịp + orphan resolution (`_ledger.md`)
|
||||
Sổ tất-cả run, 1 bảng. Mỗi run **2 lần ghi (2-nhịp)**:
|
||||
- **OPEN-beat** (@P1, lúc scaffold): thêm dòng `| <run-id> | <workflow> | <opened> | ⏳ | <agents> | ⏳ | <stage>-synthesis.md |`.
|
||||
- **CLOSE-beat** (lúc đóng run): điền `closed` timestamp + `verdict` (PASS/FAIL + 1 dòng + `wf_…`) + `harvest` (path file synthesis ✓).
|
||||
|
||||
`closed=⏳` = đang chạy (OPEN chưa CLOSE).
|
||||
|
||||
**Orphan** = dòng ledger `closed=⏳` nhưng run thực-tế đã xong/bỏ (session bị kill, agent die-0-byte, quên CLOSE-beat). **Giải-quyết-CỨNG** (không để treo):
|
||||
1. **Điều-tra đĩa THẬT:** `run.md` status + có file `sub-*`? + có `<stage>-synthesis.md`? + git-log workflow `wf_…`.
|
||||
2. **Đóng tay** nếu run thật-sự xong: điền CLOSE-beat (timestamp + verdict + harvest path).
|
||||
3. **Đánh-dấu aborted** nếu run bỏ-dở: verdict = `⚠️ ABORTED — <lý-do>`, ghi rõ phần nào hoàn-thành (recover qua git/disk/prod truth, **KHÔNG tin agent return-message**).
|
||||
|
||||
---
|
||||
|
||||
## C7 — 🔴 CAVEAT trung-thực (no-overclaim)
|
||||
- **Engine no-fs → scaffold KHÔNG tự-động.** `hmw.js` (JS-sandbox no-filesystem) KHÔNG tạo được folder/file. Run-trace dựa **100% vào em-main**: scaffold `run.md` @P1 (C2) + tự ghi `<stage>-synthesis.md` mỗi run (C4). Không có cơ-chế nào ép tạo file. **NOT fully-autonomous** — đây là **hard-gate-trên-INPUT** (em main tự dựng khung + tự bơm tham-số đầu/cuối mỗi run), KHÔNG phải engine tự-động.
|
||||
- **Cấu trúc phẳng giảm fragile từ 2 → 1, KHÔNG triệt-tiêu.** Bỏ subfolder xoá được điểm-hay-quên "dựng sẵn `sub-md/`+`harvest/`" (engine giờ ghi 1 cấp, **no subfolder-to-forget**). NHƯNG còn **dư-lượng**: em-main VẪN phải nhớ ghi `<stage>-synthesis.md` mỗi run (close-gate L3 bắt nếu thiếu). Điểm-yếu **giảm 1-trong-2**, chứ không biến mất → C2 vẫn bắt-buộc.
|
||||
- **3-layer = LƯỚI, KHÔNG khoá-cứng-cùng-lúc-fire.** L1/L2/L3 bắt lỗi **sau khi** việc đã xảy ra (post-hoc) hoặc nhắc **dựa-trên-input em-main-bơm** — giảm xác-suất sót, KHÔNG chặn tại-đúng-thời-điểm-fire. Phòng-thủ-nhiều-lớp, KHÔNG bảo-đảm tuyệt-đối.
|
||||
- **G-015 no-overclaim — TRACKED ≠ read-only-ENFORCED.** Run-folder git-tracked KHÔNG biến sub thành read-only: sub **vẫn giữ Bash** (write-channel mở — ghi-ngoài-repo git-diff mù / curl Qdrant). Containment THẬT (model dưới) = em-main single-writer + git-diff(in-repo) + chunk-count(RAG), defense-in-depth — KHÔNG sandbox cứng, KHÔNG claim "ENFORCED".
|
||||
|
||||
---
|
||||
|
||||
## C8 — MIGRATION (run cũ giữ subfolder · close-gate chấp-nhận CẢ 2 dạng)
|
||||
🔴 **KHÔNG viết lại lịch-sử.** 5 run-folder S71 đã chốt giữ **NGUYÊN cấu trúc cũ** `sub-md/` + `harvest/` (đã commit, KHÔNG đụng):
|
||||
```
|
||||
2026-06-18-h10-invest/ (run.md + sub-md/ + harvest/invest-synthesis.md) ← cũ, GIỮ
|
||||
2026-06-18-h10-implement/ (run.md + sub-md/ + harvest/implement-synthesis.md) ← cũ, GIỮ
|
||||
2026-06-18-h10-review/ (run.md + sub-md/ + harvest/review-synthesis.md) ← cũ, GIỮ
|
||||
2026-06-18-h910-finalize/ (run.md + sub-md/ + harvest/finalize-synthesis.md) ← cũ, GIỮ
|
||||
2026-06-18-h910-curate/ (run.md + sub-md/ + harvest/curate-synthesis.md) ← cũ, GIỮ
|
||||
2026-06-18-harness-audit-invest/ (run.md + audit-synthesis.md) ← 🆕 FLAT
|
||||
```
|
||||
- **Run MỚI từ refine 06-18 → dùng FLAT** (`sub-<role>-<i>.md` + `<stage>-synthesis.md` cùng cấp). Run CŨ → để yên (đừng gom-lên-cấp/đừng xoá subfolder — re-writing history vô-ích + làm bẩn git-log).
|
||||
- 🟢 **Close-gate (L3) ACCEPT BOTH** suốt giai-đoạn chuyển: verify gom ở **`harvest/<stage>-synthesis.md`** (dạng cũ) **HOẶC** `<stage>-synthesis.md` phẳng (dạng mới). Không fail run cũ chỉ vì thiếu file-phẳng.
|
||||
- **2 nấc track (C3)** áp cho CẢ 2 dạng: `check-ignore` exit!=0 + `ls-files` non-empty.
|
||||
|
||||
---
|
||||
|
||||
## Containment model (PHẢI khớp `_ledger.md` + `../hmw.js` — đồng-bộ 3 chỗ: đây · `_ledger.md` · `../hmw.js`)
|
||||
> Run-folder `runs/<run-id>/` được git **TRACKED** → mọi write **HIỆN** trong git-diff = **audit trực-tiếp**. Containment: tracked-change **NGOÀI** `runs/<run-id>/` **VÀ NGOÀI** code-disjoint đã giao = **vi-phạm** (thay model Harness-2 B6 "mọi tracked-change = vi-phạm"). G-015 no-overclaim: TRACKED ≠ read-only-enforced — sub vẫn giữ Bash (write-channel mở), containment THẬT = em-main single-writer + git-diff(in-repo) + chunk-count(RAG).
|
||||
|
||||
---
|
||||
|
||||
## Anti-bypass detector — **SE TAILORED-OUT** (quyết-định có chủ-đích, anh chốt)
|
||||
> Refine 06-18 (b) đề-xuất làm-chặt một **bộ-dò chống-lách-engine** (soi nhật-ký/mã xem có run nào né engine chuẩn không) theo **3 chức-năng**. **SE quyết-định KHÔNG hiện-thực bộ-dò này** — đã CÂN-NHẮC và thấy **N/A cho threat-model của SE.** Giữ trung-thực, KHÔNG overclaim "đã có anti-bypass detector".
|
||||
|
||||
**Lý-do tailored-out:**
|
||||
- **SE chạy workflow qua Anthropic Workflow tool (`hmw.js` invoke), KHÔNG có bề-mặt-lách kiểu CLI-launcher.** AI_INFRA threat-model giả-định một node-CLI launcher (đường-vòng chạy lén engine) cần bộ-dò canh. SE **không có launcher như vậy** → không có "đường-vòng" để dò.
|
||||
- **Containment SE đã đủ bằng cơ-chế khác (G-015 defense-in-depth):** `git diff` commit-gate (mọi write tracked HIỆN trong diff) + run-folder git-tracked (audit trực-tiếp) + `_ledger.md` orphan-scan (L2/L3) — backstop THẬT (không phải prompt). Bộ-dò pattern-match thêm = trùng-lặp, không bịt lỗ mới.
|
||||
|
||||
**3 chức-năng refine — CÂN-NHẮC rồi N/A cho SE:**
|
||||
|
||||
| Chức-năng refine (b) | Áp SE? | Vì sao |
|
||||
|---|---|---|
|
||||
| **(1) Whitelist launcher hợp-lệ** | N/A | SE không có launcher-wrapper quanh engine → không có gì để whitelist. |
|
||||
| **(2) Khớp đa-dạng biến-thể path tới engine** | N/A | SE invoke engine qua Workflow tool (1 đường duy-nhất), không có path-variant để khớp. |
|
||||
| **(3) Neo KHOÁ-khởi-chạy-thật (đừng match tên-engine lẫn trong script) + acceptance theo QUAN-HỆ** | N/A | Không có log-scan detector → không có chỗ neo launch-key. Acceptance-by-relation (hợp-lệ ≠ vi-phạm, hợp-lệ tăng dần OK) là nguyên-tắc tốt nhưng chỉ áp được khi CÓ detector. |
|
||||
|
||||
> 🔴 **No-overclaim:** SE **KHÔNG** claim đã adopt anti-bypass detector. SE claim: đã **đọc + cân-nhắc** 3 chức-năng, **chọn tailored-out** vì threat-model SE không có CLI-launcher-bypass-surface; containment dựa **git-diff + run-folder-tracked + ledger-orphan-scan** (G-015). Nếu sau này SE thêm launcher-wrapper → re-visit quyết-định này.
|
||||
24
.claude/workflows/runs/_ledger.md
Normal file
24
.claude/workflows/runs/_ledger.md
Normal file
@ -0,0 +1,24 @@
|
||||
# Workflow Run Ledger — SOLUTION_ERP (Harness-10)
|
||||
|
||||
> **Two-beat (C6):** ghi nhịp **OPEN** lúc mở run + nhịp **CLOSE** lúc đóng run. **Orphan** = OPEN mà không CLOSE → phải giải-quyết-cứng (điều tra + đóng tay hoặc đánh dấu aborted).
|
||||
> **Tracked (C3):** thư mục `runs/<run-id>/` được git theo dõi (KHÔNG gitignore). Containment chuyển sang model **"tracked-change NGOÀI run-folder (+ code-disjoint đã giao) = vi phạm"** (thay model Harness-2 B6 "mọi tracked-change = vi phạm").
|
||||
> Cột `closed=⏳` = đang chạy (OPEN-beat). Điền timestamp + verdict khi đóng (CLOSE-beat).
|
||||
> 🆕 **FLAT (C1/C8, h10-refine 06-18):** run MỚI = file phẳng cùng cấp (`run.md` + `sub-<role>-<i>.md` + `<stage>-synthesis.md`, KHÔNG subfolder). **5 run cũ (`h10-invest`…`h910-curate`) GIỮ `sub-md/`+`harvest/`** — đừng rewrite history; close-gate **dual-accept** cả hai dạng.
|
||||
|
||||
| run-id | workflow | opened | closed | agents | verdict | harvest |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 2026-06-18-h10-invest | Harness-10 adap — INVESTIGATE | 2026-06-18 08:29 +07 | 2026-06-18 08:42 +07 | 4× investigator-codebase (read-only ∥) | ✅ PASS — B+C+D strong, A stub-fail (B covered hmw.js) · `wf_9c2cd2cd-2e7` | `harvest/invest-synthesis.md` ✓ |
|
||||
| 2026-06-18-h10-implement | Harness-10 adap — IMPLEMENT | 2026-06-18 08:42 +07 | 2026-06-18 08:52 +07 | 3× general-purpose (text file-disjoint ∥) + em-main single-writer (gitignore/hmw.js/READMEs cluster) | ✅ PASS — 3/3 agent DONE, containment CLEAN, wording đồng-bộ 4 file · `wf_e4e46725-231` | `harvest/implement-synthesis.md` ✓ |
|
||||
| 2026-06-18-h10-review | Harness-10 adap — REVIEW | 2026-06-18 08:52 +07 | 2026-06-18 09:01 +07 | 3× reviewer (adversarial ∥) | ✅ PASS sau-fix — R1 PASS · R2/R3 bắt C5 L1 over-claim (high-conf, đã fix path-a) · `wf_636bc95b-939` | `harvest/review-synthesis.md` ✓ |
|
||||
| 2026-06-18-h910-finalize | Harness-9+10+checklist — FINALIZE double-check (anh giao) | 2026-06-18 09:41 +07 | 2026-06-18 09:52 +07 | 3× reviewer (R1 Part A / R2 Part B+C / R3 cross-cutting) | ✅ GAPS-FOUND, 0 code-defect — R3 thorough (R1/R2 no-StructuredOutput → self-gate); 3 gap (G1 over-cap curate · G2 stale-claims · G3 H10-memory) + 1 minor · `wf_73de399d-753` | `harvest/finalize-synthesis.md` ✓ |
|
||||
| 2026-06-18-h910-curate | Harness-9 curate G1 — reviewer+investigator L1→L2 | 2026-06-18 09:52 +07 | 2026-06-18 10:05 +07 | 2× general-purpose (1/file, file-disjoint, NO same-file race) | ✅ PASS — reviewer 36.7→24.8KB (moved 10) + inv 29.8→23.2KB (moved 3), both <25600 cap; archive +N -0 (0-byte-loss, grep-Fxf 10/10 + md5sum verified); em-main +reviewer-gist gen:2 + budget re-measure · `wf_f32987b8-03f` | `harvest/curate-synthesis.md` |
|
||||
| 2026-06-18-harness-audit-invest | Harness 8/9/10 re-audit — INVESTIGATE (anh giao) · 🆕FLAT | 2026-06-18 11:09 +07 | 2026-06-18 11:21 +07 | 4× investigator-codebase (read-only ∥) | ✅ DONE — Part B structured; Part A failed-no-SO + C/H8 truncated → em-main self-gate ground-truth. Gaps: C1/C8/refine-a FLAT migration · A8 sleep-cmd · 2 broadcast pending · detector tailored-N/A · `wf_13868efb-ea7` | `audit-synthesis.md` ✓ (FLAT) |
|
||||
| 2026-06-18-harness-fix-implement | Harness 8/9/10 re-audit — IMPLEMENT (FLAT migration + sleep-cmd + checklist-v2) · 🆕FLAT | 2026-06-18 11:22 +07 | 2026-06-18 11:36 +07 | 2× general-purpose (file-disjoint ∥) + em-main cluster | ✅ DONE — sleep-cmd port + runs/README flat (agent) · hmw.js+workflows/README+agents/README+session-cmds+ledger flat + H4.5→H8 (em-main) · 5 old runs keep subfolder C8 · node --check OK · `wf_ac43b5ff-7d1` | `implement-synthesis.md` ✓ (FLAT) |
|
||||
| 2026-06-18-harness-fix-review | Harness 8/9/10 re-audit — REVIEW (B2 double-check) · 🆕FLAT | 2026-06-18 13:27 +07 | 2026-06-18 13:37 +07 | 3× reviewer (adversarial ∥) | ✅ PASS sau-fix — R3 PASS (containment clean, honesty strong) · R1/R2 PASS-w-concerns: 1 major (auto-wire overclaim) + 4 minor → TẤT CẢ FIXED em-main (hmw.js:52 schema · WIRE last_sleep_at session-start/end · provenance · charter-anchor · README:31); post-fix node --check OK + grep verified · `wf_d482e10d-5dd` | `review-synthesis.md` ✓ (FLAT) |
|
||||
| 2026-06-18-mig54-pe-review | Mig 54 PE giá-đề-xuất + CCM duyệt-done — ADVERSARIAL REVIEW (⚠️ **custom inline, KHÔNG hmw**) · FLAT | 2026-06-18 15:55 +07 | 2026-06-18 16:05 +07 | 4× reviewer (schema-forced ∥) + verify-chéo | ⚠️ **1/4 RETURNED** — be-logic PASS 0-blocker (V1-by-design not-an-issue + stray-nit, verify-chéo) · 3/4 lane FAILED no-StructuredOutput → em-main self-gate PASS (authz/wire/regression) + 1 UAT-note (empty-candidates UX edge) · `wf_8c979a93-1a4` | `review-synthesis.md` ✓ (FLAT) |
|
||||
| 2026-06-18-mig54-pe-review (R2) | Mig 54 — DOUBLE-CHECK free-text reliable + verify fix empty-candidates | 2026-06-18 16:10 +07 | 2026-06-18 16:18 +07 | 3× reviewer (free-text ∥) | ✅ **2/3 PASS 0-issue** (authz + cross-stack/fix độc-lập) · 🎯 rủi ro #1 ĐÓNG (empty-candidates UNREACHABLE per submit-guard `:194`) · 1 lane no-return (covered) · free-text > schema-force (1/4→2/3) · `wf_f885d9ef-5f6` | `review-synthesis.md` §E |
|
||||
| 2026-06-18-h11-audit | Harness-11 adap — AUDIT (PRESENT/PARTIAL/GAP vs SE-present) · 🆕FLAT | 2026-06-18 19:45 +07 | 2026-06-18 19:55 +07 | 4× investigator-codebase (read-only ∥, 1 PHẦN/lane) `wf_7fdc3bd5-930` | ✅ DONE — A 🟡 (A4/A5/A6 GAP hợp-lệ) · **B1+B3 GAP** (derived COPY, no freshness-detector) · **C1/C2/C3 GAP** (0 detector-script, chỉ agent-judgement) · **D5/D6/D7 PARTIAL + D8 GAP** (3-tier+1-direction chưa codify); D4/D9/D11 mechanized-mạnh sẵn. read-only→em-main scribe synthesis | `audit-synthesis.md` ✓ (sub read-only, findings-in-output) |
|
||||
| 2026-06-18-h11-implement | Harness-11 adap — IMPLEMENT (detector-script + A-gate ∥ sub · em-main MD cluster) · 🆕FLAT | 2026-06-18 19:56 +07 | 2026-06-18 20:15 +07 | 2× general-purpose (script file-disjoint ∥) + em-main single-writer (governance MD cluster) `wf_c5e5844e-7c1` | ✅ DONE — Lane1 `governance-detectors.ps1` runtime-proven (71→27 post-refinement R2; bắt drift thật + gotcha #30 mojibake fix) · Lane2 `memory-archive-gate.ps1` A4/A5/A6/A7 proven · em-main engine-doc + B1 ×11 (drift RESOLVED post-rerun) + cadence-wire D1/D2 + agents/README. B+C+D đủ-trọn (claim). Single-writer CLEAN. → REVIEW judge FP-rate | `implement-synthesis.md` ✓ (FLAT) |
|
||||
| 2026-06-18-h11-review | Harness-11 adap — REVIEW (B2 double-check, free-text) · 🆕FLAT | 2026-06-18 20:16 +07 | 2026-06-18 20:35 +07 | 3× reviewer (adversarial ∥: completeness-gate / detector-correctness / honesty-containment) `wf_d7ca1ff8-942` | ✅ **PASS** — R1 completeness-gate ĐẠT (B+C+D đủ-trọn) · R2 detector 6/6 correctness + 2 refinement (C2 context-skip + C1 normalize → 59→27 sharper) · R3 honesty+containment 0-blocker (1 nit fixed). NO-API+0-auto-write PASS. Single-writer CLEAN | `review-synthesis.md` ✓ (FLAT) |
|
||||
| 2026-06-18-h11-doublecheck | Harness-11 adap — DOUBLE-CHECK #1 (anh giao, post-commit `e70c046`) · 🆕FLAT | 2026-06-18 20:48 +07 | 2026-06-18 21:05 +07 | 3× reviewer (over-suppress-regression / committed-state / containment-runtrace) `wf_a0b68d2f-30e` | ✅ **PASS** — DA2+DA3 PASS (B1×11 exact · root:53 tail byte-identical · broadcasts hash recompute KHỚP · single-writer clean · over-suppress-hunt CLEAN). DA1 failed-no-SO → em-main self-gate fake-drift CAUGHT (no over-suppress, runtime). +C2 test-project skip (27→26) + agents/README pending→run-id. tree-skip reverted (gotcha #30 box-glyph) | `doublecheck-synthesis.md` ✓ (FLAT) |
|
||||
| 2026-06-18-h11-checklist-verify | Harness-11 adap — CHECKLIST formal self-verify (anh giao) · 🆕FLAT | 2026-06-18 21:08 +07 | 2026-06-18 21:22 +07 | 3× investigator-codebase (CL1 A+B / CL2 C / CL3 D, evidence-mapping ∥) `wf_39cd4cbe-f07` | ✅ **completeness-gate ĐẠT** — B 4/4 + C 5/5 + D 11/11 đủ-trọn (function-floor MET), A 🟡 tailored (A6 runtime cần 2×-Apply legit-by-design). D5/D6/D7 explicit-label=YES + D8 one-direction codify=YES. NO-API+0-auto-write 0-hit. All 3 lane returned | `checklist-verify-synthesis.md` ✓ (FLAT) |
|
||||
19
.gitignore
vendored
19
.gitignore
vendored
@ -86,10 +86,17 @@ src/Backend/SolutionErp.Api/wwwroot/exports/
|
||||
# Pattern AFTER !.claude/** so last-match wins (.claude/ itself not excluded → re-include valid).
|
||||
.claude/hmw-mode.on
|
||||
|
||||
# HMW wave-folder + agent-team — transient per-workflow detail (Harness 2 B6 isolation —
|
||||
# H2 harvest-curator gom rồi; gitignore để git-diff audit isolation SẠCH, 0 noise).
|
||||
# HMW run-trace folders — Harness-10 (2026-06-18): `.claude/workflows/runs/<run-id>/` is git-TRACKED
|
||||
# (run.md + sub-md/ + harvest/ + _ledger.md) for auditability. Stays tracked via the !.claude/** negation
|
||||
# above — do NOT add an ignore rule for runs/. Containment model shifts from Harness-2 B6 ("wave-*/ gitignored
|
||||
# → any tracked-change post-workflow = stray-write") to Harness-10 ("tracked-change OUTSIDE runs/<run-id>/ +
|
||||
# assigned code-disjoint = violation"). Run-trace now VISIBLE in git-diff = direct audit (stronger).
|
||||
#
|
||||
# Legacy Harness-2 wave-folder + agent-team — kept ignored (superseded by runs/; no wave-*/ remain; harmless).
|
||||
# Pattern AFTER !.claude/** so last-match wins (giống hmw-mode.on).
|
||||
# Verify: git check-ignore -v .claude/workflows/wave-x/wave.md
|
||||
# ⚠️ check-ignore verify (exit-code nuance): plain `git check-ignore X` exits 1 for a negation/re-included path
|
||||
# (NOT 0); only `git check-ignore -v --no-index` shows exit 0 for BOTH. Use the idiom for a correct verdict:
|
||||
# `git check-ignore X && echo IGNORED || echo NOT-IGNORED` → runs/ = NOT-IGNORED (tracked); wave-x/ = IGNORED.
|
||||
.claude/workflows/wave-*/
|
||||
.claude/agent-teams/
|
||||
|
||||
@ -98,3 +105,9 @@ src/Backend/SolutionErp.Api/wwwroot/exports/
|
||||
|
||||
# Sub-agent output dumps (JSON/HTML scratch)
|
||||
tmp/
|
||||
|
||||
# [S76] Guard cwd-misland (feedback_agent_cwd_relative_memory_misland): sub-agent `cd`
|
||||
# vào fe-user/fe-admin chạy npm build rồi ghi MEMORY.md relative-path → stray
|
||||
# `fe-*/.claude/agent-memory/...`. Canonical agent-memory ở ROOT `.claude/` vẫn tracked
|
||||
# (negation `!.claude/**` trên). Pattern AFTER negation → last-match-wins cho path FE-app.
|
||||
fe-*/.claude/
|
||||
|
||||
11
.ragignore
Normal file
11
.ragignore
Normal file
@ -0,0 +1,11 @@
|
||||
# .ragignore - Harness-9 defensive exclude (2026-06-17)
|
||||
#
|
||||
# Read-side design: inject the archive INDEX (the map), read content on-demand.
|
||||
# Keep archive _INDEX.md + *.gist.md OUT of the RAG corpus so they do not
|
||||
# duplicate/noise against the verbatim records they point to.
|
||||
#
|
||||
# NOTE: SE's corpus glob currently matches MEMORY.md only (which is exactly WHY
|
||||
# the archive tier went dark). This guard is forward-looking — it only bites if
|
||||
# AI_INFRA later broadens the corpus glob. Re-index itself is an AI_INFRA op.
|
||||
**/agent-memory/**/_INDEX.md
|
||||
**/agent-memory/**/*.gist.md
|
||||
16
CLAUDE.md
16
CLAUDE.md
@ -50,20 +50,20 @@ Kiến trúc: **.NET 10 Clean Architecture + 2 React FE (admin + user) + SQL Ser
|
||||
- Audit fields: `CreatedAt`, `UpdatedAt`, `CreatedBy`, `UpdatedBy` (`BaseEntity`)
|
||||
- Soft delete: `IsDeleted`, `DeletedAt`, `DeletedBy` (`AuditableEntity`)
|
||||
- Migrations: `dotnet ef migrations add <Name> --project src/Backend/SolutionErp.Infrastructure --startup-project src/Backend/SolutionErp.Api`
|
||||
- **Hiện có 50 migration → 88 bảng** (Phase 10 COMPLETE + Phase 11 P11-A→F done — Mig 34-42 HRM/Office/WorkflowApps/Attendance + Contract V2 (32-33) + WireWorkflowApps V2 (41) + LeaveBalance (42) + Holiday filtered-unique (43, S45) + Vehicle/Driver catalog (44, S51) + HRM-catalog filtered-unique 3× (45, S51) + ItTicket SLA (46, S52) + Master filtered-unique 3× (47, S53 gotcha #57 EXT) + Project master fields Year/Investor/Location/Package (48, S55 — AddColumn no new table, kèm nạp 62 dự án + 71 hạng mục + 3 NCC real data từ Excel qua `SeedRealMasterDataAsync` ungated idempotent) + PE gắn Hạng mục công việc WorkItemId loose-Guid KHÔNG FK vật lý (49, S57bis — AddColumn+CreateIndex, no new table) + **Mig 50 `ReplaceBudgetModuleWithPeWorkItemBudgets` (S61, 2026-06-13) — XÓA module Budget cũ, thay bằng `PeWorkItemBudgets` ngân sách per-gói-thầu (1 record/cặp Dự án × Hạng mục, nhập theo role PRO/CCM, vượt ngân sách = cảnh báo mềm cho lưu S62); backfill `BudgetManualAmount→BudgetPeriodAmount` TRƯỚC DropColumn (phiếu UAT giữ số); net bảng 93→88; gotcha #63/#64**. V2 schema history S29-era bên dưới giữ nguyên — Mig 32+33 Plan B Contract V2 cookie-cutter mirror PE Mig 22-26 (S29). Mig 26 `AddPeLevelOpinionsForV2`: bảng mới `PurchaseEvaluationLevelOpinions` UNIQUE composite (PEId, LevelId), FK Cascade Pe + Restrict Level. Section 5 "Ý kiến cấp duyệt" V2 dynamic theo workflow đã pin: forEach Step (Phòng) → forEach Level (Cấp) → forEach NV → 1 OpinionBox. Service `ApproveV2Async` UPSERT auto khi NV duyệt — Q1=1B (sync gắn với Duyệt, KHÔNG form input rời). SignedByUserId track signer thật, FE banner "Admin duyệt thay" khi !== ApproverUserId. Comment empty → "(duyệt — không ý kiến)" placeholder. Phiếu V1 legacy fallback Mig 15 4 box readOnly (data history). Mig 25 `AddIsUserSelectableToApprovalWorkflows`: ALTER `ApprovalWorkflows` +`IsUserSelectable bit` (admin pin/unpin workflow nào cho user pick lúc create phiếu, multi-select độc lập IsActive). Backfill `WHERE IsActive=1 SET 1` giữ behavior cũ. Designer +badge "Cho user chọn" + button Ghim/Bỏ ghim. Workspace filter dropdown chỉ workflows `IsUserSelectable=true`. Mig 22-24 V2 schema (Session 17): `ApprovalWorkflows`/Steps/Levels — Quy trình > Bước (Phòng) > Cấp (N NV cụ thể qua ApproverUserId, OR-of-N cùng cấp). PE.ApprovalWorkflowId pin V2. PE.CurrentApprovalLevelOrder track. State machine 5 trạng thái: Nháp / Đã gửi duyệt / Trả lại (Phase riêng TraLai=98) / Từ chối / Đã duyệt. PE Service V2 wire match `actor.Id == ApproverUserId`. Contract V2 ĐÃ WIRE (Mig 32+33 Plan B S29 — cookie-cutter mirror PE V2: `ApproveV2Async` + `ContractLevelOpinions` UPSERT + Workspace V2 Select dropdown). Mig 21 V1 flat workflow vẫn live cho phiếu cũ.)
|
||||
- **Số migration · bảng hiện tại → [`docs/STATUS.md`](docs/STATUS.md) (canonical — KHÔNG copy số ở đây để tránh drift; Harness-11 B1).** Lịch sử mig gần đây: +S74 Mig 55 `AddCcmNoteToPeWorkItemBudget` (PE CcmNote) · +S73 Mig 54 `AddPeSuggestedAndApprovedPrice` (PE giá đề xuất PRO/CCM). +S69: Mig 53 `AddPeUrgentAndCeoApprovalThreshold` — PE +IsUrgentByPro/Ccm cờ gấp PRO/CCM + ApprovalWorkflow +CeoApprovalThreshold ngưỡng CCM duyệt-final, 3 AddColumn no new table. +S65: Mig 51 `AddDepartmentParentId` Department.ParentId loose-Guid no-FK org-tree phân cấp + Mig 52 `AddHoSoLinkToPurchaseEvaluation` PE HoSoLink nvarchar(1000) hyperlink NAS — cả 2 AddColumn no new table, tables giữ 88. Phase 10 COMPLETE + Phase 11 P11-A→F done — Mig 34-42 HRM/Office/WorkflowApps/Attendance + Contract V2 (32-33) + WireWorkflowApps V2 (41) + LeaveBalance (42) + Holiday filtered-unique (43, S45) + Vehicle/Driver catalog (44, S51) + HRM-catalog filtered-unique 3× (45, S51) + ItTicket SLA (46, S52) + Master filtered-unique 3× (47, S53 gotcha #57 EXT) + Project master fields Year/Investor/Location/Package (48, S55 — AddColumn no new table, kèm nạp 62 dự án + 71 hạng mục + 3 NCC real data từ Excel qua `SeedRealMasterDataAsync` ungated idempotent) + PE gắn Hạng mục công việc WorkItemId loose-Guid KHÔNG FK vật lý (49, S57bis — AddColumn+CreateIndex, no new table) + **Mig 50 `ReplaceBudgetModuleWithPeWorkItemBudgets` (S61, 2026-06-13) — XÓA module Budget cũ, thay bằng `PeWorkItemBudgets` ngân sách per-gói-thầu (1 record/cặp Dự án × Hạng mục, nhập theo role PRO/CCM, vượt ngân sách = cảnh báo mềm cho lưu S62); backfill `BudgetManualAmount→BudgetPeriodAmount` TRƯỚC DropColumn (phiếu UAT giữ số); net bảng 93→88; gotcha #63/#64**. V2 schema history S29-era bên dưới giữ nguyên — Mig 32+33 Plan B Contract V2 cookie-cutter mirror PE Mig 22-26 (S29). Mig 26 `AddPeLevelOpinionsForV2`: bảng mới `PurchaseEvaluationLevelOpinions` UNIQUE composite (PEId, LevelId), FK Cascade Pe + Restrict Level. Section 5 "Ý kiến cấp duyệt" V2 dynamic theo workflow đã pin: forEach Step (Phòng) → forEach Level (Cấp) → forEach NV → 1 OpinionBox. Service `ApproveV2Async` UPSERT auto khi NV duyệt — Q1=1B (sync gắn với Duyệt, KHÔNG form input rời). SignedByUserId track signer thật, FE banner "Admin duyệt thay" khi !== ApproverUserId. Comment empty → "(duyệt — không ý kiến)" placeholder. Phiếu V1 legacy fallback Mig 15 4 box readOnly (data history). Mig 25 `AddIsUserSelectableToApprovalWorkflows`: ALTER `ApprovalWorkflows` +`IsUserSelectable bit` (admin pin/unpin workflow nào cho user pick lúc create phiếu, multi-select độc lập IsActive). Backfill `WHERE IsActive=1 SET 1` giữ behavior cũ. Designer +badge "Cho user chọn" + button Ghim/Bỏ ghim. Workspace filter dropdown chỉ workflows `IsUserSelectable=true`. Mig 22-24 V2 schema (Session 17): `ApprovalWorkflows`/Steps/Levels — Quy trình > Bước (Phòng) > Cấp (N NV cụ thể qua ApproverUserId, OR-of-N cùng cấp). PE.ApprovalWorkflowId pin V2. PE.CurrentApprovalLevelOrder track. State machine 5 trạng thái: Nháp / Đã gửi duyệt / Trả lại (Phase riêng TraLai=98) / Từ chối / Đã duyệt. PE Service V2 wire match `actor.Id == ApproverUserId`. Contract V2 ĐÃ WIRE (Mig 32+33 Plan B S29 — cookie-cutter mirror PE V2: `ApproveV2Async` + `ContractLevelOpinions` UPSERT + Workspace V2 Select dropdown). Mig 21 V1 flat workflow vẫn live cho phiếu cũ.)
|
||||
|
||||
### Modules
|
||||
|
||||
| Module | Namespace | Migration | Trạng thái |
|
||||
|---|---|---|---|
|
||||
| Contract (HĐ) | `Domain/Contracts/` | 1-11 | Feature-complete (7 ContractType × 9 phase) |
|
||||
| PurchaseEvaluation (Duyệt NCC tiền-HĐ) | `Domain/PurchaseEvaluations/` | 12,13,15,49,50 | Feature-complete — +Hạng mục (Mig 49) +ngân sách per-gói-thầu role PRO/CCM (Mig 50, vượt=cảnh báo mềm S62). Export PDF pending |
|
||||
| PurchaseEvaluation (Duyệt NCC tiền-HĐ) | `Domain/PurchaseEvaluations/` | 12,13,15,49,50,52,53,54,55 | Feature-complete — +Hạng mục (Mig 49) +ngân sách per-gói-thầu role PRO/CCM (Mig 50) +Link hồ sơ NAS (Mig 52) +cờ gấp PRO/CCM + CCM duyệt-final theo ngưỡng (Mig 53) +**giá đề xuất PRO/CCM + CCM-note (Mig 54/55, S73/S74)**. Export PDF pending |
|
||||
| ~~Budget (Ngân sách dự án)~~ | — | 14 → **Mig 50 DROP** | ⚠️ **REMOVED S61** — module Budget cũ XÓA, thay bằng PE-budget-per-gói-thầu (`PeWorkItemBudgets`). FE pages/types/menu `Bg_*` gỡ hết |
|
||||
| Master (Supplier/Project/Department) | `Domain/Master/` | 2, 10 | Feature-complete |
|
||||
| Identity (User/Role/Permission/MenuItem) | `Domain/Identity/` | 1, 3, 11 | Feature-complete (30 demo user — 16 sample + 14 Solutions thật) |
|
||||
| Forms (Template + Clause) | `Domain/Forms/` | 4 | Feature-complete |
|
||||
| Notifications | `Domain/Notifications/` | 6 | In-app + SignalR OK, email SMTP TODO |
|
||||
| **Tests** | `tests/SolutionErp.{Domain,Infrastructure}.Tests/` | — | **263 test pass** (45 Domain + 218 Infra) — CI gate + path filter docs-only skip |
|
||||
| **Tests** | `tests/SolutionErp.{Domain,Infrastructure}.Tests/` | — | **Test pass — số hiện tại → [`docs/STATUS.md`](docs/STATUS.md)** — CI gate + path filter docs-only skip |
|
||||
|
||||
### Commit convention
|
||||
|
||||
@ -77,14 +77,14 @@ Kiến trúc: **.NET 10 Clean Architecture + 2 React FE (admin + user) + SQL Ser
|
||||
|
||||
```
|
||||
tests/
|
||||
├── SolutionErp.Domain.Tests/ (58 test — Domain policy: Workflow / PE / Budget)
|
||||
└── SolutionErp.Infrastructure.Tests/ (128 test)
|
||||
├── SolutionErp.Domain.Tests/ (Domain policy: Workflow / PE)
|
||||
└── SolutionErp.Infrastructure.Tests/ (codegen + CQRS handler + authz regression)
|
||||
├── Common/ (SqliteDbFixture + TestApplicationDbContext + IdentityFixture S9)
|
||||
├── Services/ (17 codegen + 6 PE 2-stage approval S9)
|
||||
└── Application/ (6 test - PeWorkflowDefinition versioning)
|
||||
```
|
||||
|
||||
**263 unit test pass** (45 Domain + 218 Infra). CI gate + path filter live. (S61 +22 `PeWorkItemBudgetTests` −14 `BudgetPolicyTests` −1 → 263; S60 +14 `PeSubmitGuardAndBypassTests` +2 spec → 256; S57bis +12 `PeWorkItemGuardTests`. Domain giảm 58→45 do drop BudgetPolicyTests cùng module Budget.)
|
||||
**Unit test pass — số hiện tại → [`docs/STATUS.md`](docs/STATUS.md) (canonical).** CI gate + path filter live. (Lịch sử: S74 +5 CcmNote; S69 +20 Office-golive/PE-threshold; S67 +23 HRM test-after; S61 +22 `PeWorkItemBudgetTests` −14 `BudgetPolicyTests`, Domain drop BudgetPolicyTests cùng module **Budget đã REMOVE S61**.)
|
||||
|
||||
```bash
|
||||
dotnet test SolutionErp.slnx # chạy cả 2 test project
|
||||
@ -128,9 +128,9 @@ Quy tắc:
|
||||
| [`docs/workflow-contract.md`](docs/workflow-contract.md) | State machine 9 phase HĐ + role matrix |
|
||||
| [`docs/forms-spec.md`](docs/forms-spec.md) | Catalog 8 form + quy định mã HĐ RG-001 |
|
||||
| [`docs/database/database-guide.md`](docs/database/database-guide.md) | DB conventions + migration workflow + cheatsheet |
|
||||
| [`docs/database/schema-diagram.md`](docs/database/schema-diagram.md) | ⭐ ERD + luồng DB + data flow 88 table (+ §11 PE + §12 ~~Budget~~ DROP + §13 PEDeptOpinions + §14 Contract V2 LevelOpinions; §16+ Mig 32-50 pending) |
|
||||
| [`docs/database/schema-diagram.md`](docs/database/schema-diagram.md) | ⭐ ERD + luồng DB + data flow 88 table (+ §11 PE + §12 ~~Budget~~ DROP + §13 PEDeptOpinions + §14 Contract V2 LevelOpinions; §16+ Mig 32-55 pending) |
|
||||
| [`docs/flows/README.md`](docs/flows/README.md) | Index 6 flow (auth, permission, contract, form, SLA) |
|
||||
| [`docs/gotchas.md`](docs/gotchas.md) | ⭐ 64 bẫy đã gặp — đọc trước khi debug tương tự |
|
||||
| [`docs/gotchas.md`](docs/gotchas.md) | ⭐ Bẫy đã gặp (số hiện tại → [`docs/STATUS.md`](docs/STATUS.md)) — đọc trước khi debug tương tự |
|
||||
| [`.claude/skills/`](.claude/skills/README.md) | 6 skill: contract-workflow, form-engine, permission-matrix, dependency-audit-erp, ef-core-migration, iis-deploy-runbook |
|
||||
| [`docs/guides/vps-setup.md`](docs/guides/vps-setup.md) | ⭐ Master runbook deploy VPS shared với VIETREPORT |
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
|---|---|---|---|---|---|---|
|
||||
| 2026-06-09 | 2026-06-09-namgroup-to-se-ui-design-conventions | namgroup → se | processed | namgroup | 0140b81fb8a6 | ✓ |
|
||||
| 2026-06-11 | 2026-06-11-ai_infra-to-se-ui-ux-design-guide | ai_infra → se | processed | ai_infra | d353ee460dba | ✓ |
|
||||
| 2026-06-18 | 2026-06-18-ai_infra-to-se-harness-11-available | ai_infra → se | processed | ai_infra | b2a2fc1cf399 | ✓ |
|
||||
|
||||
## 📤 OUTBOUND (gửi — qua `/send-email <to>`)
|
||||
| sent (ISO) | id | from → to | folder | sha256(12) |
|
||||
@ -23,3 +24,8 @@
|
||||
| 2026-06-11 | 2026-06-11-se-to-ai_infra-harness-4-runtime-verified | se → ai_infra | outbox/ai_infra | ecf1d58797af |
|
||||
| 2026-06-15 | 2026-06-15-se-to-ai_infra-harness-5-6-adopt-report | se → ai_infra | outbox/ai_infra | 8a247984df9f |
|
||||
| 2026-06-15 | 2026-06-15-se-to-ai_infra-harness-7-adopt-report | se → ai_infra | outbox/ai_infra | 7e4f91f1ff7f |
|
||||
| 2026-06-16 | 2026-06-16-se-to-ai_infra-harness-8-adopt-status | se → ai_infra | outbox/ai_infra | fa7f690d9ce6 |
|
||||
| 2026-06-17 | 2026-06-17-se-to-ai_infra-harness-9-adopt-report | se → ai_infra | outbox/ai_infra | 7c07b716e775 |
|
||||
| 2026-06-18 | 2026-06-18-se-to-ai_infra-harness-9-10-checklist-adopted | se → ai_infra | outbox/ai_infra | e5f09d57c22e |
|
||||
| 2026-06-18 | 2026-06-18-se-to-ai_infra-harness-10-flat-refine-adopt-report | se → ai_infra | outbox/ai_infra | 5f511fe5c0f2 |
|
||||
| 2026-06-18 | 2026-06-18-se-to-ai_infra-harness-11-adopt-report | se → ai_infra | outbox/ai_infra | 2316773229f2 |
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
---
|
||||
id: 2026-06-18-ai_infra-to-se-harness-11-available
|
||||
from: ai_infra
|
||||
to: se
|
||||
category: Governance
|
||||
type: coord
|
||||
date: 2026-06-18
|
||||
content_sha256: b2a2fc1cf39988c2c64e088acf0f471a4425eaa8fad433b3ffddaf67ef4b400a
|
||||
nac: sent
|
||||
---
|
||||
|
||||
# [AI_INFRA] Thông báo: Harness-11 đã sẵn sàng để adap
|
||||
|
||||
AI_INFRA vừa công bố **Harness-11 — engine bộ-nhớ-và-governance tự-bảo-trì**. Hai tài liệu đã nằm trong kênh broadcast chung; mời bạn kéo về qua `/adap-apply`:
|
||||
|
||||
1. **Broadcast nền** (`2026-06-18-Governance-harness-11.md`): công bố engine, nguyên lý lõi, và cách áp bắt buộc.
|
||||
2. **Checklist tự-verify** (`2026-06-18-Governance-checklist-harness-11.md`): công cụ để bạn tự kiểm đã áp tới đâu, đúng-nấc và đủ-bộ-khung chưa.
|
||||
|
||||
**Tóm tắt engine:** nó tự-dò toàn diện mọi điểm lệch, cũ, hoặc mâu thuẫn và nêu cờ ngay (luôn tươi), nhưng tuyệt đối không tự-viết-lại nội dung hay luật. Mọi thao tác ghi đều đi qua người-chủ-trì — một-người-ghi là ranh giới không hạ. Chỉ những việc vô hại về ngữ nghĩa mới được tự-động; còn đổi luật thì người-sở-hữu-dự-án phê duyệt.
|
||||
|
||||
🔴 **Lưu ý quan trọng nhất khi adap — kiến-trúc cứng, khối-lượng linh-động:** bạn phải hiện diện **đủ tất cả các bộ-khung sàn-chức-năng**; thiếu hoặc bỏ sót một phần là **chưa đạt**, không được tính là "đã áp một phần". Chỉ khối-lượng (con số, độ sâu, cách trình bày) mới được linh-động. Đây là chỗ dễ quên nhất, nên checklist có sẵn cổng-gác completeness để bạn rà bằng bằng chứng thật, không bằng trí nhớ.
|
||||
|
||||
**Cách áp (bắt buộc):** sau khi `/adap-apply`, hãy adap theo đúng giao thức hai workflow tách biệt — một workflow IMPLEMENT để áp engine, một workflow REVIEW độc lập để soi lại và chắt lọc know-how — rồi gửi báo cáo chuẩn về AI_INFRA.
|
||||
|
||||
Trân trọng,
|
||||
AI_INFRA
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user