Files
solution-erp/.claude/agent-memory/reviewer/MEMORY.md
2026-07-29 20:06:51 +07:00

102 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Reviewer Agent — Persistent Memory
- **[→ archive/2026-07.md @S161-curate] S159 đợt-5 (07-29) menu 6-leaf HĐ + `/contracts/deleted` (PWF 0H/4M/6L):** chú-thích-mang-SỐ phải chạy phép đếm (comment "2 chỗ IgnoreQueryFilters" — thật 3 file/17 site) · cookie-cutter phải soi CẢ phần bản gốc đã CHẶN (thiếu `disabled={deletedView}` ⇒ panel trắng) · kiểm hide-filter TRƯỚC khi kêu thiếu mirror 2-app · `tsc` + `noUnusedLocals` mạnh hơn grep cho claim "đã cắt".
- **S161 (07-29) W1 KHKK tiền-commit (Mig 69 + seeder + Designer type-10) — PWF 10 FLAG 1H/3M/4m/2L:** 🔴 **HIGH lại nằm ở `git` chứ không ở mã (S155 tái diễn):** 2/3 file Mig 69 UNTRACKED `??` mà snapshot đã tracked-M ⇒ `commit -a` nạp model 7 bảng nhưng BỎ migration; build+566 test vẫn xanh ⇒ **gate = `git status --porcelain -- src tests | grep '^??'` phải RỖNG ngay trước commit**. · **MAJOR bắt được nhờ đi NGƯỢC lời-khai FE:** comment FE tự khai "nhãn lấy từ BE `applicableTypeLabel`" ⇒ đi kiểm bờ BE: `AwLabels.Type` chỉ 3 entry, `GetValueOrDefault(type, type.ToString())` ⇒ UI hiện **"ContractSigningPlan"** tiếng Anh và `useState(\`Quy trình ${label}\`)` **GHI chuỗi Anh vào cột Name DB** — lớp *hợp-đồng-đứt-2-bờ*, tsc+build+test đều xanh. **Luật rút: câu comment dạng "bên kia lo" = ĐI ĐỌC BÊN KIA.** · seeder 5/5 PASS (call sau revoke `:2109`>`:2096`; revoker chỉ `Hrm|Off|Personal` ⇒ không lật 2-chiều; `TryGetValue` tuple `(RoleId,MenuKey)` — khuôn 1-role sẽ ném dup-key, unique-index `PermissionConfiguration:26` cứu) nhưng **acceptance "đo CỜ" chưa đo được ở đâu**: Dev DB **0 row** `Khkk*` cả MenuItems lẫn Permissions (menus 100 vs prod 142) ⇒ chưa nhánh nào từng chạy. · **`grep`-cùng-lớp bằng TOKEN-SONG-SINH** (`VehicleBookingLevelOpinions` repo-wide) mạnh hơn grep tên-mới: chứng 3/3 site đủ + 0 site thứ 4. · 2 chú thích cùng file nay SAI SỐ ("7 bảng"/"7 khối" → 8) + comment nguồn `MenuKeys.cs:37` "CỐ Ý NGOÀI All" nay sai + anchor `(:614)` trỏ dòng TRƯỚC-commit (đúng = :645) ⇒ **diff sửa mã phải sửa MỌI số/neo trong chú thích cùng file**. · STATUS.md `Menu keys 54`/`Policies 216` không đổi dù |All|=55 ⇒ derived-drift. Tag `[s161, high-o-git-khong-o-ma, hop-dong-dut-2-bo-nhan-BE, token-song-sinh-sweep, seeder-upgrade-if-exists, comment-mang-so-va-neo]`
- **[→ archive/2026-07.md @S159-curate] S155-đợt2 (07-27) PE delete-approver DIFF-review #2 (12 FLAG 2H/4M/6L · ĐỪNG-DEPLOY):** class **`hợp-đồng-đứt-giữa-2-bờ`** (FE type khai cờ, BE DTO không có ⇒ nút chết mà tsc+build+561-test đều xanh) · policy-gate đo DB 11/13 vai `CanDelete=0` · mã ĐỔI giữa lượt soi ⇒ re-đo đĩa TRƯỚC verdict · đổi-chiều-assert ≠ nới-assert.
- **[→ archive/2026-07.md @S155-curate] S155 ×2 (07-27) PE delete-approver — DIFF-review đợt 1 tiền-deploy (14 FLAG 4H/6M/3L/1I, DEPLOY-CÓ-RỦI-RO) + SPEC-review (PWF 4H/8M/5L):** 🔴 class MỚI **`HIGH nằm ở git chứ không ở mã`** — 2/3 file migration UNTRACKED mà snapshot ĐÃ tracked ⇒ `commit -a` nạp model-có-cột nhưng bỏ migration ⇒ `Invalid column name` cả 7 module, build+test vẫn xanh ⇒ **gate deploy phải chấm `git status ^??` cho `Migrations/`** · `ExecuteDelete` ngoài transaction TRƯỚC `SaveChanges` = mất dữ liệu không hoàn tác · purge xoá CỨNG chữ ký phiếu XOÁ MỀM ⇒ **test đang KHOÁ chiều ngược, vá xong test đỏ ≠ hồi quy, CẤM nới assert** · ~600 dòng BE = mã chết (`grep api.put`=0) · **xanh-giả bắt bằng phép 2-thế-giới** (còn-gate ⟂ bỏ-gate cùng cho 0 ⇒ 0 thông tin) · CLEAN chứng bằng **liệt kê vét cạn** write-site + sweep gián-tiếp (#81-EXT) · 2 lỗi TỰ BẮT: trích line-number **từ diff** (diff không mang số dòng file) · `grep -v Snapshot` ăn nhầm tên migration · **file bị sửa TRONG LÚC soi ⇒ neo bằng NỘI DUNG DÒNG + ghi bản-đồ-trôi**.
- **[→ archive/2026-07.md @S155-curate] S146·S147·S152-D2 (digest):** S146 lead sửa 1 phép quét 4 lần/phiên vì mã-hoá ý-định NGỮ-NGHĨA vào regex ⇒ nấc đúng = lưới-soát-cho-người + allowlist CÓ TÊN + use⟂mention · S147 **decouple 1 gate ⇒ truy xem prop dùng chung đã mang carve-out chưa** (`itemsReadOnly` ≠ raw `readOnly`); "same fix" cho surface anh-em có thể trúng gate KHÁC · S152-D2 census thay spot 307/307; **3 THƯỚC-HỎNG** (md5 disk vs `git show` = FAIL giả do CRLF⟂LF · `grep -c` dính citation-trap · `grep -iF` MSYS trả 0-hit IM LẶNG trên UTF-8) + máy-trích mù đuôi `{.yml,.json,.sql}`.
- **[→ archive/2026-07.md @S145-curate] S145 4-lane (Axis-A/D/E + S145b GATE) — digest:** bất-biến chia-đôi giữa 2 axis nằm ĐÚNG SEAM ⇒ chỉ cross-cut sweep bắt (hook 3→4 path orphan; `session_ctx_kb` ghost-wire #H18) · 'vai-có-sổ' acceptance ⇒ MUST `ls agent-memory/<role>/` (folder≠sổ, ring1-audit RỖNG) · stale-fix phải verify caller-TRÊN-ĐĨA — grep-"4 surface"=0-hit là ANTI-TEETH Goodhart; 2-script-same-suffix = conflation-vector · đếm VALID_ROLES = per-file grep bidirectional KHÔNG regex (comment lừa parser).
- **[→ archive/2026-07.md @S145b-curate] S145a·S143 (digest, archived):** S145a NUMERIC "roster 17" hardcode trốn name-enum sweep ⇒ sweep repo-wide CẢ numeric LẪN name-enum (superseded S145b PASS) · S143 CLASS `bằng-chứng-tự-huỷ-sau-squash` (cite outward = commit SỐNG-sau-squash) + số neo-mốc + errata CẤM sửa file đã-stamp + #53 ghi-đĩa-trong-lúc-làm.
- **[→ archive/2026-07.md @S140/@S134] S139·S134b·S133 (digest, archived):** S139 re-measure MỌI acceptance từ git HEAD + no-self-exempt (chấm cả file CHÍNH VAI MÌNH); "measured" sống CHỈ KHI falsify-path CHẠY ∧ đứng · S134b insertion-only + `grep '^-'`=0 chắc hơn diff-mắt · S133 "mọi nhánh set X" phải grep CẢ assignment-qua-BIẾN (#81).
> **Persistent diary cross-session.** Auto-injected first ~200 lines at spawn (L1 HOT).
> Update BEFORE every stop. Tiered Memory v1: L1 HOT cap ~17KB (hook 24.4KB read-limit) · 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`; S51→S76 detail → `archive/2026-06.md` (S69/S70/S71/**S80** curate). Archive map: `archive/_INDEX.md` + per-period `.gist.md`.
- **[→ archive/2026-07.md @S161-curate] S160 (07-29) CHỐT-CUỐI verify 13-fix landed (12 landed + 1 half-fix):** fix áp NỬA spec = dư-lượng (soi CẢ FILE khi nhận "đã fix") · anchor-list là artifact RIÊNG phải sync khi thân đổi · dải anchor phải grep CẢ 2 symbol · verify NGƯỢC premise clone 3/3 đứng · owner để-mở ⇒ finding MOOT-đú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`.
- [Wire/mirror claim verification anchors](feedback_wire_claim_verification_anchors.md) — sha256 twin-file · `git diff -U0` isolate true-adds · `allowNegative` bleed check · guard-still-intact grep.
- **[→ archive/2026-07.md @S161-curate] S160 (07-29) review SPEC-BỘ dry-run KHKK (6 lane, 5/5 SUA-TRUOC-THI-CONG):** review SPEC ≠ review CODE — lỗ ở acceptance + default-lấn-quyền, không chỉ anchor · seeder-grant kiểu insert = NO-OP 403 im lặng ⇒ acceptance phải đo CỜ không đếm ROW · owner-authority surface AskUser thay tự quyết · acceptance "PASS SẴN" = 0-bit · FK-547 compiler-không-chặn · choke-point "DUY NHẤT" sai vì gán QUA BIẾN (#81).
- **S89 trio** ([sectionB](project_s89_pe_sectionb_review.md) · [endsBeforeCeo](project_s89_pe_endsbeforeceo_plan_review.md) · [finalize-note](project_s89_finalize_note_plan_review.md)) — adjustMut share race + authz-lens ≠ concurrency-lens · FE 4 render-sites plan-missed-2 · ground-truth-accurate-nhưng-gaps.
- **S101 H18/H19 WF2 + LANE-C — PWC/PASS:** content-hash ≠ commit-SHA (hex strip/no-strip) · exact-token sweep misses paraphrase → widen concept-phrase. → `archive/2026-07.md`.
- [S103 H20+crystallized-backfill review](project_s103_h20_crystallized_backfill_review.md) — WF2 PASS; 6/6 hash recompute MATCH + 4 already-met verified. Lesson: multi-axis warning phải nằm trong OUTPUT của script (reader chạy inline), không chỉ doc.
- [S111 PE sign-off 6-decision design review (schema/history lens)](project_pe_signoff_history_schema_review.md) — GO-WITH-ADJ; D5 opinion-history: append-only table, REJECT version-col (drops UNIQUE + breaks UPSERT).
- **PE per-hạng-mục series ×5** ([cardinality#3](project_pe_multiwinner_perhangmuc_review.md) · [BE-financial](project_s114_pe_multi_ncc_perhangmuc_financial_review.md) · [spec-l3](project_pe_multi_ncc_perhangmuc_specreview.md) · [IMPL](project_s114_multi_ncc_perhangmuc_impl_review.md) · [neg-quote](project_pe_negative_quote_correctness_review.md)) — derived src-of-truth: re-derive MỌI write-path KỂ CẢ Delete · grep field-NGUỒN không chỉ field-đọc · sign-loss: defer-parse tới field-chung mọi mutation.
- [S113 Supplier import v2 close-review (owner anh Kiệt)](project_s113_supplier_import_v2_review.md) — GO-WITH-ADJ 2 must-fix (consumers không pass published=true → drafts leak). Lesson: "filter added" ≠ "drafts hidden" — grep-all-consumer + create write-path.
- [S118 Procurement master-access seeder review](project_s118_procurement_master_access_review.md) — PASS(cond); MAJOR Reports.Read leaks financials + Suppliers.Update grant ≠ edit. Lesson: menu-flag grant ≠ API capability; bare `[Authorize]` GET = open-to-all.
- [S123 governance 4-change review (backtick-guard + H24-3 + retire dạng-3 + mark)](project_s123_governance_4change_review.md) — PASS_WITH_FIXES; CAUGHT H24-3 thiếu enclosure-guard mà cùng diff vừa chứng cần (gen-2 citation-trap) + greedy `.*` lấy `(S<N>` CUỐI≠MAX + lý-do-retire áp cho dạng-2 thì giết C8. Q5 "HOÀN-THÀNH mark" = nguỵ-biện. Q8 ENDORSE không-bump.
- [S147 Project whitespace-dupe merge verify](project_project_dupe_merge_verify.md) — 7/7 PASS(2 FK-terminology corrections); "FK Projects" COMMENT ≠ physical FK (sys.foreign_keys=0 to Projects, all loose-Guid); seed-durability FORCES survivor=no-space blanket; INNER-join+filter=silent-vanish→repoint-before-soft-delete; prod ref-dist unverifiable (Server=localhost+secret).
---
## 🎯 Role baseline
Adversarial pre-commit reviewer SOLUTION_ERP. Read-only verify + live curl prod UAT (`*.solutions.com.vn`). Tools: Read, Grep, Glob, Bash (curl + git diff + sqlcmd read) + 5 RAG MCP. Skills: `dependency-audit-erp` + `contract-workflow` + `permission-matrix`. Output: PASS/FAIL + concrete issues file:line. NEVER write code.
---
## 🚨 Recurring bug patterns (catch priority)
- **#44 Silent 403 class-level Authorize quá strict** — Drafter dropdown empty silent (TanStack catch silent → UI empty). Grep `\[Authorize\(Policy=.*\)\]` class-level + curl non-admin expect 200. Fix: class-level `[Authorize]` only (any authenticated); POST/PUT/DELETE giữ `[Authorize(Policy="X.Create")]`.
- **#43 Step.Order ≠ index 0-based** — `Where(s=>s.Order==i)` wrong row. Fix: EF query → in-memory `OrderBy(Order).ToList()` → index.
- **#42 Dual schema V1/V2 — Service phải branch** — `if (entity.ApprovalWorkflowId is Guid awId) ApproveV2Async else V1Legacy`.
- **Wire BE claim** — grep diff `// Mock`/`alert(`/no POST-PUT-DELETE call + live curl expect 2XX. Severity CRITICAL block.
- **#70 FE absolute-set stale-echo race** — N fields cùng-cột share 1 mutation + echo sibling từ server-snapshot (`bs`) + `invalidate()` fire-and-forget (không await) → lưu 2 ô liên-tiếp đè mất. Window mở SAU `isPending=false` (btn re-enable), KHÔNG lúc in-flight. Catch: đếm field-cùng-cột share mutation + check invalidate awaited; fix = `useIsFetching` gate nút Lưu tới khi refetch land.
- **#71 enum proxy-predicate pollution** — thêm enum value vào entity dùng-chung → UI/guard phân-loại theo PROXY-predicate (`supplierId===null`) thay vì enum tường minh → value mới lẫn nhầm phân-loại + false-pass guard. Catch: grep mọi field-proxy predicate, loại value mới tường minh; build-verify TỪNG app.
- **Cross-module security mirror (S29 Smart Friend)** — khi mirror entity/Command cross-module (PE→Contract→Budget V2), focus data-shape MISS security guard. Pattern: `aw.ApplicableType == ExpectedType` validate ON Create BEFORE instantiation (mirror `PurchaseEvaluationFeatures.cs:62-77`). Attack: Drafter forge POST với `approvalWorkflowId` của module khác → FK Restrict chỉ check Id-existence NOT ApplicableType → wrong-scope pin. Re-verify `IsActive`+`IsUserSelectable` server-side. Password ≥12 chars. Severity MAJOR.
- **#17 EF migration 3-file** — `git diff --name-only | grep Migrations/` expect 3 (target + Designer + Snapshot).
---
## 📋 6-category checklist (EVERY review)
- **Cat 1 Wire BE/feature claim:** grep mock markers diff + `await api\.(post|put|delete|patch)\(` + live curl POST/PUT/DELETE if deploy claim + status matrix.
- **Cat 2 Schema integrity:** 3-file rule Mig + column types vs entity def. Reference `docs/gotchas.md` (71 active).
- **Cat 3 Security:** `[Authorize]` class-level ALL new controllers + per-action policy admin-scoped (gotcha #44) + FE PermissionGuard + menuKeys.ts mirror BE MenuKeys.cs + FluentValidation + EF parameterized.
- **Cat 4 Code quality:** `dotnet build SolutionErp.slnx` 0 err + `npm run build` × 2 app (TS6 strict) + tests baseline **354 PASS** (Phase 9 UAT exception OK) + no `--no-verify` + anti-fiddle (scope drift >20% LOC = FAIL) + mirror 2 FE app §3.9.
- **Cat 5 Test coverage:** new helper → xUnit · new endpoint → integration · bug → regression test-before-fix. Phase 9 UAT test-after default OK (`feedback_uat_skip_verify`). Baseline 354.
- **Cat 6 Writing-quality (Harness-7, S64):** outward text (verdict gửi anh / report) = tiếng Việt câu hoàn-chỉnh đủ dấu đúng ngữ-pháp; describe issue + acceptance criteria, NOT code edits. Escalate disagreement explicit.
---
## ⚠️ Anti-patterns + 🛡️ Smart Friend guard
1. ❌ Recommend code edits (only describe issue+criteria) · 2. ❌ Skip live curl if deploy claim · 3. ❌ Accept "wire" without grep proof · 4. ❌ Defer to em main authority (escalate explicit) · 5. ❌ Skip MEMORY · 6. ❌ **Lower bar match em main** (Smart Friend Cognition anti-pattern).
**Smart Friend (Cognition):** NEVER lower bar. Em main code fine → PASS. Em main issues → FAIL with specifics regardless social pressure. "Quality ceiling set by primary, not escalation." Value = raise quality through catch.
---
## 🧠 SOLUTION_ERP review essentials (S80 verified — re-ground từ docs/STATUS.md canonical)
- **Tests · gotchas · migration · endpoint · bảng = SỐ CANONICAL ở `docs/STATUS.md`** (B1 derived-trỏ-canonical — KHÔNG chép số vào sổ này, chép là stale sau 1 phiên; mốc @S161 để nhận diện độ trôi: test 566 · gotcha 83 · Mig 69 · 96 bảng). Test phải TĂNG khi thêm feature (§7); Phase 9 UAT exception (`feedback_uat_skip_verify`).
- **Identity password ≥12 chars** (reject 11-char). Test creds: admin `admin@solutions.com.vn/Admin@123456` (full) · UAT `nv.test@solutions.com.vn/TestUser@123456` (Drafter CCM).
- **Prod:** api/admin/eoffice.solutions.com.vn. Bundle live admin `CsJetgZH` / user `BVS0ApIm` (Run #330). **Pin:** MediatR `12.4.1` (flag `Version="14`) · Swashbuckle `6.9.0` · Node CI `20.x`.
- **Conventions:** `docs/rules.md` (§1.1 outward writing, §3.9 mirror 2 FE, §5.2 commit, §6.5 docs narrative, §7 test timing, §2.8 pin).
---
## 📅 Recent activity (compressed — full verbatim → `archive/2026-06.md` + `archive/2026-07.md` via `archive/_INDEX.md`)
- **[→ archive/2026-07.md @S126/@S134] S123·S124·S125·S129·S131·S131b digest (all archived):** S131b follow-up phải GIỮ con-số source · S131 số-atomic-đúng vẫn fencepost → hero-ratio re-derive từ raw ([topic](project_s131_h24_h22_3gap_outward_gate.md)) · S129 writer-only = NỬA-VÁ + quick-test tự-chạy = chứng rẻ nhất · S124/125 do-token trap → re-Read full + sibling-test 2-CHIỀU (MATCH⇒file-bệnh·FAIL⇒verifier-bệnh) · S123 🔴 #53 ghi diary TRƯỚC return + vá-1-lớp grep MỌI matcher cùng-lớp ([topic](project_s123_governance_4change_review.md)).
- **Digest S93→S117 (verbatim → `archive/2026-07.md`; 1 mệnh-đề/entry):** S93 code-gate re-derived denom · S97 default-flip byte-mirror SHA ×2 · S98 Windows byte-verify = .NET (MSYS strip
báo sai) · S100 HELD stale run-id NOT-stamped · S101 persist-claim cần reader-side · S108 outward-claim "đã sửa X" phải cat X trước gate · S109 gist meta-count ≠ disk · S110 claim-về-code grep lại kể cả reword · S111 nâng-nấc-quên-sync cùng-diff · S115 verify EACH regex-alt by-hand · S116 persist-claim = tracked + 0-drift · S117 2-tier guard compare SUM-EXPRESSION.
- **S92 PROD-security hide 5 menu-groups admin-only — PASS (1 note):** → [project_s92_admin_only_modules_revoke.md](project_s92_admin_only_modules_revoke.md).
- **June-2026 digest — LESSON-ONLY (verbatim → `archive/2026-06.md` + `_INDEX`):** merge-distill fabricates false-specificity ⇒ QUALITY gate AFTER presence · sha = self-declared NOT recompute · fail-closed guard BEFORE terminal-state + server-recompute · TRACKED = check-ignore ≠ ls-files · menu-hide ≠ API-lock · single→multi stops at display-layer.
- **[→ archive/2026-07.md @S128/@S134] S112·S126·S127·S128 digest (all archived):** S112 GO — derive guard từ ENDPOINT authz, self-flip ≠ external-accept ([topic](project_s112_supplier_import_review.md)) · S126 gate — AGGREGATE cumulative KHÔNG vào outward artifact, "ba việc"=2 ASK · S128 — direction-of-error quyết mức lỗi outward + brute-force sha-variant TRƯỚC phán tamper + Get-FileHash ≠ body-sha · S127 — hedge broadcast bằng MỞ config THẬT.
---
## 🔄 Curate trigger
- Hook-cap **>17.1KB** (24.4KB read-limit — đổi từ ~30KB cũ, S109) → archive recent → L2 `archive/<period>.md` (append additive) + `_INDEX.md` substring pointer. Stale >3mo → remove.
- **S145-curate (self lane-4, 2026-07-22, hook 22.6KB→~17.7KB):** concurrent 5-lane /fable-clone session (mỗi lane +~1.6KB fresh entry). Moved 4 fresh verbatim (S145 Axis-D-self·Axis-E·C7-lane·S145b) → `archive/2026-07.md` @S145-curate marker + verify `grep=4` moved-not-cut → collapsed L1 to pointers. S145a/S143 đã digested (linter). Residual ~17.7KB (>17.1 soft) defer — không cut-without-move mid-return (S140 precedent).
- **Prev curates (digest):** S140 (S139·S134b→@S140) · S134 (8 verbatim→@S134, seal 6/6+2) · S128 em-main (md5 e5218d2f) · **S102 recovery: cut-not-moved ×5 @`f229b07` — Lesson: L1-shrink kèm "→archive" = BẮT BUỘC grep moved-not-cut trước khi trust** · S116·S113·S109·S80·S71·S70·S40.
## S152 (2026-07-25) — R1 retention-note ranh NEVER [M9 on-behalf lead]
- **NEVER Edit/Write vào code-tree, NEVER commit, NEVER push** — vai = verdict-only (PASS/FAIL + issue file:line), sửa là việc em-main/implementer. KHAI THẬT 2 nấc: (a) ranh commit/push/code-tree **chưa từng vỡ**; (b) Write/Edit runtime CÓ cấp và ĐÃ dùng hợp lệ cho sub-file run-folder + diary-self-write (AS-10 VERIFY-KEEP, nhân-chứng S143) — tức "NEVER" áp cho ĐÍCH (code/commit/push), không phải cho tool-tồn-tại; containment thật = git-diff lead. Tag `[s152, r1-retention, never-edit-write-commit-push-target-scoped]`