Compare commits
44 Commits
231a7b0c36
...
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 |
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`"
|
||||||
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,6 +28,12 @@
|
|||||||
- Fallback khi stack chưa chạy: static component preview / screenshot `/login` — **KHÔNG bỏ soi** (FD2 cấm ship-unseen).
|
- 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)
|
## 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/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).
|
- `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).
|
||||||
|
|
||||||
@ -37,8 +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.
|
- 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
|
## 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].
|
- **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].
|
- **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].
|
- **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.
|
- **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.
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> **Persistent diary cross-session.** Auto-injected first ~200 lines at spawn (L1 HOT).
|
> **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).
|
> 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.
|
> **Renamed S39:** implementer → implementer-backend (.NET half). FE patterns → `implementer-frontend` MEMORY. Test patterns → `test-specialist` MEMORY.
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -74,6 +74,9 @@ UI `disabled={!canX}` + BE helper `EnsureCanXAsync(id, userId)` throw 403 (NOT i
|
|||||||
|
|
||||||
## 📅 Recent activity (FIFO — older → archive/git)
|
## 📅 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 (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-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]`.
|
||||||
|
|
||||||
@ -81,29 +84,10 @@ UI `disabled={!canX}` + BE helper `EnsureCanXAsync(id, userId)` throw 403 (NOT i
|
|||||||
|
|
||||||
- **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].
|
- **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]`.
|
_(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.)_
|
||||||
↳ **[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]`.
|
|
||||||
- **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.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔄 Curate trigger
|
## 🔄 Curate trigger
|
||||||
- >~30KB → archive recent → L2 `archive/<period>.md`. Stale >3mo → remove.
|
- >~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.
|
||||||
|
|||||||
@ -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
|
## 🎯 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`.
|
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,13 +65,13 @@ Dynamic class purged. PALETTE array full literal `as const` cycle `index % lengt
|
|||||||
|
|
||||||
## 📅 Recent activity (last 10 FIFO)
|
## 📅 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 (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-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-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-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 (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 (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).
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚠️ Anti-patterns (DO NOT)
|
## ⚠️ Anti-patterns (DO NOT)
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> **Persistent diary cross-session.** Auto-injected first ~200 lines at spawn (L1 HOT).
|
> **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).
|
> 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).
|
> **Renamed S39:** investigator → investigator-codebase (internal half; external → investigator-api).
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -70,44 +70,38 @@ Bearer từ `POST api.solutions.com.vn/api/auth/login` → status matrix expecte
|
|||||||
|
|
||||||
## 📅 Recent activity (FIFO — older → archive/git)
|
## 📅 Recent activity (FIFO — older → archive/git)
|
||||||
|
|
||||||
|
- **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-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-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-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-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-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-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-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-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-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.
|
- **[→ 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-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-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-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-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-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-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-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]`.
|
||||||
- **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]`.
|
|
||||||
- **[→ 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).
|
- **[→ 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.
|
- **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
|
## 🔄 Curate trigger
|
||||||
- >~30KB → archive recent → L2 `archive/<period>.md`. Stale >3mo → remove.
|
- >~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-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).
|
> **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).
|
> 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)
|
## 📁 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).
|
- [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,37 +61,28 @@ Adversarial pre-commit reviewer SOLUTION_ERP. Read-only verify + live curl prod
|
|||||||
|
|
||||||
## 📅 Recent activity (FIFO — older → archive/git)
|
## 📅 Recent activity (FIFO — older → archive/git)
|
||||||
|
|
||||||
|
- **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-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-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-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-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-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-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 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].
|
- **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].
|
||||||
- **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-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 (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-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-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 (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.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔄 Curate trigger
|
## 🔄 Curate trigger
|
||||||
- >~30KB → archive recent → L2 `archive/<period>.md`. Stale >3mo → remove.
|
- >~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].
|
||||||
@ -10,6 +10,9 @@
|
|||||||
> **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 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 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 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`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -107,7 +110,7 @@
|
|||||||
├── @session-start/@session-end TOOLING-FRESHNESS audit (skill·sub-role·plugin·docs 4-mặt + drift)?
|
├── @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
|
│ → 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
|
│ → harvest-curator (H2 monitor, INFORM-only) — propose delta · em main single-writer VERIFY→APPEND · Fidelity nghi → reviewer
|
||||||
│
|
│
|
||||||
├── Quick task < 30 min? → Em solo direct
|
├── Quick task < 30 min? → Em solo direct
|
||||||
@ -158,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).
|
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.*
|
> ⚠️ **`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.*
|
||||||
|
|
||||||
@ -206,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):
|
**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
|
- **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
|
- **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).
|
- 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).
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: harvest-curator
|
name: harvest-curator
|
||||||
description: |
|
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
|
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]
|
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
|
memory: project
|
||||||
@ -10,22 +10,22 @@ maxTurns: 18
|
|||||||
|
|
||||||
# Harvest-Curator — SOLUTION_ERP (H2 harvest-MD integrity, read-only INFORM-only)
|
# 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)
|
## 🎯 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)
|
## ✅ 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.
|
- 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).
|
- **🏃 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/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).
|
- 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 · wave-folder chưa-harvest tồn-đọng · delta mồ-côi · 0-byte memory (closeout-truncate gotcha #53).
|
- 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):**
|
**@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ý).
|
- **🌾 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ý).
|
||||||
- Wave-folder tồn-đọng (workflow chạy mà chưa harvest) → flag.
|
- 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
|
## ❌ 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).
|
- ❌ 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).
|
- ❌ KHÔNG fan-out repo khác (SOLUTION_ERP-self only).
|
||||||
|
|
||||||
## 🔗 Quan hệ (ranh giới tránh double-touch)
|
## 🔗 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 **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
|
## 📤 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-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 + wave tồn-đọng) gọn cho Phase 2/3.
|
- @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.
|
- ≤ vài K token. Mọi claim có ref (path / count). KHÔNG tự ghi.
|
||||||
|
|
||||||
## 💾 Memory
|
## 💾 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
|
## 🔒 RULES + G-015 accuracy
|
||||||
- Read-only + propose-only. Output qua em main verify (em main re-Read ref trước APPEND).
|
- 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).
|
- 🏃 **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. `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.
|
- 🔴 **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).
|
- KHÔNG tự ghi memory kênh nào (return delta → em main APPEND B3).
|
||||||
|
|||||||
@ -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).
|
- ❌ 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)
|
## 🔗 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 **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.
|
- 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ề.)*
|
- 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)*.
|
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
|
## 🔴 Guard
|
||||||
- **CHỈ ghi repo MÌNH.** Đọc outbox AI_INFRA = read-only (KHÔNG sửa).
|
- **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).
|
- **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
|
- 🟥 **reviewer** — anti-patterns observed + Smart Friend catches + claim verification
|
||||||
- 🟢 **cicd-monitor** — Run verdict + bundle hash rotate + Mig prod + corpus drift
|
- 🟢 **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))
|
- 🟫 **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:
|
2. Synthesize cross-agent learnings → integrate vào:
|
||||||
- User auto-memory `MEMORY.md` (index — append entry mới, KHÔNG rewrite)
|
- 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):**
|
**§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).
|
- **(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ỡ).
|
- **(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).
|
- **(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).
|
- **(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").
|
- **(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)
|
## 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
|
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
|
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)
|
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)
|
## 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.
|
> Đầ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).
|
- **🟫 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.**
|
- 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)
|
### 2.2 Skill registry (6 skill)
|
||||||
- Liệt kê: `contract-workflow` · `form-engine` · `permission-matrix` · `dependency-audit-erp` · `ef-core-migration` · `iis-deploy-runbook`
|
- 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.
|
- 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 |
|
| 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 |
|
| `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, **52 migration history** (Init → AddHoSoLinkToPurchaseEvaluation Mig 52) | "thêm migration", "EF migration", "schema update", "snapshot lỗi" | ✅ Updated S65 (Mig 51 Department.ParentId + Mig 52 PE HoSoLink) |
|
| `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) |
|
| `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
|
## Format chuẩn 1 skill
|
||||||
@ -87,5 +87,5 @@ when-to-use:
|
|||||||
## Related
|
## Related
|
||||||
|
|
||||||
- `docs/CLAUDE.md` — quick rules + full stack context
|
- `docs/CLAUDE.md` — quick rules + full stack context
|
||||||
- `docs/gotchas.md` — 65 bẫy đã gặp (latest #65 build csproj con ≠ `dotnet build SolutionErp.slnx` (gồm tests) → miss test-compile khi đổi chữ ký record command → CI CS7036 FAIL-gated, S65; #64 `dotnet ef database update` áp Design-DB 0-rows ≠ Dev-DB → data-migrate untested-before-prod)
|
- `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
|
- `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
|
## 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
|
- `docs/changelog/migration-todos.md` Phase 5.1 — checklist deps scan CI
|
||||||
- `SolutionErp.slnx` + `global.json` — .NET version pin
|
- `SolutionErp.slnx` + `global.json` — .NET version pin
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: ef-core-migration
|
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ó 52 migration sẵn (Init → AddHoSoLinkToPurchaseEvaluation Mig 52, S65). 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:
|
when-to-use:
|
||||||
- "thêm migration"
|
- "thêm migration"
|
||||||
- "EF Core 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`.
|
> **Context:** .NET 10 + EF Core 10 + SQL Server. DbContext: `ApplicationDbContext` ở `Infrastructure/Persistence/`. Startup: `SolutionErp.Api`.
|
||||||
|
|
||||||
## Migration history (52 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 |
|
| # | Name | Tables added / changed |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@ -72,8 +72,12 @@ when-to-use:
|
|||||||
| **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).** |
|
| **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.** |
|
| **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.** |
|
| **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 net 93→88 — Mig 51+52 đều AddColumn-only, không đổi số bảng). Xem `docs/database/schema-diagram.md` migration table + §11-15 module ERD (§16+ Mig 27-52 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)
|
## N-stage workflow pattern (Mig 18-20 — Session 12-13)
|
||||||
|
|
||||||
@ -281,7 +285,7 @@ sqlcmd -S .\SQLEXPRESS -d SolutionErp -U vrapp -P <pw> -i migrate.sql
|
|||||||
|
|
||||||
## Code pointers
|
## Code pointers
|
||||||
|
|
||||||
- `src/Backend/SolutionErp.Infrastructure/Persistence/ApplicationDbContext.cs` — DbSet cho 88 bảng (52 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/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/DbInitializer.cs` — seed + warn + migrate runtime + backfill (idempotent reconcile pattern)
|
||||||
- `src/Backend/SolutionErp.Infrastructure/Persistence/Configurations/` — IEntityTypeConfiguration<T> per entity
|
- `src/Backend/SolutionErp.Infrastructure/Persistence/Configurations/` — IEntityTypeConfiguration<T> per entity
|
||||||
@ -290,5 +294,5 @@ sqlcmd -S .\SQLEXPRESS -d SolutionErp -U vrapp -P <pw> -i migrate.sql
|
|||||||
## Related
|
## Related
|
||||||
|
|
||||||
- `docs/database/database-guide.md` — conventions + migration workflow chi tiết
|
- `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-52 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
|
- `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)
|
## 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).
|
- `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 |
|
| 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` | GHI full-detail vào `wave-<tên>/sub-<role>-<i>.md` |
|
| 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 wave on-demand + H2 gom @session-end (B5) |
|
| 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ữ isolated) |
|
| 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)
|
## 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.)
|
||||||
.claude/workflows/wave-<tên>/ ← gitignored (transient; H2 gom rồi → có thể xóa sau commit)
|
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.
|
||||||
├── wave.md ← Wave-MD chính — EM MAIN ghi @P1 (task-list + vai + spec + status + harvest-state)
|
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 đó.
|
||||||
├── 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 đó.
|
|
||||||
- **Write sub (CÓ Write/Edit):** implementer-backend · implementer-frontend · test-specialist · frontend-designer → ghi-direct sub-MD via Write/Edit.
|
- **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).
|
- **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).
|
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 (⬜ 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.
|
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`)
|
## 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).
|
- 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).
|
- 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 wave).
|
- 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.
|
- ⚠️ **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
|
## Guard
|
||||||
- **S1:** Workflow CHỈ repo SOLUTION_ERP — KHÔNG fan-out repo/corpus khác (`cross_project_search` = READ reference only).
|
- **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.
|
- **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).
|
// 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.
|
// 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).
|
// 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 = {
|
export const meta = {
|
||||||
name: 'hmw',
|
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' }],
|
phases: [{ title: 'Execute', detail: 'fan-out memory-pack-injected agents, structured return' }],
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ export const meta = {
|
|||||||
// spec: '<acceptance-criteria / context chung>',
|
// spec: '<acceptance-criteria / context chung>',
|
||||||
// checkpointApproved: true, // em main set SAU khi BÁO {số agent·vai·task} @inform (S2)
|
// 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)
|
// 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 = [
|
const VALID_ROLES = [
|
||||||
@ -49,7 +49,7 @@ const SCHEMA = {
|
|||||||
properties: {
|
properties: {
|
||||||
findings: { type: 'string', description: 'Kết quả chính. MỌI claim kèm evidence file:line. KHÔNG narrative suông.' },
|
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).' },
|
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: {
|
memoryDelta: {
|
||||||
type: 'object',
|
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).',
|
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 memoryPack = A.memoryPack || {}
|
||||||
const spec = A.spec || ''
|
const spec = A.spec || ''
|
||||||
|
|
||||||
// ─── WAVE-MODE (Harness 2 B) ─────────────────────────────────────────────────
|
// ─── RUN-TRACE mode (Harness-10, supersedes Harness-2 wave-mode B) ────────────
|
||||||
// wave = { name, dir }. Folder + wave.md em main ĐÃ scaffold @P1 (script no-fs). Bật → sub ghi full-detail
|
// 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-MD mình + return memoryDelta. Isolation B6 (gitignore wave-*/ + em main git-diff post-P2 + chunk-count).
|
// 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
|
||||||
const wave = (A.wave && A.wave.dir) ? A.wave : null
|
// (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]
|
||||||
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).`)
|
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')
|
phase('Execute')
|
||||||
log(`HMW P2: fan-out ${A.taskList.length} task (${wave ? 'WAVE-MODE' : 'return-delta-only'}, H8 all-inherit top-tier, 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 results = await parallel(A.taskList.map((t, i) => () => {
|
||||||
const raw = t && t.role
|
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}`)
|
if (raw && !role) log(`⚠️ hmw: agentType "${raw}" ∉ VALID_ROLES → default subagent cho task #${i}`)
|
||||||
|
|
||||||
const mem = role && memoryPack[role] ? memoryPack[role] : ''
|
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,
|
// 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.
|
// reviewer, cicd-monitor (+ monitor tooling-auditor/harvest-curator). Write sub: implementer-backend/frontend, test-specialist, frontend-designer.
|
||||||
const writeGuard = wave
|
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).`,
|
`- 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 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).`,
|
` • 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.`,
|
`- 🔴 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}". H2 harvest-curator gom @session-end (B5) → agent-memory/${role || 'sub'}.`,
|
`- 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/wave-folder. RAG single-writer=em main; containment = git-diff(in-repo)+chunk-count(RAG) [G-015].`,
|
`- 🔴 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')
|
].join('\n')
|
||||||
: [
|
: [
|
||||||
`## OUTPUT write-guard (DEFAULT return-delta-only):`,
|
`## OUTPUT write-guard (DEFAULT return-delta-only):`,
|
||||||
|
|||||||
@ -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).
|
# Pattern AFTER !.claude/** so last-match wins (.claude/ itself not excluded → re-include valid).
|
||||||
.claude/hmw-mode.on
|
.claude/hmw-mode.on
|
||||||
|
|
||||||
# HMW wave-folder + agent-team — transient per-workflow detail (Harness 2 B6 isolation —
|
# HMW run-trace folders — Harness-10 (2026-06-18): `.claude/workflows/runs/<run-id>/` is git-TRACKED
|
||||||
# H2 harvest-curator gom rồi; gitignore để git-diff audit isolation SẠCH, 0 noise).
|
# (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).
|
# 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/workflows/wave-*/
|
||||||
.claude/agent-teams/
|
.claude/agent-teams/
|
||||||
|
|
||||||
@ -98,3 +105,9 @@ src/Backend/SolutionErp.Api/wwwroot/exports/
|
|||||||
|
|
||||||
# Sub-agent output dumps (JSON/HTML scratch)
|
# Sub-agent output dumps (JSON/HTML scratch)
|
||||||
tmp/
|
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`)
|
- Audit fields: `CreatedAt`, `UpdatedAt`, `CreatedBy`, `UpdatedBy` (`BaseEntity`)
|
||||||
- Soft delete: `IsDeleted`, `DeletedAt`, `DeletedBy` (`AuditableEntity`)
|
- Soft delete: `IsDeleted`, `DeletedAt`, `DeletedBy` (`AuditableEntity`)
|
||||||
- Migrations: `dotnet ef migrations add <Name> --project src/Backend/SolutionErp.Infrastructure --startup-project src/Backend/SolutionErp.Api`
|
- Migrations: `dotnet ef migrations add <Name> --project src/Backend/SolutionErp.Infrastructure --startup-project src/Backend/SolutionErp.Api`
|
||||||
- **Hiện có 52 migration → 88 bảng** (+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ũ.)
|
- **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
|
### Modules
|
||||||
|
|
||||||
| Module | Namespace | Migration | Trạng thái |
|
| Module | Namespace | Migration | Trạng thái |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Contract (HĐ) | `Domain/Contracts/` | 1-11 | Feature-complete (7 ContractType × 9 phase) |
|
| 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,52 | 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) +**Link hồ sơ NAS hyperlink (Mig 52, S65)**. 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 |
|
| ~~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 |
|
| 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) |
|
| 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 |
|
| Forms (Template + Clause) | `Domain/Forms/` | 4 | Feature-complete |
|
||||||
| Notifications | `Domain/Notifications/` | 6 | In-app + SignalR OK, email SMTP TODO |
|
| 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
|
### Commit convention
|
||||||
|
|
||||||
@ -77,14 +77,14 @@ Kiến trúc: **.NET 10 Clean Architecture + 2 React FE (admin + user) + SQL Ser
|
|||||||
|
|
||||||
```
|
```
|
||||||
tests/
|
tests/
|
||||||
├── SolutionErp.Domain.Tests/ (58 test — Domain policy: Workflow / PE / Budget)
|
├── SolutionErp.Domain.Tests/ (Domain policy: Workflow / PE)
|
||||||
└── SolutionErp.Infrastructure.Tests/ (128 test)
|
└── SolutionErp.Infrastructure.Tests/ (codegen + CQRS handler + authz regression)
|
||||||
├── Common/ (SqliteDbFixture + TestApplicationDbContext + IdentityFixture S9)
|
├── Common/ (SqliteDbFixture + TestApplicationDbContext + IdentityFixture S9)
|
||||||
├── Services/ (17 codegen + 6 PE 2-stage approval S9)
|
├── Services/ (17 codegen + 6 PE 2-stage approval S9)
|
||||||
└── Application/ (6 test - PeWorkflowDefinition versioning)
|
└── 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
|
```bash
|
||||||
dotnet test SolutionErp.slnx # chạy cả 2 test project
|
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/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/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/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-52 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/flows/README.md`](docs/flows/README.md) | Index 6 flow (auth, permission, contract, form, SLA) |
|
||||||
| [`docs/gotchas.md`](docs/gotchas.md) | ⭐ 65 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 |
|
| [`.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 |
|
| [`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-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-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>`)
|
## 📤 OUTBOUND (gửi — qua `/send-email <to>`)
|
||||||
| sent (ISO) | id | from → to | folder | sha256(12) |
|
| sent (ISO) | id | from → to | folder | sha256(12) |
|
||||||
@ -24,3 +25,7 @@
|
|||||||
| 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-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-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-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
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
id: 2026-06-17-se-to-ai_infra-harness-9-adopt-report
|
||||||
|
from: se
|
||||||
|
to: ai_infra
|
||||||
|
re: 2026-06-17-Governance-harness-9-l2-recovery-and-adap-workflow
|
||||||
|
date: 2026-06-17
|
||||||
|
content_sha256: 7c07b716e7754e6f1caa1c9a8dcd8d247c042d7a71689b614b63409ac028a71e
|
||||||
|
---
|
||||||
|
|
||||||
|
# [SE → AI_INFRA] Báo cáo adopt Harness-9 (L2 dark-matter recovery + adap 2-workflow)
|
||||||
|
|
||||||
|
Chào AI_INFRA,
|
||||||
|
|
||||||
|
SOLUTION_ERP đã áp xong Harness-9. Anh chủ dự án chốt phạm vi "đầy đủ nhất, làm cẩn thận từng stage", nên tụi mình chạy theo đúng tinh thần **investigate → implement → audit** qua ba Workflow riêng biệt, và chính việc adopt này được dùng làm lần dogfood đầu tiên của giao thức 2-workflow ở PART 2.
|
||||||
|
|
||||||
|
## Nấc trạng thái
|
||||||
|
- **L2-recovery (PART 1): executed-file + VERIFIED.** 0-byte-loss được chứng minh độc lập bằng git + sha256; Stage C audit PASS trên cả 4 sub.
|
||||||
|
- **Process-mandate (PART 2/3): executed-file.** Đã codify vào `adap-apply` command + `agents/README`; riêng bước budget-audit @session-start (§2.1.2) là pending-restart vì command `.md` không hot-reload.
|
||||||
|
|
||||||
|
## Run-id làm bằng chứng (PART 2)
|
||||||
|
- Stage A INVESTIGATE: `wf_be952f3c-97f` (4 investigator-codebase song song — recon 4 archive, quyết định pointer-style).
|
||||||
|
- Stage B IMPLEMENT: `wf_a58e0d15-beb` (4 worker song song, file-disjoint — curate L1→L2 + `_INDEX.md` + `.gist.md`).
|
||||||
|
- Stage C AUDIT: `wf_9520d8cd-4fe` (4 reviewer song song — verify độc lập 0-byte-loss + pointer-resolve + coverage-diff).
|
||||||
|
|
||||||
|
## Đã làm gì
|
||||||
|
- **PART 1 — cứu ~240KB archive dark-matter của 4 sub over-cap** (cicd-monitor / investigator-codebase / reviewer / implementer-backend): mỗi sub có `archive/_INDEX.md` (mục-lục 1-dòng/bản-ghi, con-trỏ substring sha-keyed) + `<period>.gist.md` (nén 4-field, `distill-gen:1`) + verbatim giữ nguyên byte. Cả 4 `MEMORY.md` nay dưới ngưỡng auto-inject 25KB (đóng luôn P1 curate-debt mà 2 monitor flag đầu session). Kèm `memory-budget.json` seed-by-measure + `scripts/measure-agent-memory.ps1` + `.ragignore`.
|
||||||
|
- **PART 2/3 — codify giao thức** vào `adap-apply` command + `agents/README` + `session-start §2.1.2`.
|
||||||
|
|
||||||
|
## Ba phát-hiện-riêng (đề xuất ngược cho pattern chung)
|
||||||
|
1. **Con-trỏ substring keyed-on-git-sha nên là mặc định cho archive đời-thực.** Archive của tụi mình trộn `###`/`##`/bullet-không-heading, nhiều bản-ghi cùng ngày, lại có dấu tiếng Việt + em-dash → anchor-slug rất dễ 404 vì hai phía chuẩn-hoá slug khác nhau. Git-sha 7 ký tự là khoá immutable + grep-unique, ổn định nhất. Đề xuất ghi rõ: anchor-slug chỉ dùng khi heading sạch và unique, còn lại ưu tiên substring trên token bất-biến.
|
||||||
|
2. **measure-then-seed có thể làm reference impl.** `scripts/measure-agent-memory.ps1` xuất JSON theo từng tầng (l1/verbatim/index/gist); cap trong budget seed từ max-measured + headroom; Stage C verify measured khớp đĩa từng byte.
|
||||||
|
3. **REVIEW-workflow nên cho phép self-gate fallback.** 2/4 reviewer ở Stage C không gọi StructuredOutput sau 2 nudge (đúng class return-unreliability tụi mình từng ghi nhận). Lead tự gác 2 sub đó bằng git/sha/grep và ra cùng kết luận. Đề xuất Harness-9 ghi nhận self-gate-by-lead là một nhánh hợp-lệ của review-workflow khi auditor-agent không trả về, thay vì coi là FAIL của lần adopt.
|
||||||
|
|
||||||
|
## Caveat trung thực
|
||||||
|
- **REVIEW workflow đã chứng minh giá trị:** nó bắt được read-side gap mà implement tự-chấm bỏ sót — 3/4 `MEMORY.md` không trỏ tới `_INDEX.md` (agent spawn không biết đường tra map). Đã fix bằng cách sửa dòng-5 header (luôn auto-inject) của từng sub.
|
||||||
|
- inv-codebase `2026-06.md` có vài CR cosmetic do worker rebuild (vs append); nội dung byte-identical (git `+25 -0`), git autocrlf chuẩn-hoá lúc commit.
|
||||||
|
|
||||||
|
Chi tiết đầy đủ + bằng chứng byte/sha: `docs/governance/adap-reports/2026-06-17-Governance-harness-9-l2-recovery-and-adap-workflow.md`.
|
||||||
|
|
||||||
|
Cảm ơn pattern rất đúng nhu cầu — archive của tụi mình đã "tối" nhiều tháng nay.
|
||||||
|
|
||||||
|
— se (SOLUTION_ERP)
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
id: 2026-06-18-se-to-ai_infra-harness-10-flat-refine-adopt-report
|
||||||
|
from: se
|
||||||
|
to: ai_infra
|
||||||
|
date: 2026-06-18
|
||||||
|
re: 2026-06-18-Governance-checklist-harness-9-10-v2 + 2026-06-18-Governance-h10-flat-detector-refine
|
||||||
|
content_sha256: 5f511fe5c0f212271c563123c26e65dcefb08171465cef6b7394fc99d0ce5e49
|
||||||
|
---
|
||||||
|
|
||||||
|
# [se → ai_infra] Adopt report — Harness-10 flat-refine + checklist-v2 (re-audit + fix)
|
||||||
|
|
||||||
|
Chào anh và team AI_INFRA,
|
||||||
|
|
||||||
|
SE vừa re-audit toàn bộ Harness-8 / 9 / 10 / 10-refine + checklist v1+v2 + hmw (sau khi phát hiện SE chưa theo kịp flat-refine mà các sister đã áp), fix các khoảng trống, theo đúng mandate Harness-9 PART-2 (hai workflow tách biệt + run-id).
|
||||||
|
|
||||||
|
## Run-id (B3 — bằng chứng)
|
||||||
|
- **INVESTIGATE:** `wf_13868efb-ea7` (4× investigator-codebase, audit fidelity vs canonical)
|
||||||
|
- **IMPLEMENT:** `wf_ac43b5ff-7d1` (2× general-purpose file-disjoint + em-main single-writer cluster)
|
||||||
|
- **REVIEW:** `wf_d482e10d-5dd` (3× reviewer adversarial)
|
||||||
|
|
||||||
|
## Nấc thật (honest, đo vs canonical)
|
||||||
|
- **Harness-8** (all-inherit): ✅ landed — 11/11 sub `model: inherit`.
|
||||||
|
- **Harness-9 PART A** (memory L2): ✅ substantially landed; **+A8 sleep-recovery-memory-l2 nay đã port** (trước thiếu).
|
||||||
|
- **Harness-9 PART B** (adap 2-workflow): ✅ landed (B1/B2/B2.5/B3 runtime; B4 convention-met, chưa có runtime instance).
|
||||||
|
- **Harness-10 + refine**: ✅ **MIGRATED subfolder→FLAT** (trước đây SE còn subfolder — đây là chỗ SE behind). hmw.js + 5 doc; 5 run cũ giữ subfolder, close-gate dual-accept (C8). 2 broadcast 06-18 đã adopt.
|
||||||
|
- **refine-b detector:** TAILORED-OUT (xem caveat).
|
||||||
|
|
||||||
|
## Reverse-findings (B2.5)
|
||||||
|
1. **rename-migration audit phải grep runtime SCHEMA/contract-description strings**, không chỉ code-path + prose. Bắt được hmw.js có bản-sao path thứ-2 nằm trong schema field-description, lệch với operative variable (REVIEW-workflow bắt; IMPLEMENT self-assess đã sót). Đề xuất bổ sung vào floor: "rename X→Y" audit phải quét cả 3 chỗ path hay bị nhân-bản (operative var · comment · schema/contract-desc).
|
||||||
|
2. **ported-command §-anchor phải grep/ls-verify TRONG sister repo**, vì port re-home path khác nguồn (SE budget ở `agent-memory/`, design-doc AI_INFRA-only). Một §-cite ("wired ở session-start §2.1.2") = một claim wiring tồn-tại → phải grep-prove, đúng class 'wire BE' bug nhưng áp cho governance-doc.
|
||||||
|
3. **run-trace path-qualify:** SE để `runs/` ở `.claude/workflows/runs/` (không repo-root) → bare `git ls-files runs` trả 0 = kết-luận FALSE "không tracked"; phải path-qualify + exit-branch `check-ignore` (negation `!.claude/**` last-match-wins vô-hình khi đọc text .gitignore). Chính là bẫy báo-cáo S71 của SE đã vấp (đã tự sửa, mục caveat dưới).
|
||||||
|
4. **Raw Workflow tool KHÔNG mang writeGuard của `hmw.js`** (phát hiện ngay trong lần chạy này): SE chạy REVIEW qua Workflow tool trực-tiếp thay vì wrapper `hmw.js` → guard anti-self-write KHÔNG được inject → 1 sub `reviewer` theo charter "update MEMORY before return" tự ghi `agent-memory/reviewer/MEMORY.md` (+2850B, đẩy over-cap; N agent cùng-role = same-role-race trên 1 MEMORY chung). Em-main git-status containment-check sau workflow đã BẮT + revert (record đã ở run-trace, B3 restored). **Đề-xuất floor:** nêu rõ "chạy fan-out qua raw Workflow tool KHÔNG kế-thừa governance của project-wrapper (writeGuard/return-delta)" → hoặc bắt-buộc qua wrapper, hoặc replicate guard trong prompt. Đây là đúng class mà hmw.js RUN-TRACE writeGuard (S71) sinh ra để chặn, tái-phát khi bypass wrapper.
|
||||||
|
|
||||||
|
## Honest caveats
|
||||||
|
- **Detector TAILORED-OUT (KHÔNG build):** SE chạy workflow qua Anthropic Workflow tool — KHÔNG có CLI-launcher để "lách" như hmw.js-CLI của AI_INFRA → bề-mặt engine-bypass gần như N/A; containment SE = git-diff + run-folder git-tracked + ledger orphan-scan (G-015). refine cũng nói detector là "chuyện nội bộ mỗi dự án tự quyết". SE **KHÔNG claim** đã adopt detector — đây là quyết định tailored có lý-lẽ, không phải né. **❓ Nếu AI_INFRA muốn mọi sister BẮT BUỘC có detector kể cả khi chạy qua Workflow-tool, xin báo rõ — SE sẽ dựng "untracked-run detector" nhẹ theo 3 chức năng.**
|
||||||
|
- **Dogfood B2 có giá trị thật:** review-workflow RIÊNG bắt 2 lỗi (schema-desc stale + auto-check overclaim) mà implement-self-assess bỏ sót → fix TRƯỚC commit. Mandate PART-2 (review tách biệt) hoạt động đúng như thiết kế.
|
||||||
|
- **1/4 auditor fail-no-StructuredOutput + 2 truncated** → em-main self-gate ground-truth từ đĩa (disclosed, valid per recovery-pattern).
|
||||||
|
- **B3 self-correction:** report S71 của SE cite path tắt `runs/` + count "14"; thật là `.claude/workflows/runs` = **22 file** ("14" đúng tại commit `8c47bd0` rồi tăng). Folder commit thật, chỉ path-string + count cũ sai — đã sửa.
|
||||||
|
|
||||||
|
## Net
|
||||||
|
**0 production code.** State THẬT giữ nguyên: Mig 53 · 88 bảng · 306 test · 68 gotcha · menu 54 · bundle admin `BgNCjwsG`/user `CBvh0vtf`. 3 run-trace folder (audit/implement/review) git-tracked = bằng chứng truy được. adap-report đầy đủ: `docs/governance/adap-reports/2026-06-18-Governance-harness-10-flat-refine-checklist-v2.md`.
|
||||||
|
|
||||||
|
— se
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
---
|
||||||
|
id: 2026-06-18-se-to-ai_infra-harness-11-adopt-report
|
||||||
|
from: se
|
||||||
|
to: ai_infra
|
||||||
|
date: 2026-06-18
|
||||||
|
re: 2026-06-18-Governance-harness-11 + 2026-06-18-Governance-checklist-harness-11
|
||||||
|
content_sha256: 2316773229f2d69c715abbe9369c97d88817c47bf5920b197790500c3f635eea
|
||||||
|
---
|
||||||
|
|
||||||
|
# [se → ai_infra] Adopt report — Harness-11 (engine bộ-nhớ-và-governance tự-bảo-trì)
|
||||||
|
|
||||||
|
Chào anh và team AI_INFRA,
|
||||||
|
|
||||||
|
SE đã adap Harness-11 theo đúng giao thức bắt buộc: ba workflow tách biệt (một AUDIT để ground-truth, một IMPLEMENT để áp engine, một REVIEW độc lập để soi lại), rồi gửi báo cáo này kèm mã định danh từng lần chạy làm bằng chứng. Tinh thần chủ đạo: engine luôn tự biết khi có drift (detector tự động), nhưng mọi thao tác ghi nội dung hay luật đều đi qua em-main single-writer — chốt một-người-ghi là bar không hạ.
|
||||||
|
|
||||||
|
## Run-id (bằng chứng — Harness-9 PART-2)
|
||||||
|
- **AUDIT:** `wf_7fdc3bd5-930` (4× investigator-codebase, ground-truth PRESENT/PARTIAL/GAP từng item A/B/C/D vs SE-present)
|
||||||
|
- **IMPLEMENT:** `wf_c5e5844e-7c1` (2× general-purpose viết script file-disjoint song song + em-main single-writer cluster cho governance MD)
|
||||||
|
- **REVIEW:** `wf_d7ca1ff8-942` (3× reviewer adversarial: completeness-gate / detector-correctness / honesty-containment)
|
||||||
|
- Run-trace git-tracked: `.claude/workflows/runs/2026-06-18-h11-{audit,implement,review}/` (FLAT, mỗi run có `*-synthesis.md`).
|
||||||
|
|
||||||
|
## Nấc thật theo từng PHẦN (trung thực, completeness-gate ĐẠT)
|
||||||
|
AUDIT xác nhận Harness-11 đúng như anh mô tả — phần lớn là chuẩn-hoá lại cái SE đã thể hiện một phần qua Harness-9 (L2 archive) và Harness-10 (run-trace, single-writer). Khoảng trống thật nằm ở ba chỗ, và SE đã lấp đủ:
|
||||||
|
|
||||||
|
- **PHẦN A** (hot-mem auto-archive, tailorable): A1–A3 đã có sẵn từ Harness-9. SE thêm mới A4 hysteresis (0.85), A5 keep-floor (5), A6 2-strike, A7 cổng NO-API L1-eval — gom vào `scripts/memory-archive-gate.ps1` (DRY-RUN planner) + params trong `memory-budget.json`. Runtime đã chạy: A7 resolve 186/186 con-trỏ, A4/A5 quan sát được trong DRY-RUN. A6 honest là executed-file (cần hai lần -Apply mới đủ runtime).
|
||||||
|
- **PHẦN B** (derived trỏ canonical, function-floor): đã chuyển 11 chỗ chép số dễ đổi (migration/test/gotcha/table count) trong các tài liệu dẫn xuất sang con-trỏ "→ docs/STATUS.md". Sau khi chuyển, chạy lại detector xác nhận ba flag drift thật của root CLAUDE.md (mig 53→55, test 306→339, gotcha 68→69) đã biến mất — bằng chứng B1 hoạt động.
|
||||||
|
- **PHẦN C** (ba detector grep, function-floor mandate): xây mới hoàn toàn `scripts/governance-detectors.ps1` (C1 con-trỏ-gãy + C2/B3 derived-staleness + C3 vocab-fork + C4 loại-trừ-dòng-tự-thân + C5 điều-kiện-gỡ-cờ). NO-API, chỉ DÒ và NÊU-CỜ, không tự sửa. Runtime: exit 0, C4 đạt 0 self-match, C5 mọi flag đều có resolve-condition.
|
||||||
|
- **PHẦN D** (engine điều phối): D1/D2 nay đã wire vào session-start (§2.1.3 chạy detector) và session-end (§L.b chạy archive-gate). D3/D4/D9/D10/D11 vốn đã mạnh sẵn (checkpoint THROW, store_memory strip, byte-0-loss). Ba tầng D5/D6/D7 và khoá-chiều một-chiều D8 nay được nhãn-hoá explicit trong một engine-doc canonical mới: `docs/governance/harness-11-engine.md` (các doc khác trỏ về đây, không chép luật — chính là B1 áp cho bản thân governance).
|
||||||
|
|
||||||
|
## Giá trị dogfood của REVIEW tách biệt (đúng như anh nhấn mạnh)
|
||||||
|
REVIEW workflow độc lập đã chạy thật cả hai script và phân loại từng flag tại nguồn, qua đó bắt được hai điểm mà IMPLEMENT tự chấm sẽ bỏ sót: (1) detector C2 có tỷ lệ báo nhầm cao tới ~89% raw do đếm count-token phẳng (nuốt phải "EF Core 10", "N bảng module", "154 test lịch sử"); (2) một con số "71 flag" bị viết cứng trong agents/README — đúng là cái anti-pattern hardcoded-volatile-count mà engine sinh ra để chống. SE đã fix cả hai ngay trước commit: thêm context-skip cho C2 (bỏ qua dòng bảng + tiền tố version + mốc lịch sử) và normalize hyphen↔underscore cho C1 → tổng flag giảm 59 xuống 27, sắc hơn hẳn; và đổi "71 flag" thành con-trỏ động. Đây là minh chứng mandate "review là workflow riêng" hoạt động đúng thiết kế.
|
||||||
|
|
||||||
|
## Reverse-findings (đề xuất ngược)
|
||||||
|
1. **Cảnh báo encoding cho detector non-ASCII (đề nghị bổ sung checklist PHẦN C):** detector PowerShell chạy `powershell.exe -File` decode file .ps1 UTF-8-không-BOM bằng codepage ANSI → mọi literal tiếng Việt bị mojibake → detector MÙ với token bản địa (vòng một thiếu 18/71 flag, gồm cả drift thật "68 bẫy"). Cách bền là dựng token tiếng Việt từ Unicode code-point ngay trong script, không inline literal. Đây có thể là bài học floor-class cho mọi sister làm việc với ngôn ngữ ngoài ASCII.
|
||||||
|
2. **Count-token grep là lưới mềm, tỷ lệ báo nhầm cao bản chất:** cần kèm context-skip (dòng bảng + tiền tố version/ordinal + mốc lịch sử) thì mới dùng được, nếu không sẽ gây mệt-mỏi-vì-báo-động-giả ngay. Đề nghị checklist PHẦN C ghi chú pattern giảm FP này.
|
||||||
|
3. **B1 và C2 là cặp bổ trợ:** sau khi B1 chuyển chép-số thành con-trỏ, C2 trên chính tài liệu đó thành no-op (không còn gì để so) — đây đúng là ý đồ: B1 gỡ nguồn drift, C2 chỉ còn gác các tài liệu chép-số MỚI. SE xác nhận thiết kế này khớp intent của anh.
|
||||||
|
|
||||||
|
## Honest caveats (không nói quá)
|
||||||
|
- Engine không có móc-nối hệ-điều-hành: detector và gate chạy trong thân session-start/end do em-main kích hoạt — việc DÒ thì tự động và toàn diện, việc SỬA và GÁC-CỔNG dựa trên người. SE không mô tả là tự-động-hoàn-toàn.
|
||||||
|
- Tự-động-ghi luật/copy: SE giữ đúng quyết định bảo thủ của anh — CỐ Ý chưa làm, mọi thứ chạm luật chỉ DÒ và NÊU-CỜ, người-chủ-trì soạn bản sửa.
|
||||||
|
- C2 còn ~11 flag báo nhầm dư (module-local "4 bảng Budget" lịch sử, "1 migration" trong câu lệnh revert) — chấp nhận như lưới mềm, đều là LOW/MED advisory, exit 0, không bao giờ chặn.
|
||||||
|
- C1 còn 13 wikilink gãy = drift THẬT có sẵn của memory-index (link thiếu file + link xuyên-scope) — engine nêu cờ đúng; việc sửa từng cái là chore riêng, không chặn adap.
|
||||||
|
|
||||||
|
## Double-check thêm hai vòng (anh chủ-trì yêu cầu)
|
||||||
|
Sau ba workflow trên, anh yêu cầu em chạy thêm hai vòng kiểm-tra độc-lập nữa trước khi chốt. Em xin báo cáo trung-thực:
|
||||||
|
- **Vòng double-check công việc** (`wf_a0b68d2f-30e`, 3× reviewer): xác nhận trạng-thái đã commit đúng — B1 đúng 11 chỗ, dòng lịch-sử migration giữ nguyên byte phần đuôi (không mất nội-dung), băm broadcast tính lại khớp, một-người-ghi sạch. Trọng-tâm là bắt regression của chính hai refinement em vừa áp ở vòng review. Một lane reviewer không trả kết-quả có cấu-trúc, nên em tự kiểm bằng cách tiêm một dòng drift giả ("99 migration") vào văn-bản thường rồi chạy detector — nó vẫn bắt đúng, chứng tỏ refinement không làm detector mù; hai lane còn lại độc-lập xác nhận cùng kết-luận (không che giấu drift thật nào).
|
||||||
|
- **Vòng kiểm checklist** (`wf_39cd4cbe-f07`, 3× investigator-codebase): rà từng mục checklist Harness-11 bằng bằng-chứng thật. Cổng-gác completeness ĐẠT — PHẦN B (4/4) + PHẦN C (5/5) + PHẦN D (11/11) đều hiện-diện đủ-trọn; PHẦN A tailorable. Hai điểm anh nhấn mạnh (ba tầng D5/D6/D7 nhãn-hoá rõ + khoá-chiều một-chiều D8) đều xác nhận có. Trung-thực: mục A6 hai-nhịp ở nấc executed-file, cần hai lần chạy -Apply mới đủ runtime — khoảng-trống có-chủ-đích của bản tailored.
|
||||||
|
- **Bài học mới:** không đưa ký-tự khung-cây (├└) vào file .ps1 — đó chính là gotcha #30 (PowerShell 5.1 đọc -File bằng codepage ANSI gây mojibake), kể cả khi nhập qua công-cụ-sửa-file (bị chuẩn-hoá escape thành ký-tự thật). Detector giữ thuần ASCII (quét Python xác nhận 0 ký-tự ngoài ASCII).
|
||||||
|
|
||||||
|
## Net
|
||||||
|
**0 dòng production code** — toàn bộ là governance/script/doc. State thật giữ nguyên: Mig 55 · 88 bảng · 339 test · gotcha 69 · menu 54 · bundle admin `BYF5vIMJ` / user `CB-tiRxd`. adap-report đầy đủ: `docs/governance/adap-reports/2026-06-18-Governance-harness-11.md`.
|
||||||
|
|
||||||
|
❓ Một câu hỏi gửi anh: nếu AI_INFRA muốn chuẩn hoá tham số PHẦN A (hysteresis 0.85 / keep-floor 5 / 2-strike) thành con số thống nhất giữa các sister thay vì để mỗi dự án tự tailor, xin anh báo — SE đang để mức hợp với khối lượng memory hiện tại của mình.
|
||||||
|
|
||||||
|
Trân trọng,
|
||||||
|
— se (SOLUTION_ERP)
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
id: 2026-06-18-se-to-ai_infra-harness-9-10-checklist-adopted
|
||||||
|
from: se
|
||||||
|
to: ai_infra
|
||||||
|
category: Governance
|
||||||
|
type: report
|
||||||
|
date: 2026-06-18
|
||||||
|
content_sha256: e5f09d57c22e43fa24b97822c41aa3f56ce475ed08420fac988d41410958b15d
|
||||||
|
nac: sent
|
||||||
|
---
|
||||||
|
|
||||||
|
# [SE → AI_INFRA] Báo cáo adopt — Checklist Harness-9 + Harness-10 (run-trace folder)
|
||||||
|
|
||||||
|
Chào AI_INFRA,
|
||||||
|
|
||||||
|
SOLUTION_ERP (se) đã áp dụng xong broadcast `2026-06-18-Governance-checklist-harness-9-10` trong phiên S71 hôm nay. Báo cáo này gửi đúng theo giao thức adap hai-workflow của Harness-9 (PART 2): kèm nấc trạng thái thật, các phát hiện riêng, và mã định danh ba lần chạy workflow làm bằng chứng.
|
||||||
|
|
||||||
|
## 1. Phạm vi đã áp
|
||||||
|
- **Part A (Harness-9 — tầng bộ nhớ archive):** đã adopt từ phiên S70 (file ngân-sách seed bằng số đo thật, `_INDEX.md` con-trỏ substring sha-keyed, `.gist.md` có counter thế-hệ, `.ragignore`). Phiên này em tự-verify lại — còn nguyên và đạt nấc. Một điểm tailor: bên em không có command nén-gist riêng (mục A8); việc curate làm thủ công qua workflow, nhưng vẫn giữ đúng chức-năng-sàn "nén gist tách biệt với build mục-lục".
|
||||||
|
- **Part B (Harness-9 — giao thức adap hai-workflow):** đã codify từ S70, phiên này dùng thật (ba workflow ở mục 2).
|
||||||
|
- **Part C (Harness-10 — run-trace folder):** MỚI áp phiên này — đây là phần chính.
|
||||||
|
|
||||||
|
## 2. Bằng chứng — ba workflow (mandate B3)
|
||||||
|
- INVESTIGATE: `wf_9c2cd2cd-2e7` (4 investigator đọc-thuần) — dựng kế hoạch migration.
|
||||||
|
- IMPLEMENT: `wf_e4e46725-231` (3 agent file-disjoint + em-main giữ cụm wording-critical) — áp thay đổi.
|
||||||
|
- REVIEW: `wf_636bc95b-939` (3 reviewer đối-kháng, ba lăng-kính độc lập) — kiểm tra lại.
|
||||||
|
|
||||||
|
Đã commit (`8c47bd0`) và push lên main. Ba thư-mục run-trace tracked đầu tiên cùng sổ-cái hai-nhịp chính là phần dogfood của Harness-10.
|
||||||
|
|
||||||
|
## 3. Nấc trạng thái thật (trung thực, không nói quá)
|
||||||
|
- Thư-mục run-trace TRACKED đủ ba phần (run.md + sub-md/ + harvest/) + sổ-cái hai-nhịp: executed-file và đã committed. Mục C3 đạt cả hai mức (check-ignore = NOT-IGNORED và `git ls-files` ra 14 file).
|
||||||
|
- Ba lớp chống bỏ-sót: L2 (quét mồ-côi lúc session-start) và L3 (cổng đóng idempotent lúc session-end) đã wire vào command; L1 (nhắc trong-run) là convention của lead @P1, KHÔNG phải engine-prompt (xem phát hiện số 1).
|
||||||
|
- hmw.js đã chuyển wave → run-trace: mã sạch, `node --check` PARSE-OK, nhưng runtime chưa chạy thật (engine không hot-reload nên cần restart; bên em ít dùng chế-độ này nên phần runtime mang tính forward-looking).
|
||||||
|
- Model containment đã chuyển từ Harness-2 B6 ("mọi tracked-change = vi-phạm", dựa trên wave-folder bị gitignore) sang Harness-10 ("tracked-change nằm ngoài run-folder và ngoài code-disjoint đã giao = vi-phạm"; run-folder nay được track nên hiện trong git-diff = audit trực-tiếp). Vẫn giữ G-015 không-nói-quá: TRACKED không đồng nghĩa read-only-enforced (sub vẫn giữ Bash); containment thật vẫn là em-main single-writer cộng git-diff cộng chunk-count.
|
||||||
|
|
||||||
|
## 4. Phát hiện riêng gửi ngược (know-how, B2.5)
|
||||||
|
1. **Layer-1 của C5 không thể là engine-prompt.** Workflow review độc lập (hai reviewer khác nhau cùng kết luận) bắt được một chỗ nói-quá: tài liệu ban đầu ghi L1 fire trong "hmw.js prompt-builder", nhưng engine không đọc được hệ-thống-file nên không thể kiểm "run trước đã harvest chưa". L1 bắt buộc phải là việc của lead lúc mở run. Đề xuất: checklist C5 nên ghi rõ L1 thuộc về lead, không phải workflow-prompt. Đây cũng là minh chứng sống cho giá trị của mandate B2 — một workflow vừa-làm-vừa-tự-chấm đã bỏ sót (bản implement không nhắc tới), chỉ workflow review riêng mới bắt được, và bắt được trước khi commit.
|
||||||
|
2. **Workflow tự-soạn (ngoài engine) thiếu return-delta-guard nên gây đua ghi cùng-vai.** Bốn investigator chạy song song cùng tự ghi vào file memory chung của vai, dẫn tới lỗi "file modified since read" và đẩy file quá ngưỡng. Engine hmw.js ở chế-độ mặc-định đã có guard return-delta-only, nhưng workflow tự-soạn thì không. Đề xuất: mandate B nên nhắc "workflow tự-soạn phải sao chép guard return-delta, không để sub tự ghi memory chung".
|
||||||
|
3. **Bẫy exit-code của `git check-ignore`.** Lệnh trả exit 0 cho cả pattern phủ-định (re-include) lẫn pattern ignore, nên nếu verify C3/C8 chỉ dựa exit-code sẽ kết luận ngược. Phải dùng `git check-ignore X && echo IGNORED || echo NOT`. Đề xuất đưa thẳng câu này vào phần tự-verify của C3.
|
||||||
|
4. **C3 hai-mức (đủ-điều-kiện-track so với đã-commit) là một bẫy thật.** Review bắt được `git ls-files` còn rỗng dù tài liệu đã ghi "tracked". Floor C3 của các anh tách đúng hai mức này — bên em xác nhận giá trị của nó qua thực-tế.
|
||||||
|
|
||||||
|
## 5. Caveat trung thực
|
||||||
|
- Bên em gần như không dùng workflow wave/run-mode (đã rút kinh nghiệm fan-out kém tin trong harness hiện tại, thường ưu tiên em-main tự gác), nên phần lớn machinery run-trace mang tính forward-looking. Dù vậy em vẫn áp đầy-đủ floor và dogfood để giữ đúng mandate.
|
||||||
|
- Còn pending việc restart CLI để hmw.js RUN-TRACE chạy thật ở runtime, kèm các mục carry từ S66/S70 (budget-audit §2.1.2, reviewer Category 6, H8 inherit).
|
||||||
|
- Hai file agent-memory (reviewer 33.8KB, investigator 29.8KB) đang quá ngưỡng do đúng cái đua ghi nói trên — nội dung hợp lệ, em để curate ở phiên sau.
|
||||||
|
|
||||||
|
Chi tiết đầy đủ ở adap-report `docs/governance/adap-reports/2026-06-18-Governance-checklist-harness-9-10.md` (các anh đọc cross-repo, read-only).
|
||||||
|
|
||||||
|
Cảm ơn các anh.
|
||||||
|
— se (SOLUTION_ERP)
|
||||||
@ -62,12 +62,12 @@ SOLUTION_ERP/
|
|||||||
│ ├── PROJECT-MAP.md bản đồ tổng quan
|
│ ├── PROJECT-MAP.md bản đồ tổng quan
|
||||||
│ ├── rules.md coding conventions
|
│ ├── rules.md coding conventions
|
||||||
│ ├── architecture.md layered + PE §9 + Budget §10 + Testing §11
|
│ ├── architecture.md layered + PE §9 + Budget §10 + Testing §11
|
||||||
│ ├── gotchas.md 58 pitfall đã gặp
|
│ ├── gotchas.md pitfall đã gặp (số → docs/STATUS.md)
|
||||||
│ ├── forms-spec.md 8 form catalog + RG-001
|
│ ├── forms-spec.md 8 form catalog + RG-001
|
||||||
│ ├── workflow-contract.md 9 phase HĐ + role matrix
|
│ ├── workflow-contract.md 9 phase HĐ + role matrix
|
||||||
│ ├── database/
|
│ ├── database/
|
||||||
│ │ ├── database-guide.md conventions + migration workflow
|
│ │ ├── database-guide.md conventions + migration workflow
|
||||||
│ │ └── schema-diagram.md ERD 93 bảng (+§11 PE +§12 Budget +§13 PEDeptOpinions +§14 Contract V2 LevelOpinions; §16+ Mig 27-48 pending)
|
│ │ └── schema-diagram.md ERD 88 bảng (+§11 PE +§12 ~~Budget~~ DROP +§13 PEDeptOpinions +§14 Contract V2 LevelOpinions; §16+ Mig 27-55 pending)
|
||||||
│ ├── flows/ 6 sequence diagram (auth/permission/contract/form/sla + PE ref architecture)
|
│ ├── flows/ 6 sequence diagram (auth/permission/contract/form/sla + PE ref architecture)
|
||||||
│ ├── guides/ setup, cicd, deploy, runbook, security
|
│ ├── guides/ setup, cicd, deploy, runbook, security
|
||||||
│ ├── changelog/
|
│ ├── changelog/
|
||||||
|
|||||||
200
docs/HANDOFF.md
200
docs/HANDOFF.md
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,85 @@
|
|||||||
|
# S68 (2026-06-16) — Hồ sơ NS header chi tiết NV: làm nổi bật + fix tên render đen→trắng
|
||||||
|
|
||||||
|
> em main solo · 3 commit (2 FE + 1 docs) · 2 deploy prod-verified Run #303-304 · HMW-mode ON · anh UAT realtime trên eoffice.
|
||||||
|
|
||||||
|
## Bối cảnh
|
||||||
|
|
||||||
|
Anh đang UAT trang **Hồ sơ Nhân sự** (master-detail, S65/S67) trên `eoffice.solutions.com.vn`. Yêu cầu xoay quanh **header banner chi tiết NV** (vùng gradient xanh chứa avatar + tên + dòng meta + badge trạng thái).
|
||||||
|
|
||||||
|
**Luồng:** `/session-start` → screenshot eoffice header NV *"sửa lại màu sắc/font các text trong khung đỏ cho nổi bật, màu đen + chữ in mảnh nhìn xấu"* → deploy #303 → screenshot *"cho chữ nhỏ lại, màu đen trên nền xanh ko nổi bật"* → fix #304 → `/session-end`.
|
||||||
|
|
||||||
|
## Bootstrap (`/session-start`)
|
||||||
|
|
||||||
|
2 monitor RE-REPORT (background, parallel) — đều **CLEAN**:
|
||||||
|
- 🟫 **H1 tooling-auditor:** roster **11/11 `model: inherit`** confirmed trên disk (Harness-8 runtime-landed, hết "executed-file pending-restart" ở mặt frontmatter) · ef-core skill FRESH (Mig 52, flush S66 giữ vững) · plugin 18/15/3 y nguyên. **Flag:** (1) stale `dependency-audit-erp/SKILL.md:153` "64 bẫy"→65; (2) **drift-correction**: curate-debt thực ra = **4 agent over-cap** (cicd 38.9 · inv-codebase 38.5 · **reviewer 35.4** · **impl-backend 30.7**), S67 chỉ flag 2.
|
||||||
|
- ⬜ **H2 harvest-curator:** **0 orphan · 0 corruption · 5-trục PASS 5/5**. Harvest S67 landed trọn (4 file, test-specialist proxy-record đúng). 0 wave-folder residual · 0 stray cwd-path · 0 zero-byte.
|
||||||
|
|
||||||
|
→ em main patch 2 doc-drift (`11bc96d`): dep-audit 64→65 + root `CLAUDE.md:66` module-Tests row 263→286 (45D+241I).
|
||||||
|
|
||||||
|
**RAG:** collection alive, `last_indexed 05-29` stale; chunk-stats cold-read 0 (artifact MCP vừa connect — không phải mất index; các project khác cũng 0).
|
||||||
|
|
||||||
|
## Done
|
||||||
|
|
||||||
|
### #303 `6983609` — header polish ×2 app SHA256 (`F013B748`)
|
||||||
|
File: `fe-{user,admin}/src/pages/hrm/EmployeesListPage.tsx` (block header dòng 643-676).
|
||||||
|
- **Tên NV** `<h2>`: `text-lg font-bold` → `text-xl font-extrabold` + `drop-shadow-sm`.
|
||||||
|
- **Dòng meta** (mã • phòng • vị trí): `text-xs text-white/85` → `text-[13px] font-medium text-white` (to + đục 100% + nét dày); mã NV `font-mono font-semibold`; separator `text-white/55`.
|
||||||
|
- **Badge trạng thái**: pill trắng-mờ `bg-white/20 text-[11px]` chìm → **pill đặc màu theo status** dùng `EmployeeStatusColor` (1 emerald / 2 amber / 3 slate) + chấm `bg-current opacity-70` + `shadow-sm`. Reuse token sẵn có, no new import (`cn` + `EmployeeStatusColor` đã import).
|
||||||
|
- Build ×2 PASS · SHA256 mirror khớp · cicd **Run #303 PASS** · bundle admin `D532XZKG` / user `CuFaBoWt`.
|
||||||
|
|
||||||
|
### #304 `37752eb` — fix tên render ĐEN→TRẮNG (gotcha #66)
|
||||||
|
Anh báo *"tên màu đen nền xanh ko nổi bật"* — screenshot cho thấy `<h2 text-white>` (tên) render **đen `#0b1220`** trong khi `<div text-white>` (meta cùng banner) trắng đúng.
|
||||||
|
|
||||||
|
**Diagnose:** `fe-user/src/index.css:79` có rule `h1,h2,h3,h4 { color:#0b1220 }` viết **NGOÀI mọi `@layer`** (thêm S65 #290). Tailwind v4: CSS unlayered có priority **cao hơn tất cả `@layer`** (kể cả utilities) → `text-white` THUA. `<div>` không bị rule element nhắm nên trắng đúng → nghịch lý meta-trắng/heading-đen.
|
||||||
|
|
||||||
|
**Quyết định fix:** đo blast-radius `grep '<h[1-4][^>]*text-'` → **~30+ heading toàn app** gắn `text-slate-700/900/brand-800` đang bị rule ép về `#0b1220`. Rule = **load-bearing** → KHÔNG được move vào `@layer base` (sẽ đổi màu loạt toàn app giữa UAT). → fix **ĐIỂM**: ép `text-white` → `text-white!` (important modifier Tailwind v4, trailing-bang). Verify bằng grep dist CSS: `.text-white\!{color:var(--color-white)!important}` ✓ → guaranteed thắng. Kèm thu nhỏ `text-xl → text-lg` + `font-extrabold → font-bold` (per "nhỏ lại").
|
||||||
|
- 1 dòng/file × 2 app · build ×2 PASS · SHA256 `8BBAEC34` · cicd **Run #304 PASS** · bundle admin `CNUv1jxY` / user `CpOskeS1`.
|
||||||
|
|
||||||
|
→ **gotcha #66 NEW** (Tailwind v4 unlayered element rule thắng utility — fix điểm `!`, không move @layer khi load-bearing).
|
||||||
|
|
||||||
|
## §L AUTO-MAINTAIN
|
||||||
|
|
||||||
|
- **§L.a AS-scan:** **0 production-bug RCA.** Iter-1 (#303 tên to+đen) KHÔNG phải defect mới — màu đen là **pre-existing** (rule heading ép mọi tên đen từ trước; anh báo "đen" ngay từ đầu = chính xác), iter-1 chỉ làm to nên lộ rõ; iter-2 fix root. Đây là UI iterate bình thường trên live UAT (authed page không screenshot dev-rig được — gotcha #3, visual gate = anh mắt thường qua prod). Guard mới = **gotcha #66**.
|
||||||
|
- **§L.b(a) summary-index:** STATUS Recently Done += S68. **(b) Active-Guards:** không guard mới ngoài gotcha #66. **(c) chore-flag:** 🔴 4 agent-memory > 30KB cap — cicd-monitor **44.1KB** (worst, +#303/#304), inv-codebase 38.5, reviewer 35.4, impl-backend 30.7 → curate next (P1). 0 zero-byte (AS-8 PASS).
|
||||||
|
- **§L.d flush + spawn-record (4-field):**
|
||||||
|
| agent | task | nấc | evidence |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 🟫 tooling-auditor (H1) | bootstrap RE-REPORT 4-mặt + diff S67 | verified-self | roster 11/11 inherit disk + 2 flag actioned (dep-audit 64→65, curate-debt 2→4) |
|
||||||
|
| ⬜ harvest-curator (H2) | bootstrap RE-REPORT harvest 5-trục | verified-self | 0-orphan/0-corruption/0-zero-byte PASS (S67 landed trọn) |
|
||||||
|
| 🟩 cicd-monitor | verify Run #303 + #304 deploy | verified-self | self-record ×2 (FIFO-trim S74→pointer); bundle rotate confirm both runs |
|
||||||
|
- cicd self-recorded (agent-memory). H1/H2 spawn-record ghi **tại session log này** = canonical (em main single-writer) — per-agent diary append SKIP có chủ đích: bootstrap re-report CLEAN, 0 learning-at-risk (khác gotcha #53 case nơi work sub có thể MẤT → bắt buộc proxy). Coverage vẫn ✓ (traceable đủ 3 sub).
|
||||||
|
- **§L.e pending-request audit:** anh-pending = (1) xác nhận mắt tên trắng+gọn ưng chưa; (2) Ops S58/S59 giữ nguyên (tzutil · email anh Chương · 5 staff password · gán CNTT). Đã log SPECIFICS ở HANDOFF NEXT.
|
||||||
|
- **§L.f harvest GATE:** Coverage ✓ (3 sub đều có spawn-record) · Completeness ✓ (4-field) · Placement ✓ (agent-memory đúng role, 0 stray) · Corruption ✓ (0 zero-byte, no mojibake) · Fidelity ✓ no-flag. **No wave-folder** (dùng Agent-tool spawn lẻ, KHÔNG Workflow fan-out — task focused → solo+spawn đúng, không cần HMW dù mode ON). GATE PASS.
|
||||||
|
- **§L.g tooling CHỐT:** roster KHÔNG đổi (11 all-inherit). Skill: dep-audit 64→65 đã patch (H1 flag resolved). Plugin: 0 new-alloc. Doc-drift còn defer-monthly: `docs/CLAUDE.md` full count + schema-diagram §16+.
|
||||||
|
|
||||||
|
## State (verified)
|
||||||
|
|
||||||
|
| Metric | Value |
|
||||||
|
|---|---|
|
||||||
|
| Migrations | 52 (unchanged — FE-only) |
|
||||||
|
| SQL tables | 88 (unchanged) |
|
||||||
|
| Tests | 286 (45D + 241I — unchanged, no test này session; UAT mode skip per-chunk, build ×2 giữ) |
|
||||||
|
| Gotchas | **66** (+#66 Tailwind v4 unlayered heading rule) |
|
||||||
|
| Menu keys | 53 |
|
||||||
|
| Bundle prod | admin **`CNUv1jxY`** · user **`CpOskeS1`** (Run #304) |
|
||||||
|
| RAG | last_indexed 05-29 (stale); chunk cold-read 0 (MCP connect artifact) |
|
||||||
|
|
||||||
|
## Commit
|
||||||
|
|
||||||
|
| SHA | Scope | Note |
|
||||||
|
|---|---|---|
|
||||||
|
| `11bc96d` | Docs | bootstrap drift-fix — dep-audit 64→65 + root CLAUDE test 263→286 (H1) |
|
||||||
|
| `6983609` | FE | header polish (tên to/đậm + meta đậm + badge màu) ×2 SHA256, Run #303 |
|
||||||
|
| `37752eb` | FE | fix tên đen→trắng (`text-white!` thắng rule unlayered) + thu nhỏ ×2 SHA256, Run #304 |
|
||||||
|
| (this) | Docs | session-end closeout — gotcha #66 + STATUS/HANDOFF/session log + harvest |
|
||||||
|
|
||||||
|
## NEXT
|
||||||
|
|
||||||
|
- **anh:** xác nhận mắt tên NV trắng + gọn — ưng chưa (nhỏ hơn → `text-base`; màu nhấn nếu cần).
|
||||||
|
- **🔴 em main P1:** curate L1 — cicd-monitor 44.1KB + inv-codebase 38.5 + reviewer 35.4 + impl-backend 30.7 (4 agent over-cap, defer nhiều session).
|
||||||
|
- **defer-monthly (07-01):** docs/CLAUDE.md full count-flush + schema-diagram §16+ Mig 32-52 ERD + STATUS/HANDOFF re-tier (header bloat).
|
||||||
|
- **Ops của anh:** tzutil VPS · anh Chương email typo · 5 real staff password · gán CNTT → lock nv.cao/nv.truong.
|
||||||
|
|
||||||
|
## INFRA-ADOPTION
|
||||||
|
|
||||||
|
N/A (no infra adoption this session — 2 monitor bootstrap re-report là vận hành thường, không adopt broadcast mới).
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
# S69 (2026-06-17) — Văn phòng số (E-Office) port + golive + PE cờ gấp/ngưỡng CCM
|
||||||
|
|
||||||
|
> 4 deploy prod-verified Run #305→#308. HMW-mode ON, 2 workflow fan-out (`office-puro-foundation` 5-agent + `office-puro-reskin-all` 8-agent) + nhiều spawn lẻ. Buổi sản phẩm CỰC LỚN.
|
||||||
|
|
||||||
|
**Anh: `/session-start` → "port Văn phòng số giống PURO NamGroup, CSS giống Hồ sơ NS" `/ultra-on` → (foundation) → "làm hết đi, PE golive rồi giờ còn Văn phòng số" (re-skin all) → "public Văn phòng số cho all user" (golive) → [screenshot Zalo anh Kiệt FDC: cờ gấp + CCM duyệt-final theo ngưỡng + chuyển phiếu→dự án] → "publish + session-end".**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## #305 `a8bbdae` — Văn phòng số foundation (workflow 5-agent)
|
||||||
|
- **Phát hiện then chốt:** SE ĐÃ CÓ module Office với feature map 1:1 PURO (Danh bạ/Phòng họp/Đề xuất/Đơn từ/Đặt xe/Ticket) → đây là **re-layout + re-skin**, không phải build mới. Anh chốt 2 nhánh: "Nền tảng trước" + "Giữ menu SE (không phẳng-hoá sidebar)".
|
||||||
|
- **Foundation:** sync `fe-admin/src/index.css` ← fe-user (đóng drift S66-S68: heading 600→700, ink #0f172a→#0b1220, label-eyebrow slate→brand-600 + gotcha #66 rule) → 2 app đồng bộ. + 3 shared component `PageHeader`/`KpiCard`/`WidgetCard` (×2 SHA256, tái dùng token Hồ sơ NS) + `OfficeDashboardPage` 2-cột widget kiểu PURO HomePage (reuse data hooks Đề xuất/Đơn từ/Ticket/Phòng họp, đếm client-side, no BE) + menu key `Off_Dashboard` (no-mig idempotent seed dưới Off, giữ ẩn).
|
||||||
|
- **gotcha #67 NEW:** em main fix `KpiCard` activeBorder `-300`→`-500` — accent palette (teal/violet/amberx/greenx) chỉ ship 50/100/500/600/700; `-300` trên teal/violet rơi DEFAULT Tailwind (sai-hệ), amberx/greenx drop hẳn. Build PASS cả 2 → reviewer soi dist CSS mới bắt.
|
||||||
|
- cicd Run #305: bundle admin `Bl2o_kUq`/user `BImrKQNn`, Off_Dashboard seeded, Office still hidden (admin-only).
|
||||||
|
|
||||||
|
## #306 `c556f6c` — re-skin TRỌN 10 page (workflow 7 designer song song + reviewer)
|
||||||
|
- **Phẫu thuật trình bày, giữ 100% logic:** Đề xuất(List/Create/Detail) + Đơn từ/Đặt xe(List/Detail) + Ticket CNTT + Danh bạ + Phòng họp(Calendar/Rooms) + Báo cáo CC(fe-admin) → PURO layout (PageHeader + KpiCard-filter row + card-accent) + CSS Hồ sơ NS. reviewer verify **mọi `api.*`/`queryKey` byte-identical HEAD vs working tree** (logic preserved).
|
||||||
|
- **Cơ chế chống parallel-build-interference:** 7 designer ghi SONG SONG fe-user → **cấm chạy npm build** (project cấm worktree do Windows MAX_PATH) → em main **build-tập-trung 1 lần** + mirror SHA256 sang fe-admin (9 page identical@HEAD verify trước cp) + build fe-admin.
|
||||||
|
- **gotcha #68 NEW:** harness bắn loạt diagnostic `× unused/Element` sau workflow — đều STALE mid-edit snapshot; build sau-cùng exit 0 cả 2 app. Bài học: agent nền → chỉ tin build SẠCH sau-cùng, bỏ qua diagnostic giữa-chừng.
|
||||||
|
- cicd Run #306: bundle admin `Wt54PHYl`/user `B99fMU6X`, 6 Office SPA route 200, Office vẫn ẩn.
|
||||||
|
|
||||||
|
## #307 `1f8947e` — Office golive public (+6 test)
|
||||||
|
- Anh: "public Văn phòng số cho all user eoffice". `SeedAllRolesOfficeModulePermissionsAsync` grant **read+create 16-key allow-list mọi role** (mirror đúng S65 HRM `SeedAllRolesHrmProfileReadPermissionsAsync` — chạy SAU `RevokeTemporarilyHiddenModulesAsync` để THẮNG; upgrade-only nâng false→true).
|
||||||
|
- **Allow-list 16:** Off + Dashboard + DanhBa + PhongHop(View/Book) + DeXuat(List/Create/Inbox) + DonTu(Leave/Ot/Travel) + DatXe + ItTicket.
|
||||||
|
- **Excluded (giữ ẩn):** Off_PhongHop_Manage (admin CRUD phòng) + Off_AttendanceReport (báo cáo riêng-tư) + Off_ChamCong (Cá nhân, golive riêng). HRM (trừ Hồ sơ NS) + Personal VẪN ẩn.
|
||||||
|
- **reviewer PASS security** (2 finding load-bearing): (1) cascade-safe — `Off` KHÔNG phải inherit-root trong GetMyMenuTree (chỉ Contracts/Workflows/PE/PeWf) → excluded-3 giữ false không lan; (2) no write-path opened — Office controller dùng class-`[Authorize]` self-service + per-action `[Authorize(Roles=Admin)]` cho admin-write → CanCreate chỉ mở menu+nút, API authz độc lập menu-key.
|
||||||
|
- **+6 test** `OfficeModulePermissionSeedTests`. cicd **DB-verify sqlcmd: 16/16 allow-list read+create=1 × 13 role**, excluded-3 = 0, HRM/Personal vẫn 0, admin không hạ. Bundle BE-only frozen.
|
||||||
|
|
||||||
|
## #308 `ebd7e1c` — PE cờ gấp PRO/CCM + CCM duyệt-final theo ngưỡng (Mig 53, anh Kiệt FDC, +14 test)
|
||||||
|
- Yêu cầu anh Kiệt sau họp sếp (screenshot Zalo). Anh chốt 4 quyết định (AskUserQuestion): ngưỡng cấu-hình-được · giá-trị = winnerQuoteTotal · cờ gấp visibility-only · nhận diện CCM/CEO theo role.
|
||||||
|
- **B — CCM duyệt-final theo ngưỡng giá trị:** ApprovalWorkflow +`CeoApprovalThreshold` (decimal? admin nhập Designer, **null = luồng tuyến tính cũ → rollout an toàn** dark-launch). `ApproveV2Async`: actor role `CostControl` (CCM) + `winnerQuoteTotal` (tổng ThanhTien NCC được chọn) < ngưỡng → `DaDuyet` luôn, bỏ CEO (guard chưa-ở-slot-cuối). Vai PRO=Procurement, CCM=CostControl, CEO=Director (mapping qua AppRoles).
|
||||||
|
- **A — cờ gấp per-vai (visibility-only):** PE +`IsUrgentByPro`(đỏ)/`IsUrgentByCcm`(xanh). Endpoint `PUT /purchase-evaluations/{id}/urgent` role-gated (Procurement→ByPro, CostControl→ByCcm, Admin→cả 2, khác→Forbidden) + notify CEO (Director) qua `INotificationService.NotifyManyAsync` khi MỚI bật (best-effort try/catch). Q3: KHÔNG đổi luồng.
|
||||||
|
- **FE ×2:** Workflow Designer ô "Ngưỡng giá trị gói CEO" (fe-admin) + PE detail cờ gấp toggle/badge theo role + hint "giá trị gói vs ngưỡng → CCM duyệt-final/cần CEO" + PE list badge.
|
||||||
|
- **+14 test:** `PeCcmThresholdFinalizeTests` 5 (load-bearing: CCM dưới ngưỡng → DaDuyet skip CEO; ≥ ngưỡng → lên CEO; null → linear; non-CCM → không finalize) + `PeUrgentToggleAuthzTests` 9. reviewer PASS security (cascade-safe + no-write-path). cicd Run #308: Mig 53 applied (3 cột, sys.tables=88), endpoint /urgent 401-not-404, bundle admin `BgNCjwsG`/user `CBvh0vtf`.
|
||||||
|
- **C (sau duyệt → chuyển phiếu đến dự án):** anh Kiệt "sẽ làm chi tiết với em để lên form" → DEFERRED, chờ spec.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## §L AUTO-MAINTAIN
|
||||||
|
- **AS scan / RCA:** 2 truncation #53 (impl-backend truncate giữa fix positional-record DTO → em main fix 2 CS7036 + tự viết endpoint cờ gấp agent chưa kịp; impl-frontend truncate → em main verify-complete-on-disk). 0 production-bug (cả 2 build PASS sau-cùng). Class quen #53 — recover-disk + self-gate, không cần RCA mới.
|
||||||
|
- **gotcha NEW ×2:** #67 (Tailwind accent palette thiếu-stop vỡ-màu-im-lặng) · #68 (stale-diagnostic-background-agent → chỉ tin build sau-cùng). → `feedback_stale_diagnostic_background_agent` (user memory).
|
||||||
|
- **H2 harvest GATE PASS** (1 non-block: PE-CCM-threshold logic test-covered [14 test] not standalone-reviewed — acceptable). 0 corruption/stray/wave. cicd #308 orphan → commit closeout.
|
||||||
|
- **H1 tooling CHỐT:** 0 new skill/plugin/roster (0 new-alloc). Count-flush must-fix: ef-core SKILL Mig 53 + skills/README + dep-audit + root CLAUDE + STATUS + HANDOFF (done closeout). docs/CLAUDE.md full + schema-diagram §16+ = monthly-defer.
|
||||||
|
- **curate-debt P1 (next session):** cicd-monitor **65.2KB** (worst, trend 41→54→56→61→62→65) + inv-codebase 47 + reviewer 43.5 + impl-be 33 over 30KB cap.
|
||||||
|
|
||||||
|
## State THẬT cuối S69
|
||||||
|
**Mig 53 · 88 bảng · 306 test (45D+261I) · 68 gotcha · menu 54 · bundle admin `BgNCjwsG`/user `CBvh0vtf` (Run #308).**
|
||||||
|
|
||||||
|
## 🔴 NEXT
|
||||||
|
- **Anh/anh Kiệt UAT:** (1) cấu hình "Ngưỡng giá trị gói CEO" Workflow Designer + test phiếu < / ≥ ngưỡng; (2) test cờ gấp PRO/CCM → badge + notify CEO; (3) **xác nhận** quy trình đặt CCM(CostControl)-trước-CEO + CEO = role Director (đích notify).
|
||||||
|
- **C** (chuyển phiếu→dự án) chờ anh Kiệt spec form.
|
||||||
|
- **Em:** curate cicd-monitor 65.2KB P1 · doc-flush docs/CLAUDE.md full + schema-diagram §16+ Mig 32-53 (monthly 2026-07-01).
|
||||||
|
- **Ops của anh (giữ S58/S59):** tzutil VPS · anh Chương email typo · 5 real staff password · gán CNTT lock nv.cao/nv.truong.
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
# Session S70 (2026-06-17→18) — Harness-9 adopt: L2 archive dark-matter recovery + adap 2-workflow mandate
|
||||||
|
|
||||||
|
> em main + 3 Workflow (12 agent) + 2 monitor bootstrap. **0 production code** (governance/memory only). Commit `f36aab8` pushed.
|
||||||
|
|
||||||
|
## Trigger
|
||||||
|
Anh: `/session-start` → `/check-email AI_INFRA và /adap-apply Harness-9` → chốt scope **"đầy-đủ-nhất, làm cẩn thận từng stage — investigate → implement → audit"** → `/session-end`.
|
||||||
|
|
||||||
|
## Bootstrap (/session-start S70)
|
||||||
|
- **2 monitor RE-REPORT:** 🟫 H1 — 3 skill cite stale (`dependency-audit-erp/SKILL.md:153`, `skills/README.md:20`+`:90`) + curate-debt 4 agent over-cap (cicd 63.6KB worst · inv-codebase 45.9 · reviewer 42.5 · impl-backend 32.4). ⬜ H2 — CLEAN, 0-orphan, 5-trục PASS (S69 fully harvested).
|
||||||
|
- RAG `proj_solution_erp` alive **2426 chunks** (stale-index 05-29). test **306 baseline verified** (45D+261I). HMW-mode ON. Lead Opus 4.8 (Fable suspended H5 fallback).
|
||||||
|
- `/check-email AI_INFRA`: `outbox/se` chỉ UI/UX guide (processed S58) → **0 thư mới directed**. Harness-9 ở `outbox/all` (kênh `/adap-apply`, S64 lesson).
|
||||||
|
|
||||||
|
## Harness-9 = 2 phần
|
||||||
|
- **PART 2/3 (🔴 function-floor):** mỗi adap = 2 workflow tách biệt (IMPLEMENT + REVIEW double-check) + REPORT-kèm-run-id; task ngắn-nhưng-cần-confirm VẪN cần review-workflow.
|
||||||
|
- **PART 1 (proposal, tailor tự do):** cứu L2 archive dark-matter — gist-additive + `_INDEX.md` TOC + budget seed-by-measure + budget-audit @session-start + command nén-gist tách build-index.
|
||||||
|
- PROJECT-FIT cao: SE có ~240KB archive dark-matter thật (4 over-cap sub), trùng đúng P1 curate-debt.
|
||||||
|
|
||||||
|
## Stage A — INVESTIGATE (`wf_be952f3c-97f`, 4× investigator-codebase)
|
||||||
|
Recon 4 archive → build-plan. **Quyết định pointer = substring sha-keyed** (4 agent đồng thuận độc-lập): anchor-slug 404 dễ (slug-collision same-date; reviewer q3 hai `## 2026-05-22` identical; cicd `2026-06.md` ZERO heading = bullet; Vietnamese-diacritic/em-dash slug-fragile). git-sha 7-char = immutable + grep-unique. KHÔNG line-hint (additive append shift dòng). + per-agent moveToArchive list + importantMarkers checklist (83 total, làm gate Stage C) + risks.
|
||||||
|
|
||||||
|
## Stage B — IMPLEMENT (`wf_a58e0d15-beb`, 4× general-purpose, file-disjoint)
|
||||||
|
Mỗi sub: curate L1→L2 byte-exact → `_INDEX.md` → `.gist.md` (theo thứ tự, để index/gist phủ trạng-thái cuối).
|
||||||
|
|
||||||
|
| sub | L1 trước→sau | moved | markers | `_INDEX` ptr |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| cicd-monitor | 65→**23.2KB** | 10 | 22/22 | 76/76 |
|
||||||
|
| investigator-codebase | 47→**24.1KB** | 15 (tạo `2026-05-q4.md` fresh — was git-only d2f52ba) | 22/22 | 37/37 |
|
||||||
|
| reviewer | 43.5→**24.8KB** | 9 | 19/19 | 19/19 |
|
||||||
|
| implementer-backend | 33→**17.4KB** | 14 | 20/20 | 41/41 |
|
||||||
|
|
||||||
|
Cả 4 <25KB auto-inject cap → **P1 curate-debt CLOSED**, ~240KB archive hết RAG-dark.
|
||||||
|
**Em-main shared (single-writer, ngoài workflow):** `memory-budget.json` (seed-by-measure — cicd `_INDEX` 16.8KB largest = **82% of 20KB cap, WATCH-agent**) + `scripts/measure-agent-memory.ps1` + `.ragignore` + codify PART 2/3 (`adap-apply.md` mandate + `agents/README` Upgrade S70 + `session-start §2.1.2` budget-audit).
|
||||||
|
|
||||||
|
## Stage C — AUDIT (`wf_9520d8cd-4fe`, 4× reviewer, independent)
|
||||||
|
- reviewer **PASS** (50/50 markers, 19/19 ptr, 0-byte airtight) · implementer-backend **CONCERN** (read-side gap `memoryPointsToIndex:false` + stale curate-log).
|
||||||
|
- **2 (cicd + inv-codebase) no-StructuredOutput** (after 2 nudges) → em-main **self-gate** via git/sha/grep (recovery-path `feedback_agent_kill_recovery`).
|
||||||
|
- **4/4 0-byte-loss PASS:** git numstat `+N -0` (additive, 0 deletion trên verbatim modified) + frozen `2026-05-*` sha256 IDENTICAL + content-identity (inv-codebase `2026-06.md` rebuilt → CR-normalize prefix byte-exact; git autocrlf normalizes on commit).
|
||||||
|
|
||||||
|
## Findings fixed before commit
|
||||||
|
- **Read-side gap (3/4):** cicd/inv-codebase/impl-backend `MEMORY.md` không trỏ `_INDEX.md` → agent spawn không biết đường tra map (read-side mechanism inert). Fixed L5 header → `_INDEX.md` + `2026-06.md` + gist. (reviewer đã sẵn.) **Stage-C độc-lập bắt = giá trị review-workflow riêng** (implement tự-chấm bỏ sót) — đúng lý-do PART 2 tách 2 workflow.
|
||||||
|
|
||||||
|
## §L closeout
|
||||||
|
- **doc-drift 4 flush (H1):** `dep-audit:153` 65→68 · `skills/README:20` 52→53 (+Mig 52→53 + label S65→S69) · `:90` 65→68 · `session-start:44` (58)→(68).
|
||||||
|
- **4 curate-log refresh S70** (was stale S40/S66/S69).
|
||||||
|
- **H2 GATE CONDITIONAL PASS:** Corruption/Placement/Fidelity 🟢 (md5-proven 0-loss, 0 stray cwd, no-fabrication); Coverage/Completeness 🟡 (spawn-record fill via curate-log + 3 `S?`-label cosmetic worker-artifact trong impl-backend `2026-06.md:1`/`_INDEX:51`/`MEMORY:77` — content intact, label-only).
|
||||||
|
- **Wave-folder:** n/a (Workflow tool direct, không wave-mode → 0 `.claude/workflows/wave-*/`).
|
||||||
|
|
||||||
|
## Phase 6 EVAL — infra-adoption (§6.3)
|
||||||
|
Harness-9 = infra/governance adoption. **adap-report** `docs/governance/adap-reports/2026-06-17-Governance-harness-9-l2-recovery-and-adap-workflow.md` (G-011 = executed-file + VERIFIED). **email-back** `broadcasts/outbox/ai_infra/2026-06-17-se-to-ai_infra-harness-9-adopt-report.md` (body-hash `7c07b716e775`) — 3 đề-xuất ngược: (1) substring-pointer-keyed-on-sha cho archive đời-thực · (2) measure-then-seed reference impl · (3) self-gate-fallback hợp-lệ cho review-workflow khi auditor không return. SERVER-VERIFIABLE: commit `f36aab8` + closeout commit origin synced.
|
||||||
|
|
||||||
|
## State (UNCHANGED — 0 production code)
|
||||||
|
**Mig 53 · 88 bảng · 306 test · 68 gotcha · menu 54 · bundle admin `BgNCjwsG`/user `CBvh0vtf`.**
|
||||||
|
|
||||||
|
## Lessons
|
||||||
|
- **substring-pointer > anchor-slug** cho archive trộn heading-level + same-date + diacritic — git-sha là khoá ổn-định nhất.
|
||||||
|
- **REVIEW-workflow riêng có giá-trị thật** (bắt read-side gap implement bỏ sót) — PART 2 dogfood đúng trên chính nó.
|
||||||
|
- **self-gate-fallback** khi reviewer no-StructuredOutput (#53 family) = nhánh hợp-lệ, KHÔNG FAIL adopt. → memory `feedback_harness9_l2_recovery_2workflow_adap`.
|
||||||
|
|
||||||
|
## NEXT
|
||||||
|
- ⚠️ **Restart CLI** (đầu việc): session-start §2.1.2 budget-audit + (tồn) H8 all-inherit runtime + reviewer Category 6.
|
||||||
|
- **Budget-audit @session-start (mới):** cicd `_INDEX` 16.8KB/20KB cap (82% — gist-of-index/split khi vượt, đừng drop marker).
|
||||||
|
- **Pending product carry S69:** CEO-threshold Designer + cờ gấp PE test; "C" chờ spec. Ops S58/S59 (tzutil · anh Chương email · 5 real-staff pw · gán CNTT lock).
|
||||||
|
- **Monthly audit 2026-07-01:** STATUS/HANDOFF re-tier (trim S67↓→logs, defer ×9) · docs/CLAUDE deep-doc count-flush (gotcha 58→68 · 93→88 bảng · Budget-removed) + schema-diagram §16+ Mig 32-53 ERD · rollout Harness-9 `_INDEX`/gist cho sub khác KHI archive lớn lên.
|
||||||
|
- **Cert** `api.solutions.com.vn` ~2026-07-23 (auto-renew ~06-23).
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user