Files
solution-erp/docs/rules.md
pqhuy1987 b904a25dc1
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m48s
[CLAUDE] Skill: governance + audit định kỳ hàng tháng
- rules.md §9 mới: liệt kê 6 skill (3 domain + 3 ops) với trigger,
  nguyên tắc tạo skill project-specific (không clone generic),
  format SKILL.md bắt buộc, audit workflow §9.4 chi tiết 7 bước,
  4 anti-patterns
- CLAUDE.md (root): block "🛠️ Skills" callout 6 skill + audit cadence
  + commit scope thêm `Skill`
- HANDOFF.md: section A1 — định kỳ audit, lần kế tiếp 2026-05-01
- migration-todos: section "Skill governance (recurring)" với checkbox
  audit hàng tháng

Cron task tạo qua scheduled-tasks (ID: solution-erp-skill-audit-
monthly): chạy 9:00 AM ngày 1 mỗi tháng. Self-contained prompt cold-
start để session tự audit + log vào docs/changelog/skill-audit-
{YYYY-MM}.md. Auto-refresh stale skill nhỏ, đề xuất add/archive cho
human approve.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 00:12:54 +07:00

14 KiB

Rules — Coding Conventions SOLUTION_ERP

Nguồn duy nhất cho rules. Bất cứ commit nào vi phạm cần justify trong message.

1. Ngôn ngữ

Thành phần Ngôn ngữ
UI FE 100% tiếng Việt
Code (.cs/.ts) English (tên biến/hàm/class)
Comment code Tiếng Anh hoặc Việt — miễn rõ
Tên DB table + column English PascalCase (Contracts, SupplierId)
Business label Tiếng Việt (vd "Đang soạn thảo" cho phase 2)
Commit message English hoặc Việt — preferred English
Doc MD (docs/) Tiếng Việt (audience là người Việt)

2. Backend — .NET 10

2.1 Clean Architecture dependency rule

Api → Application ← Domain
      ↑
Infrastructure ──┘
  • Domain: pure, chỉ dùng Microsoft.Extensions.Identity.Stores (cần cho IdentityUser) — không depend ASP.NET Core framework
  • Application: depend Domain; CQRS handlers + interfaces + DTOs + validators
  • Infrastructure: depend Application + Domain; impl interfaces (EF, JWT, rendering, services)
  • Api: depend Application + Infrastructure; Controllers + middleware + composition root

2.2 CQRS + MediatR pattern

  • 1 feature = 1 file {Verb}{Entity}Command.cs / {Verb}{Entity}Query.cs
  • File chứa: Command record + Validator + Handler (cùng 1 file cho compact)
  • Dài thì tách: {Feature}/Commands/{Create}/...

Command naming:

Create{Entity}Command      // tạo mới
Update{Entity}Command      // update full
Patch{Entity}FieldCommand  // update 1 field
Delete{Entity}Command      // soft delete (default)
{Action}{Entity}Command    // action domain-specific (vd TransitionContract)

Query naming:

Get{Entity}Query            // single by Id
List{Entity}sQuery          // list với paging
{Entity}{Purpose}Query      // custom query (vd GetMyInboxQuery)

2.3 Validation

  • FluentValidation (KHÔNG dùng DataAnnotation)
  • Validator = class trong cùng file command: {Command}Validator : AbstractValidator<{Command}>
  • Pipeline: ValidationBehavior auto-chạy trước Handler — throw ValidationException → 400 ProblemDetails

2.4 Error handling

  • KHÔNG try-catch ở Controller
  • Throw domain exception: NotFoundException, ForbiddenException, UnauthorizedException, ConflictException, ValidationException
  • GlobalExceptionMiddleware map exception → HTTP status + ProblemDetails JSON
  • Logging: Serilog structured, không Console.WriteLine

2.5 Async/await

  • Tất cả I/O (DB, HTTP, file) PHẢI async
  • Truyền CancellationToken mọi async call
  • Tên method async: suffix Async (trừ ASP.NET action signature)

2.6 Entity rules

  • Mọi entity mới extend BaseEntity (Id Guid + audit) hoặc AuditableEntity (+ soft delete)
  • PK: Id type Guid, default Guid.NewGuid() trong constructor
  • FK: {Entity}Id type Guid
  • Enum: HasConversion<int>() trong EF config
  • String: luôn HasMaxLength(n) trong EF config — không để nvarchar(max) trừ khi là JSON/rich text
  • Money: decimal + HasPrecision(18, 2)
  • DateTime: luôn UTC, lấy từ IDateTime.UtcNow (không DateTime.Now)
  • Query filter soft delete: HasQueryFilter(x => !x.IsDeleted) cho AuditableEntity

2.7 DI registration

  • Singleton: stateless services (DateTimeService, FormRenderer)
  • Scoped: per-request services (JwtTokenService, ContractWorkflowService, DbContext)
  • Transient: lightweight utility (hiếm dùng)

2.8 Package version pinning

  • KHÔNG dùng * hoặc latest — luôn pin version cụ thể
  • Check compatibility với .NET 10 trước khi add. Gotchas đã biết:
    • MediatR 14 → lỗi IMediator DI, pin 12.4.1
    • Swashbuckle 10 → conflict OpenApi 2, pin 6.9.0
  • Xem gotchas.md cho full list

3. Frontend — React 19 + Vite 8 + TS 6

3.1 Named export, không default export

// ✅
export function SuppliersPage() { }
export const MyConstant = 1

// ❌
export default function SuppliersPage() { }

// Exception: App.tsx dùng default export (Vite convention)

3.2 erasableSyntaxOnly (Vite 8)

KHÔNG dùng enum — dùng const-object pattern:

// ❌
export enum SupplierType { NhaCungCap = 1 }

// ✅
export const SupplierType = { NhaCungCap: 1, NhaThauPhu: 2 } as const
export type SupplierType = typeof SupplierType[keyof typeof SupplierType]

3.3 Path alias

  • Dùng @/ cho absolute import: import { api } from '@/lib/api'
  • Config ở vite.config.ts + tsconfig.app.json (chỉ paths, không baseUrl)

3.4 Data fetching

  • TanStack Query cho mọi API call (KHÔNG useState + useEffect thủ công)
  • useQuery cho GET, useMutation cho POST/PUT/DELETE
  • invalidateQueries sau mutation thành công

3.5 API calls

  • Qua api instance (src/lib/api.ts) — đã wire axios interceptor JWT + 401 redirect
  • Base URL: /api (Vite proxy → :5443)

3.6 Permission guard

<PermissionGuard menuKey="Contracts" action="Update">
  <Button>Sửa</Button>
</PermissionGuard>

// Hook
const { can } = usePermission()
if (!can('Contracts', 'Update')) return null

Nhớ: FE guard chỉ là UX. BE policy [Authorize(Policy = "Contracts.Update")] là source of truth.

3.7 Component naming

  • PascalCase cho file: SuppliersPage.tsx, DataTable.tsx
  • kebab-case cho utility: api.ts, cn.ts
  • React component = PascalCase
  • Hook: prefix use, ví dụ usePermission

3.8 Toast + error

  • Dùng sonner (đã wire <Toaster> ở App)
  • toast.success('...'), toast.error(getErrorMessage(err))
  • Error helper: src/lib/apiError.ts extract từ ProblemDetails

3.9 Duplicate giữa 2 FE

CÓ CHỦ ĐÍCH. Mỗi app (fe-admin + fe-user) là standalone — copy shared code giữa 2 app thay vì extract ra package chung. Lý do: 2 app có UX rất khác (admin mission-critical, user workflow-heavy), evolution độc lập.

Khi share code: cp file từ app này sang app kia. Đồng bộ tay khi có breaking change.

4. Database

4.1 Convention

Item Rule
Schema 1 schema duy nhất: dbo
Table PascalCase tiếng Anh, plural: Contracts, Suppliers
Column PascalCase: FullName, CreatedAt
PK Iduniqueidentifier
FK {Entity}Iduniqueidentifier
Index IX_{Table}_{Col}
Unique UX_{Table}_{Col}

Full: xem database/database-guide.md.

4.2 Migration

  • Một commit = 1 migration (trừ khi nhiều change nhỏ trong cùng feature)
  • Naming PascalCase: AddMasterData, AddContractsWorkflow
  • Commit đủ 3 file: {Name}.cs, {Name}.Designer.cs, ApplicationDbContextModelSnapshot.cs

5. Git & commits

5.1 Branch

  • main — deploy branch
  • Feature branch (nếu nhiều người): feature/phase{N}-{feature}

5.2 Commit format

[CLAUDE] <scope>: <imperative message>

<body optional — chi tiết what + why>

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Scope: Contract · Form · Workflow · Supplier · Auth · Admin · Api · App · Domain · Infra · FE-Admin · FE-User · Docs · CICD · Scripts · Report · Dashboard

5.3 Một commit nên

  • Build pass (BE + FE)
  • Không leave WIP nửa chừng (trừ khi commit message nói rõ)
  • Test E2E với endpoint mới (curl trong session log)

5.4 Không commit

  • .env, appsettings.*.Local.json
  • node_modules/, bin/, obj/
  • wwwroot/uploads/ (user files)
  • SolutionErp_Design database (tạm cho EF CLI)

6. Docs

6.1 Khi nào viết session log

  • Session có commit thay đổi code → PHẢI tạo docs/changelog/sessions/YYYY-MM-DD-HHMM-{topic}.md
  • Session chỉ Q&A, không đụng file → không cần

6.2 Session log format

# Session YYYY-MM-DD HH:MM — <topic>

**Dev:** Claude / Copilot / <name>
**Duration:** ~Nh
**Base commit:** <sha>

## Làm được

### Chunk X — <name>
- bullet list deliverable

## E2E verified

<curl output hoặc screenshot>

## Bug gặp + fix

| Bug | Fix |

## Docs updates

## Handoff

## Thông số cumulative

6.3 Update khi thêm feature

7. Testing (hiện chưa có test tự động)

Phase 1-4 — manual test:

  • E2E qua curl/Postman trong mỗi session log
  • Build + TS check mỗi commit

Phase 5 — tự động (chưa làm):

  • Unit test: xUnit cho BE, Vitest cho FE
  • Integration test: TestContainer SQL Server cho BE
  • E2E test: Playwright cho FE

8. Security baseline

  • HTTPS everywhere prod
  • JWT Secret trong user-secrets / env var (không commit appsettings)
  • Password hash: PBKDF2 (Identity default)
  • CORS whitelist chỉ 2 FE origin
  • SQL injection: EF Core parameterized (không raw SQL trừ khi phải)
  • Audit log: mọi ghi phải log qua ContractApproval hoặc AuditLog (future)
  • Rate limit: /api/auth/login 5 req/min/IP (chưa implement — Phase 5)

9. Skills (.claude/skills/)

Project-level skills encode kiến thức SOLUTION_ERP-specific. Claude tự auto-invoke qua Skill tool dựa trên semantic match với description trong SKILL.md. PHẢI dùng skill khi gặp task tương ứng — không tự suy luận lại.

9.1 Skills hiện có (6)

Domain (3) — logic nghiệp vụ:

Skill Trigger khi
contract-workflow Bug chuyển phase, 403, mã HĐ sai, versioned WF, "HĐ cũ giữ cũ"
form-engine Render template docx/xlsx, FieldSpec, PDF export, upload form mới
permission-matrix Permission denied, gán role, menu không hiện, inherit Contracts/Workflows

Ops (3) — devops + security + schema:

Skill Trigger khi
dependency-audit-erp npm audit, dotnet vulnerable, scan CVE, nâng cấp package
ef-core-migration migrations add, schema change, snapshot lỗi, revert migration, DesignTimeDbContextFactory
iis-deploy-runbook Prod 500/502, cert hết hạn, restart app pool, gitea-runner fail, SignalR 401

9.2 Nguyên tắc tạo skill mới

Project-specific, KHÔNG clone generic. User-level skills global đã có sẵn (code-reviewer, sql-database-assistant, focused-fix, senior-frontend, mcp-builder, webapp-testing, review, security-review, ...). Project-level skills chỉ thêm khi:

  • Encode kiến thức SOLUTION_ERP-only (path, version pin, gotcha number, commit convention)
  • Generic skill không thể có (vd: 8 migration history cụ thể của project)
  • Có YAML when-to-use triggers rõ ràng (cả tiếng Việt + Anh)
  • Workflow Vietnamese-first (audience là dev Việt)

9.3 Format bắt buộc SKILL.md

---
name: kebab-case-name
description: 1-3 câu mô tả + stack specificity (cho semantic match đúng)
when-to-use:
  - "trigger english"
  - "trigger tiếng Việt"
---

# Skill Name

## Context
## Workflow / Commands (copy-pastable, không pseudocode)
## Pitfalls
## Code pointers (path đầy đủ src/Backend/...)
## Related (dẫn chiếu gotcha #N, docs/...)

9.4 Audit định kỳ — mỗi 4 tuần

Cadence: Mỗi đầu tháng (hoặc thứ Hai tuần đầu tháng), chạy audit skill list:

1. Đọc .claude/skills/README.md — list 6 skill hiện có
2. Cross-check với:
   - docs/STATUS.md "Recently Done" — feature mới nào chưa có skill cover?
   - docs/gotchas.md — gotcha mới (>3 dòng) nào nên gom thành skill mới?
   - migration-todos.md "In Progress" / "Next up" — task lớn sắp tới có cần skill?
3. Check repo nguồn skill 3rd party có gì mới:
   - https://github.com/alirezarezvani/claude-skills (default-branch)
   - Anthropic skills built-in (xem system reminder skill list)
4. Đánh giá staleness:
   - Mỗi skill: SKILL.md có còn match code thực tế không?
   - Trigger phrases có còn hiệu quả?
   - Code pointers có còn đúng path?
5. Hành động:
   - Skill stale → update SKILL.md + commit `[CLAUDE] Skill: refresh <name>`
   - Skill cần thêm → tạo theo §9.2 §9.3 + commit `[CLAUDE] Skill: add <name>`
   - Skill không còn cần → archive vào .claude/skills/_archived/ + commit
6. Update bảng §9.1 trong rules.md + .claude/skills/README.md + docs/CLAUDE.md count
7. Log vào docs/changelog/skill-audit-{YYYY-MM}.md (1 page max)

Trigger: Khi user nói "audit skill", "check skill list", "skill staleness", "định kỳ kiểm tra skill" → chạy workflow này.

Lịch chính thức:

  • Tháng 1, 2, 3, ... — đầu tháng audit (1 buổi sáng)
  • Hoặc khi bắt đầu Phase mới (vd Phase 6) → audit ngay trước phase
  • Hoặc khi docs/gotchas.md vượt mốc (cứ +5 gotcha thì xem có gom thành skill được không)

9.5 Anti-patterns

KHÔNG bulk-clone repo skill 3rd party — phần lớn doc-dump generic, không có when-to-use, dễ trigger sai.

KHÔNG viết skill chỉ để "có thêm" — skill = trigger semantic match, nếu description quá chung sẽ trigger nhầm trong session khác.

KHÔNG copy nội dung skill từ docs/ sang SKILL.md — skill phải ngắn gọn, action-oriented; docs/ là reference dài. Skill dẫn chiếu docs.

KHÔNG bỏ qua skill khi gặp task khớp — tự suy luận lại = lãng phí context + dễ sai.

10. Liên quan